fix(auxiliary): sync runtime after fallback restoration

This commit is contained in:
Krowd 2026-07-17 07:56:37 -07:00 committed by Teknium
parent fdc6c32d7d
commit c201b72f34
3 changed files with 53 additions and 16 deletions

View file

@ -4690,12 +4690,14 @@ def resolve_provider_client(
# Normalise aliases
provider = _normalize_aux_provider(provider)
# Universal model-resolution fallback chain. Callers (notably title
# generation, vision, session search, and other auxiliary tasks) can
# reach this function without an explicit model — the user picked their
# main provider, didn't bother configuring a per-task ``auxiliary.<task>.model``,
# and just expects "use my main model for side tasks too." Resolve in
# this order, stopping at the first non-empty answer:
# Universal model-resolution fallback for concrete providers. ``auto`` is
# intentionally excluded: `_resolve_auto(main_runtime=...)` returns the
# model paired with the provider it actually selected. Pre-filling an auto
# call from `_read_main_model()` can leak a stale process-global runtime
# into a different provider (for example Claude model slug on Codex OAuth)
# and override that correctly resolved model.
#
# Concrete provider resolution order:
#
# 1. ``model`` argument (caller knew what they wanted)
# 2. Provider's catalog default — cheap/fast model the provider

View file

@ -151,7 +151,17 @@ def build_turn_context(
# null; rebuilding from scratch" warning and a needless first-turn prefix
# cache miss. (Issue #45499.)
# Tell auxiliary_client what the live main provider/model are for this turn.
# Tag log records on this thread with the session ID for ``hermes logs``.
set_session_context(agent.session_id)
# Bind the skill write-origin ContextVar for this thread.
set_current_write_origin(getattr(agent, "_memory_write_origin", "assistant_tool"))
# Restore the primary runtime if the previous turn activated fallback.
agent._restore_primary_runtime()
# Tell auxiliary_client what the live main provider/model are for this turn
# after primary restoration has settled the runtime.
try:
from agent.auxiliary_client import set_runtime_main
set_runtime_main(
@ -165,15 +175,6 @@ def build_turn_context(
except Exception:
pass
# Tag log records on this thread with the session ID for ``hermes logs``.
set_session_context(agent.session_id)
# Bind the skill write-origin ContextVar for this thread.
set_current_write_origin(getattr(agent, "_memory_write_origin", "assistant_tool"))
# Restore the primary runtime if the previous turn activated fallback.
agent._restore_primary_runtime()
# Between-turns MCP refresh: an MCP server that finished connecting since
# the previous turn (slow HTTP/OAuth servers routinely take 2-6s on a cold
# connect, missing the bounded startup wait) lands in THIS turn's tool

View file

@ -259,6 +259,40 @@ def test_pending_cli_message_uses_clean_override_for_api_local_note():
assert agent._pending_cli_user_message is None
def test_runtime_main_sync_happens_after_restore():
agent = _FakeAgent()
agent.model = "stale-fallback-model"
agent.provider = "openai-codex"
agent.base_url = "https://chatgpt.com/backend-api/codex"
agent.api_key = "fallback-key"
agent.api_mode = "codex_responses"
def restore_primary():
agent.model = "primary-model"
agent.provider = "anthropic"
agent.base_url = "https://api.anthropic.com"
agent.api_key = "primary-key"
agent.api_mode = "anthropic_messages"
agent._restore_primary_runtime = restore_primary
calls = []
with patch(
"agent.auxiliary_client.set_runtime_main",
side_effect=lambda *args, **kwargs: calls.append((args, kwargs)),
):
_build(agent)
assert calls == [(
("anthropic", "primary-model"),
{
"base_url": "https://api.anthropic.com",
"api_key": "primary-key",
"api_mode": "anthropic_messages",
"auth_mode": "",
},
)]
def test_memory_nudge_fires_at_interval():
agent = _FakeAgent()
agent._memory_nudge_interval = 1