Skip to content

feat(settings): choose the model provider in the app, not the environment - #35

Merged
IBJunior merged 1 commit into
mainfrom
feat/model-settings-in-app
Sep 14, 2026
Merged

IBJunior merged 1 commit into
mainfrom
feat/model-settings-in-app

Conversation

@IBJunior

Copy link
Copy Markdown
Member

Provider and model are now chosen in a new /settings page and stored in the config table. This replaces localStorage and the env-derived defaults, which is why configuring OPENAI_COMPATIBLE_* previously left the UI still showing Anthropic — the client's hardcoded pair always won.

Nothing is inferred. With no provider stored the app is unconfigured: the composer is replaced by a notice linking to Settings, and threads stay readable. A bad key surfaces as a failed message, not a pre-flight check.

Keys stay in the environment. Settings names the variable each provider needs and never reads, stores, or displays it. GET /api/agent/config returns no secrets.

The agent can't change its own model. The rows are deliberately absent from src/lib/config/catalog.ts, the allowlist gating set_config, so they're invisible and unwritable to Cameron.

Also adds openai-compatible for any OpenAI chat-completions endpoint (Ollama, vLLM, Groq, OpenRouter, DeepSeek). Its base URL is a setting; apiKey is always passed explicitly so OPENAI_API_KEY can never leak to a third-party host.

Verified

pnpm test (266), tsc --noEmit, typecheck:eval, next build, format:check all pass.

End-to-end against the dev stack: unconfigured → clean SSE error naming the fix; each validation path returns a field-specific 400; save round-trips and persists; a real message works with no query params; set_config still rejects the model keys.

Notes

  • The eval harness is unchanged — it already passed provider/model explicitly. The two eval/ edits are stale-reference fixes: a comment pointing at the old util.ts, and a tsconfig include that meant typecheck:eval wasn't covering models.ts.
  • approvalGate.test.ts scanned the deleted UISettingsContext; it now scans the settings UI that replaced it. Same invariant.
  • The settings form's two interactions (model clears on provider change, green saved banner) were verified by typecheck and build, not clicked through — Playwright is only a transitive binary here and I didn't want to add a dependency for a check.

🤖 Generated with Claude Code

…ment

Provider and model are picked in a new /settings page and stored in the
`config` table, replacing localStorage and the env-derived defaults.

Nothing is inferred: with no provider stored the app is unconfigured and
says so instead of guessing. API keys stay in the environment — settings
names the variable each provider needs and never reads it.

The rows are deliberately absent from the agent's config catalog, so
`set_config` cannot reach them and Cameron cannot change its own model.

Adds `openai-compatible` for any OpenAI chat-completions endpoint
(Ollama, vLLM, Groq, OpenRouter, DeepSeek); its base URL is a setting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@IBJunior
IBJunior merged commit 099142f into main Sep 14, 2026
1 check passed
@IBJunior
IBJunior deleted the feat/model-settings-in-app branch September 14, 2026 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant