diff --git a/strix/config/models.py b/strix/config/models.py index dfd54cb9..84a42deb 100644 --- a/strix/config/models.py +++ b/strix/config/models.py @@ -736,10 +736,13 @@ def resolve_api_type(model_name: str, settings: Settings) -> ApiType: An explicit ``STRIX_API_TYPE`` wins. Otherwise the model decides: Responses when LiteLLM's catalog lists ``/v1/responses`` for it, chat completions for - everything else. + everything else. Models routed through LiteLLM (non-OpenAI providers) always + use chat completions because the Responses endpoint is OpenAI-specific. """ if settings.llm.api_type is not None: return settings.llm.api_type + if routes_through_litellm(model_name): + return "chat_completions" if _RESPONSES_ENDPOINT in _catalog_supported_endpoints(model_name): return "responses" return "chat_completions" diff --git a/tests/test_models.py b/tests/test_models.py index aafbaf40..fc2f8f31 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -169,6 +169,14 @@ def test_resolve_api_type_follows_the_catalog_not_the_base_url( assert uses_chat_completions_tool_schema(model, settings) is True, model +def test_litellm_models_use_chat_completions_even_when_catalog_lists_responses( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(models, "_catalog_supported_endpoints", lambda _model: ["/v1/responses"]) + + assert resolve_api_type("anthropic/claude-sonnet-4-5", Settings()) == "chat_completions" + + def test_resolve_api_type_explicit_override_wins(monkeypatch: pytest.MonkeyPatch) -> None: _settings(monkeypatch, "gpt-daybreak-blue-latest", "https://gateway.example/v1") monkeypatch.setenv("STRIX_API_TYPE", "chat_completions")