diff --git a/src/agent_loop.py b/src/agent_loop.py index 5effc54b5..45570a90f 100644 --- a/src/agent_loop.py +++ b/src/agent_loop.py @@ -1948,6 +1948,10 @@ async def stream_agent_loop( # and can override this list for users who know their setup. _model_no_tools = any(kw in _model_lc for kw in ( "deepseek-r1", + # Open-weight GPT-OSS models are commonly served through llama.cpp / + # llama-cpp-python. Their names contain "gpt-o", but they do not use + # OpenAI's native tool-call channel unless the endpoint opts in. + "gpt-oss", )) # Native Ollama endpoints (/api/chat) handle tool schemas differently from # the OpenAI-compat path. Models like gemma4, qwen3.5, ministral respond to diff --git a/tests/test_tool_support_heuristic.py b/tests/test_tool_support_heuristic.py index ed2dbc76d..9294fc740 100644 --- a/tests/test_tool_support_heuristic.py +++ b/tests/test_tool_support_heuristic.py @@ -25,6 +25,7 @@ def _compute_is_api_model(model: str, endpoint_url: str, endpoint_supports=None) )) model_no_tools = any(kw in model_lc for kw in ( "deepseek-r1", + "gpt-oss", )) if endpoint_supports is True: @@ -72,6 +73,11 @@ class TestDeepSeekToolSupport: "gemma4:e4b", "http://host.docker.internal:11434/v1" ) is False + def test_gpt_oss_local_openai_compat_defaults_to_fenced_tools(self): + assert _compute_is_api_model( + "gpt-oss-20b", "http://localhost:8000/v1" + ) is False + def test_qwen_native_ollama_defaults_to_fenced_tools(self): assert _compute_is_api_model( "qwen3.5:4b", "http://localhost:11434/api/chat" @@ -117,6 +123,12 @@ class TestDeepSeekToolSupport: ) assert result is True + def test_endpoint_supports_true_overrides_gpt_oss_default(self): + result = _compute_is_api_model( + "gpt-oss-20b", "http://localhost:8000/v1", endpoint_supports=True + ) + assert result is True + def test_endpoint_supports_false_overrides_cloud(self): """supports_tools=False on an endpoint gates even cloud APIs.""" result = _compute_is_api_model(