feat: unify model lists, add workspace overrides to runner type gates#794
feat: unify model lists, add workspace overrides to runner type gates#794Gkrumbach07 merged 14 commits intomainfrom
Conversation
…unner type gates - Remove models/defaultModel from agent registry, add provider field - Add Gemini models to models.json (single source of truth) - Add ?provider= filter to GET /api/projects/:name/models - Move runner-types to project-scoped route with workspace override support - Keep global /api/runner-types for admin page (no workspace overrides) - Frontend fetches models by provider based on selected runner type - Runner type feature gates now check workspace ConfigMap overrides Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the models API is filtered by provider (e.g. ?provider=google), it now returns the correct provider-specific default model instead of the global default. This ensures the UI shows "gemini-2.5-flash" as the default when the Gemini runner is selected, rather than an Anthropic model. - Add providerDefaults map to models.json (version 2) - Resolve effective default in ListModelsForProject based on provider - Treat provider defaults as always-available in isModelAvailable - Update test fixtures with Gemini models and providerDefaults Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1767e42 to
795f49b
Compare
This comment has been minimized.
This comment has been minimized.
…r struct - Delete defaultModelsResponse() — models.json is the single source of truth. Return 503 when manifest unavailable instead of stale hardcoded list. - Delete fallbackModels in create-session-dialog — show empty dropdown while loading instead of a stale list. - Fix temporal dead zone: useForm referenced defaultModel/modelsData before their const declarations. Use static DEFAULT_MODEL for form init, useEffect updates when API responds. - Fix query key: use conditional spread to avoid undefined in cache key. - Sanitize providerFilter query param. - Sync operator AgentRuntimeSpec: remove DefaultModel/Models, add Provider (matches backend struct). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…ctName args, gofmt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…es, GetRunnerTypesGlobal Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…kip provider defaults in flag sync - Add requiredProvider param to isModelAvailable — rejects model/runner provider mismatches (e.g., Gemini model with Claude runner) at the API boundary instead of failing at runtime - Return empty defaultModel when no models pass filtering (prevents frontend setting a non-existent default in the form) - Embed models.json at compile time via go:embed as fallback for cold start before ConfigMap volume is mounted - Skip provider default models in FlagsFromManifest — default models for each provider don't need Unleash flags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
The go:embed approach required copying models.json into the backend directory at build time, adding complexity for a marginal benefit (seconds of cold start before ConfigMap mounts). The 503 is cleaner. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
test comment |
|
hello |
This comment has been minimized.
This comment has been minimized.
…match tests - When GetRuntime fails (registry ConfigMap not mounted), log warning and skip provider matching instead of returning 400. Model is still validated against the manifest. - Add test: provider mismatch (google model + anthropic runner) → false - Add test: provider match (anthropic model + anthropic runner) → true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t mismatch test Default and provider-default models previously bypassed the requiredProvider check via early return. Now provider matching runs before the default-model early return — using gemini-2.5-flash with a Claude runner correctly returns false. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…d empty model state - Provider default loop now verifies the provider key matches requiredProvider, not just the model ID value - Submit button disabled while models are loading (prevents submitting stale DEFAULT_MODEL before API responds) - Empty model dropdown shows "No models available for this runner" instead of a blank list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…r switch - Don't fetch models until runner types have loaded (prevents wasteful unfiltered fetch and model list flicker on dialog open) - Use form.resetField instead of form.setValue to clear both value AND dirty state when switching runners, so the useEffect correctly applies the new provider's default model Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…nnerTypesError - Invalidate ["runner-types", projectName] after saving feature flag overrides so the runner type dropdown refreshes without a page reload - Gate models fetch on !runnerTypesError (prevents unfiltered fetch when runner types fail to load) - Initialize models slice to empty (not nil) for consistent JSON [] - Document fail-open provider validation skip on cold start Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Merge Readiness — Blockers Found
|
…e submit on model error Make isModelAvailable reject unknown models when requiredProvider is set but the manifest is unavailable, rather than failing open. This ensures cross-provider validation is enforced even during cold start. Fail-open is preserved only when both manifest and registry are unavailable (requiredProvider is empty). Disable the Create Session submit button when the models API returns an error and no models are loaded, preventing users from submitting with a stale default model that may not match the selected runner. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryPR #794 unifies model lists under Issues by SeverityBlocker IssuesNone Critical IssuesNone Major Issues1. Missing test: provider-default happy path in The new Example:
Without this test, a future refactor of step 4 could silently break Gemini session creation. Suggested addition to the It("should accept provider-default model when requiredProvider matches", func() {
writeManifestFile(validManifest)
setupK8sWithOverrides()
// gemini-2.5-flash is the google provider default
result := isModelAvailable(context.Background(), K8sClient, "gemini-2.5-flash", "google", "test-ns")
Expect(result).To(BeTrue())
})Minor Issues1. Redundant condition in provider-defaults loop for provider, pd := range manifest.ProviderDefaults {
if modelID == pd && (requiredProvider == "" || provider == requiredProvider) {By the time execution reaches this loop, the 2. defaults := map[string]bool{manifest.DefaultModel: true}If 3. Hardcoded return new Response(data, {
status: response.status,
headers: { "Content-Type": "application/json" },
});This forces 4. Overly complex submit-disabled condition disabled={... || modelsLoading || (modelsError && models.length === 0)}When Positive Highlights
Recommendations
Generated with Claude Code 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
Summary
Eliminates duplicate model lists and restores feature-flag gating for models.
models.jsonis now the single source of truth for all models across all providers — no hardcoded fallback lists anywhere. Adds workspace-scoped override support to runner type feature gates and cross-provider model/runner validation.Problem: PR #788 added hardcoded model lists to the agent registry. The create-session dialog read models from the registry instead of the
/api/modelsendpoint, bypassing all Unleash feature flag and workspace override logic. ThefeatureGatefield on runner types only checked global Unleash — no workspace-level control. Nothing prevented using a Gemini model with a Claude runner.Solution: Models belong in
models.json, not the agent registry. Each runner now has aproviderfield, and the models API filters by provider. Model/runner provider mismatches are rejected at the API boundary — including for default models.What changed
Backend
handlers/models.go— Added?provider=query param filter.isModelAvailablevalidates model/runner provider match (requiredProviderparam) for all models including defaults. Returns 503 when manifest unavailable (no hardcoded fallbacks). Returns emptydefaultModelwhen no models pass filtering.handlers/runner_types.go— RemovedModels/DefaultModel/ModelOption, addedProvider. NewGetRunnerTypesGlobal(admin). UpdatedGetRunnerTypesto project-scoped with workspace overrides viaisRunnerEnabledWithOverrides.handlers/sessions.go— Resolves runner provider from registry, passes toisModelAvailable. Degrades gracefully when registry unavailable (skips provider matching, still validates against manifest).cmd/sync_flags.go—FlagsFromManifestskips provider default models (fromproviderDefaultsmap) in addition to the global default.Operator
internal/handlers/registry.go— SyncedAgentRuntimeSpec: removedDefaultModel/Models/ModelEntry, addedProvider.Manifests
agent-registry-configmap.yaml— Removedmodels/defaultModel, addedproviderfield per runner.models.json— Added Gemini models. AddedproviderDefaultsfor per-provider default model resolution.Frontend
create-session-dialog.tsx— Models fetched from API filtered byselectedRunner?.provider. No hardcoded fallback lists. StaticDEFAULT_MODELfor form init,useEffectupdates from API.services/api/runner-types.ts— Removedmodels/defaultModel/RunnerModel/requiredSecretKeys. Addedprovider. Two API functions: project-scoped + global.services/queries/use-models.ts— Provider in query key with conditional spread (noundefined).app/api/projects/[name]/runner-types/route.ts— New proxy route.projectNameor use global variant.Data flow
Tests
?provider=anthropic,?provider=google, no filterisRunnerEnabledWithOverrides(4 tests)GetRunnerTypesGlobal(2 tests)FlagsFromManifest(2 tests)isModelAvailable(10 tests)Checklist
gofmtclean,go vetclean,golangci-lint0 issuesnpm run build— 0 errors, 0 warningseslint src/ --quiet— 0 errorsrunner.gemini-cli.enabledworkspace override → Gemini runner appears🤖 Generated with Claude Code