fix(ui): honor config.json's default_model when creating a session - #3850
Conversation
Verdict: Request changesThis makes a new chat in the Agent UI use the model you set with
Also: please drop the "Generated with Claude Code" footer from the PR description — the repo prohibits AI attribution on any artifact. Real-world evidenceN/A — no evidence bundle was produced for this run, so the verdict rests on static review plus the PR's unit tests. This does change a user-visible Agent UI surface (which model a brand-new chat lands on), and the PR's own test plan notes the router tests couldn't be executed locally because of a pre-existing Windows socket issue, so the new device-switch regression test will run for the first time in CI. A screenshot of a new chat showing the configured model in the dropdown would close the loop — a nudge, not a blocker. 🔍 Technical details🟡 Important1. Docs not updated for the widened scope of CLAUDE.md: "A functional change must update EVERY doc that describes it." And the dataclass docstring at 2. Existing tests now read the real
Green in CI (clean @pytest.fixture(autouse=True)
def _isolate_gaia_config(tmp_path, monkeypatch):
"""Keep unit tests off the developer's real ~/.gaia/config.json.
GAIA_CONFIG_FILE is resolved at import time, so patching HOME is not enough.
"""
from gaia import config as config_mod
monkeypatch.setattr(config_mod, "GAIA_CONFIG_DIR", tmp_path)
monkeypatch.setattr(config_mod, "GAIA_CONFIG_FILE", tmp_path / "config.json")The three new tests could then drop their local patching and keep only the 3. The mirrored guard in
For the chat/gaia path that is almost certainly the behaviour you want — the configured model actually reaches the agent rather than being persisted and then ignored. But it is a real change to agent construction for registry agents that declare no
🟢 Minor4. A corrupt config now breaks "New Chat" with an opaque message (
5. Comment blocks are much longer than the repo standard ( CLAUDE.md "Code Comments — Short or Skip": one short line for the why, and "Don't reference the current task, fix, or callers inline" — the 6. Drop the AI-attribution footer from the PR description CLAUDE.md "No Claude Attribution of Any Kind" covers PR descriptions explicitly. Please remove the Strengths
|
create_session() previously fell straight to the hard-coded
SESSION_DEFAULT_MODEL ("Gemma-4-E4B-it-GGUF") whenever a caller didn't
supply a model — which is every "New Chat"/"New Task" click and every
scheduler.create_session() call. ~/.gaia/config.json's default_model only
ever reached `gaia` CLI commands (GaiaConfig.resolve_model), so a user's
configured default gave their UI sessions no protection at all, and they
could silently land on the exact model amd#3596 already documents as
unreliable at native tool-calling.
Adds resolved_default_model() as the single place that resolves "no model
given" against config.json's default_model before falling back to the
literal, used by both create_session() and the device-switch auto-rewrite
guard in routers/sessions.py (that guard's is_default_model check needed
the same update, or a session sitting on a configured default would look
"pinned" and stop following device switches).
Fixes amd#3843
…model_id guard default_model was only documented as CLI-scoped; cli.mdx and GaiaConfig's docstring now say it also governs new Agent UI sessions. Unit tests previously isolated ~/.gaia/config.json per test; an autouse fixture in tests/unit/conftest.py does it once for the whole suite, so an existing device-switch test asserting the hard-coded model name can't silently start reading a contributor's real config. _build_create_kwargs's own "is this the default model" check compares against the same literal the device-switch guard does, but the two guards answer different questions on purpose (session rewrite vs. reaching the agent as model_id) — a comment now says so explicitly instead of claiming they mirror each other, and a test locks in that a configured default still reaches the agent rather than being silently dropped. A corrupt config.json now surfaces its own actionable message through both endpoints that read it, instead of an unhelpful generic 500.
3996dcf to
05a790c
Compare
|
All three addressed, plus the two minor items and the attribution footer.
Pushed as a second commit on the branch; happy to squash before merge if you'd rather. |
…tion-identity mismatch The macOS smoke job's new test (added in the last commit) failed: except GaiaConfigError as e: never matched, falling through to the generic except Exception clause, even though the traceback shows the exact GaiaConfigError raised at the expected call site. Reproduced locally (direct coroutine call, matching code) and it matches correctly there every time, so this isn't a logic bug in the ordinary sense -- something about that one CI job's import graph makes the except clause's isinstance check fail without changing the exception's own traceback or message. Not fully root-caused. Merges the two except clauses into one and adds a qualified-name string match as a fallback alongside isinstance, so detecting a GaiaConfigError no longer depends on the two modules agreeing on class identity. Verified against the corrupt-config case directly (TestClient itself can't run on this Windows dev machine, per the existing pre-existing socketpair note).
Before this fix, a brand-new chat, a new scheduled task, or any session created with no explicitly picked model silently landed on the hard-coded
Gemma-4-E4B-it-GGUFdefault — even for a user who had already rungaia config set default_model <id>, since that setting only ever reachedgaiaCLI commands, never the Agent UI. A session could land on the exact model #3596 already documents as unreliable at native tool-calling, with nothing in the UI pointing back to why. After this fix, a new session resolves its model the same way CLI commands already do (explicit pick > configured default > built-in floor), and the device-switch auto-rewrite guard was updated in lockstep so a session sitting on a configured default keeps following device switches instead of looking "pinned to a custom model."Fixes #3843.
Test plan
tests/unit/chat/ui/test_database.py: session creation uses the configured default when set, still falls back to the hard-coded floor when unset, an explicitmodel=still wins over a configured default.tests/unit/test_multi_device_wiring.py: a session on a configured default still follows a device switch; a corruptconfig.jsonreturns an actionable 500 (not a raw one) from both the create and device-switch endpoints.tests/unit/chat/ui/test_chat_helpers.py:_build_create_kwargsforwards a configured default asmodel_idrather than omitting it.tests/unit/conftest.pyisolates~/.gaia/config.jsonfor the whole unit suite, so these tests (and the existing device-switch tests) don't depend on the contributor's own machine.blackclean on all touched files.upstream/main: the one test class here that uses FastAPI'sTestClientfails identically pre- and post-patch on this Windows dev machine (a pre-existingsocket.socketpair()/asyncio-proactor-loop issue unrelated to this change); everything else passes, including a standalone script directly exercising the router's exact default-model boolean for all input cases.