diff --git a/backend/onyx/llm/multi_llm.py b/backend/onyx/llm/multi_llm.py index 0b4b31e8a0..091397b9d6 100644 --- a/backend/onyx/llm/multi_llm.py +++ b/backend/onyx/llm/multi_llm.py @@ -612,6 +612,7 @@ class LitellmLLM(LLM): ) is_claude_model = any("claude" in name.lower() for name in model_identity_names) is_qwen_model = any("qwen" in name.lower() for name in model_identity_names) + is_glm_model = any("glm" in name.lower() for name in model_identity_names) uses_adaptive_thinking = any( anthropic_uses_adaptive_thinking(name) for name in model_identity_names ) @@ -687,16 +688,17 @@ class LitellmLLM(LLM): # Tool choice # Downgrade tool_choice=required to AUTO for models that mishandle it: - # Claude skips reasoning when it's set, and Qwen thinking models reject - # it with a 400. The chat loop's fallback tool-call extraction still - # enforces the forced tool. Matched by model name rather than - # `is_reasoning` because the litellm/local registry lags behind new - # Qwen releases (e.g. qwen3.7-plus). + # Claude skips reasoning when it's set, Qwen thinking models reject it + # with a 400, and Z.AI rejects any GLM tool_choice other than auto + # ("Tool choice must be auto"). The chat loop's fallback tool-call + # extraction still enforces the forced tool. Matched by model name + # rather than `is_reasoning` because the litellm/local registry lags + # behind new Qwen/GLM releases (e.g. qwen3.7-plus, glm-5.3). # A NamedToolChoice is deliberately NOT downgraded: legacy Claude - # thinking is skipped below instead, and Qwen thinking models may still + # thinking is skipped below instead, and the other models may still # reject the forced tool upstream (a loud 400 beats silently ignoring # the caller's forced tool). - if (is_claude_model or is_qwen_model) and ( + if (is_claude_model or is_qwen_model or is_glm_model) and ( tool_choice == ToolChoiceOptions.REQUIRED ): tool_choice = ToolChoiceOptions.AUTO diff --git a/backend/tests/unit/onyx/llm/test_multi_llm.py b/backend/tests/unit/onyx/llm/test_multi_llm.py index 33341418b4..609f4e75f9 100644 --- a/backend/tests/unit/onyx/llm/test_multi_llm.py +++ b/backend/tests/unit/onyx/llm/test_multi_llm.py @@ -2527,13 +2527,14 @@ _TOOL_CHOICE_DOWNGRADE_TOOLS = [ [ (LlmProviderNames.OPENROUTER, "qwen/qwen3.7-plus"), (LlmProviderNames.ANTHROPIC, "claude-sonnet-5"), + (LlmProviderNames.OPENROUTER, "z-ai/glm-5.3"), ], ) def test_required_tool_choice_downgraded_to_auto( model_provider: str, model_name: str ) -> None: - """Claude and Qwen thinking models reject/degrade required tool_choice, so - it must be sent to the provider as AUTO instead.""" + """Claude, Qwen thinking, and GLM models reject/degrade required + tool_choice, so it must be sent to the provider as AUTO instead.""" llm = LitellmLLM( api_key="test_key", timeout=30,