"""The ``_primary_runtime`` snapshot: the route a fallback turn restores on the next turn. Both builders (agent construction, and a deliberate model switch) record the custom entry that owns the route's credential, resolved against the config the route runs on. Restoring scopes the credential pool by that record, never by a live ``config.yaml`` lookup, so an entry edited or removed mid-session can't hand the session another account's key. """ from __future__ import annotations from typing import Any from agent.custom_entry_pool import SNAPSHOT_OWNER_KEY, runtime_entry_owner def _snapshot_primary_runtime(agent): # Per-turn restoration snapshot: after a fallback, the next turn restores these so the # preferred model gets a fresh attempt. _cc = agent.context_compressor agent._primary_runtime = { "model": agent.model, "provider": agent.provider, "requested_provider": agent.requested_provider, SNAPSHOT_OWNER_KEY: runtime_entry_owner(agent.provider, agent.requested_provider, agent.base_url), "base_url": agent.base_url, "api_mode": agent.api_mode, "api_key": getattr(agent, "api_key", ""), "request_overrides": dict(getattr(agent, "request_overrides", {}) or {}), "client_kwargs": dict(agent._client_kwargs), "use_prompt_caching": agent._use_prompt_caching, "use_native_cache_layout": agent._use_native_cache_layout, "reasoning_echo_flag": getattr(agent, "_reasoning_echo_flag", False), # Engine state _try_activate_fallback() overwrites (getattr: plugin engines may lack them). "compressor_model": getattr(_cc, "model", agent.model), "compressor_base_url": getattr(_cc, "base_url", agent.base_url), "compressor_api_key": getattr(_cc, "api_key", ""), "compressor_provider": getattr(_cc, "provider", agent.provider), "compressor_context_length": _cc.context_length, "compressor_threshold_tokens": _cc.threshold_tokens, } if agent.api_mode == "anthropic_messages": agent._primary_runtime.update({ "anthropic_api_key": agent._anthropic_api_key, "anthropic_base_url": agent._anthropic_base_url, "is_anthropic_oauth": agent._is_anthropic_oauth, }) def _build_primary_runtime_snapshot(agent, api_mode) -> dict[str, Any]: """The ``_primary_runtime`` record that persists a switch across turns.""" cc = getattr(agent, "context_compressor", None) or None rt = { "model": agent.model, "provider": agent.provider, "requested_provider": agent.requested_provider, SNAPSHOT_OWNER_KEY: runtime_entry_owner(agent.provider, agent.requested_provider, agent.base_url), "base_url": agent.base_url, "api_mode": agent.api_mode, "api_key": getattr(agent, "api_key", ""), "client_kwargs": dict(agent._client_kwargs), "use_prompt_caching": agent._use_prompt_caching, "use_native_cache_layout": agent._use_native_cache_layout, "reasoning_config": dict(agent.reasoning_config) if getattr(agent, "reasoning_config", None) else None, "reasoning_echo_flag": getattr(agent, "_reasoning_echo_flag", False), # Overrides must travel with the switched-to identity or a later recovery/restore resurrects # PRE-switch overrides from the stale init snapshot. # See #75091. "request_overrides": dict(getattr(agent, "request_overrides", {}) or {}), "runtime_capabilities": dict(getattr(agent, "runtime_capabilities", {}) or {}), "compressor_model": getattr(cc, "model", agent.model), "compressor_base_url": getattr(cc, "base_url", agent.base_url), "compressor_api_key": getattr(cc, "api_key", ""), "compressor_provider": getattr(cc, "provider", agent.provider), "compressor_context_length": cc.context_length if cc else 0, "compressor_api_mode": getattr(cc, "api_mode", agent.api_mode), "compressor_threshold_tokens": cc.threshold_tokens if cc else 0, } if api_mode == "anthropic_messages": rt.update({ "anthropic_api_key": agent._anthropic_api_key, "anthropic_base_url": agent._anthropic_base_url, "is_anthropic_oauth": agent._is_anthropic_oauth, }) return rt