diff --git a/hub/agents/gaia/npm/CHANGELOG.md b/hub/agents/gaia/npm/CHANGELOG.md index 64ab8581fc..b7f509540e 100644 --- a/hub/agents/gaia/npm/CHANGELOG.md +++ b/hub/agents/gaia/npm/CHANGELOG.md @@ -8,6 +8,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Requires the `gaia` Python CLI 0.26.0+ on `PATH`. +- **The terminal header recognizes cloud models with custom IDs.** After a + `/model` switch, the status uses the live client’s catalog provider, matching + the switch notice instead of reporting a custom-prefixed cloud model as local. + - **The "not confirmed" line on an answer is now documented.** An answer that ends with `I haven't confirmed this works — …` is the agent saying a check failed, was blocked, or never ran after a change. README, SPEC and SKILL say diff --git a/hub/agents/gaia/python/gaia_agent/stdio.py b/hub/agents/gaia/python/gaia_agent/stdio.py index 19c85f0845..dfb779cd4e 100644 --- a/hub/agents/gaia/python/gaia_agent/stdio.py +++ b/hub/agents/gaia/python/gaia_agent/stdio.py @@ -536,14 +536,19 @@ def _model_state_event(agent: Any) -> Dict[str, Any]: chat = agent.chat is_claude = bool(chat.config.use_claude) model_id = chat.effective_model - cloud_provider = cloud_model_provider(model_id) if not is_claude else None + lookup = getattr(getattr(chat, "llm_client", None), "cloud_model_provider", None) + location = resolve_inference_location( + model_id, + use_claude=is_claude, + cloud_provider_lookup=lookup if callable(lookup) else None, + ) event = { "type": "status", "message": "", "model_id": model_id, "model_display": _model_display_name(model_id, is_claude), - "model_backend": "claude" if is_claude else cloud_provider or "lemonade", - "model_remote": is_claude or bool(cloud_provider), + "model_backend": location.provider, + "model_remote": location.remote, # The header shows these, not an install record that may name another build. "agent_version": agent_version, "gaia_version": gaia_version, diff --git a/hub/agents/gaia/python/tests/test_stdio.py b/hub/agents/gaia/python/tests/test_stdio.py index d457bacd53..c7aa982b78 100644 --- a/hub/agents/gaia/python/tests/test_stdio.py +++ b/hub/agents/gaia/python/tests/test_stdio.py @@ -1551,6 +1551,55 @@ def test_switch_reads_the_catalog_once_and_names_a_discovered_provider(lemonade_ assert "not sending this conversation to any cloud provider" not in answer +def test_model_status_tracks_cloud_and_local_switches(lemonade_http): + """The wire status and switch notice agree through repeated switches.""" + agent = _ModelSwitchAgent() + for model, backend, remote in [ + ("custom.gemma-4-31b-it", "fireworks", True), + ("Gemma-4-E4B-it-GGUF", "lemonade", False), + ("custom.gemma-4-31b-it", "fireworks", True), + ]: + events = _events(_model_run(agent, f"/model {model}")) + status = next(event for event in events if "model_id" in event) + assert status["model_id"] == model + assert status["model_backend"] == backend + assert status["model_remote"] is remote + assert ("(cloud)" in events[-1]["answer"]) is remote + assert len(lemonade_http) == 3 + + +@pytest.mark.parametrize( + "model, metadata, backend, remote", + [ + ( + "custom.gemma", + {"recipe": "cloud", "cloud_provider": "fireworks"}, + "fireworks", + True, + ), + ("custom.gemma", {"recipe": "cloud", "cloud_provider": "amd"}, "amd", True), + ("custom.gemma", {"recipe": "llamacpp", "downloaded": True}, "lemonade", False), + ("Gemma-4-E4B-it-GGUF", {"downloaded": True}, "lemonade", False), + ("fireworks.gemma", {}, "fireworks", True), + ("amd.gemma", {}, "amd", True), + ], +) +def test_model_status_uses_live_catalog( + model, metadata, backend, remote, lemonade_http +): + from gaia.llm.providers.lemonade import LemonadeProvider + + agent = _ModelSwitchAgent() + agent.chat.config.model = model + agent.chat.llm_client = LemonadeProvider( + model=model, model_metadata=[{"id": model, **metadata}] + ) + event = stdio._model_state_event(agent) + assert event["model_backend"] == backend + assert event["model_remote"] is remote + assert lemonade_http == [] + + def test_a_client_that_cannot_classify_still_completes_the_switch( monkeypatch, stub_lemonade ):