fix: also reject a fallback model from a different provider

This commit is contained in:
Alex Schapiro
2026-08-10 15:54:36 +00:00
parent 9ddb2b0bb6
commit 6334074a0b
2 changed files with 23 additions and 6 deletions
+19
View File
@@ -733,6 +733,25 @@ def _configure_litellm_default(name: str, value: str) -> None:
setattr(litellm, name, value)
def fallback_model_rejection(fallback: str, primary: str, settings: Settings) -> str | None:
"""Why ``fallback`` cannot stand in for ``primary`` mid-run, or None if it can.
Provider credentials, SDK route, and each agent's tool wrappers are all set
up once from the primary model, so a fallback that needs a different
provider or tool schema would be rejected on every request it serves.
"""
if (
_split_model_provider(_normalized_model_name(fallback))[0]
!= (_split_model_provider(_normalized_model_name(primary))[0])
):
return "needs a different provider, whose credentials are not configured"
if uses_chat_completions_tool_schema(fallback, settings) != uses_chat_completions_tool_schema(
primary, settings
):
return "needs a different tool schema than the agents are built with"
return None
def uses_chat_completions_tool_schema(model_name: str, settings: Settings) -> bool:
"""Return whether the resolved SDK route can only receive JSON function tools."""
if codex.subscription_model(model_name):
+4 -6
View File
@@ -22,6 +22,7 @@ from strix.config import load_settings
from strix.config.models import (
StrixProvider,
configure_sdk_model_defaults,
fallback_model_rejection,
uses_chat_completions_tool_schema,
)
from strix.config.settings import DEFAULT_MAX_TURNS
@@ -177,14 +178,11 @@ async def run_strix_scan(
coordinator.set_snapshot_path(agents_path)
fallback_model = settings.llm.fallback_model
if fallback_model and (
uses_chat_completions_tool_schema(fallback_model, settings) != chat_completions_tools
rejection := fallback_model_rejection(fallback_model, resolved_model, settings)
):
# Agents build their tool set once, for the primary model's schema. A
# fallback on the other SDK route would reject every replayed turn, so
# it can never recover a denied agent — fail fast instead.
raise RuntimeError(
f"STRIX_LLM_FALLBACK '{fallback_model}' uses a different tool schema than "
f"'{resolved_model}'. Pick a fallback from the same provider family."
f"STRIX_LLM_FALLBACK '{fallback_model}' {rejection}; it could never serve a "
f"turn for '{resolved_model}'. Pick a fallback from the same provider family."
)
coordinator.configure_denial_fallback(
fallback_model,