From 9f92def9631db5f8216b5a7c9219522e7590e40f Mon Sep 17 00:00:00 2001 From: Timothy Oluwatobi Ojebiyi Date: Sat, 16 May 2026 01:12:08 +0100 Subject: [PATCH] fix(transformation): ensure 'reasoning_effort' is preserved and 'thinking' is handled correctly for Claude models --- .../github_copilot/chat/transformation.py | 11 ++++++-- .../test_github_copilot_transformation.py | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/litellm/llms/github_copilot/chat/transformation.py b/litellm/llms/github_copilot/chat/transformation.py index 6f294045fa5..c4969f67602 100644 --- a/litellm/llms/github_copilot/chat/transformation.py +++ b/litellm/llms/github_copilot/chat/transformation.py @@ -151,8 +151,13 @@ class GithubCopilotConfig(OpenAIConfig): global ``openAIGPTConfig`` whose supported-params list does not include ``reasoning_effort`` for Claude models, so a deferred write would be dropped. """ - thinking = non_default_params.pop("thinking", None) - existing_reasoning_effort = non_default_params.get("reasoning_effort") + if "claude" in model.lower(): + thinking = non_default_params.pop("thinking", None) + else: + thinking = None + existing_reasoning_effort = non_default_params.get( + "reasoning_effort" + ) or optional_params.get("reasoning_effort") if ( thinking is not None and isinstance(thinking, dict) @@ -169,7 +174,7 @@ class GithubCopilotConfig(OpenAIConfig): else: reasoning_effort = "minimal" optional_params["reasoning_effort"] = reasoning_effort - elif existing_reasoning_effort is not None: + elif non_default_params.get("reasoning_effort") is not None: optional_params["reasoning_effort"] = non_default_params.pop( "reasoning_effort" ) diff --git a/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py b/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py index 705766d5fd0..8d64b0030ca 100644 --- a/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py +++ b/tests/test_litellm/llms/github_copilot/test_github_copilot_transformation.py @@ -535,6 +535,34 @@ def test_map_openai_params_no_thinking_leaves_params_unchanged(): assert optional_params.get("temperature") == 0.5 +def test_map_openai_params_thinking_does_not_overwrite_reasoning_effort_in_optional_params(): + """``reasoning_effort`` already in ``optional_params`` must not be overwritten + by the ``thinking`` conversion.""" + config = GithubCopilotConfig() + + optional_params = config.map_openai_params( + non_default_params={"thinking": {"type": "enabled", "budget_tokens": 15000}}, + optional_params={"reasoning_effort": "low"}, + model="claude-sonnet-4-20250514", + drop_params=False, + ) + assert optional_params["reasoning_effort"] == "low" + + +def test_map_openai_params_thinking_not_popped_for_non_claude_model(): + """For non-Claude models, ``thinking`` must not be silently discarded — + it should pass through to the parent's unsupported-param handling.""" + config = GithubCopilotConfig() + + optional_params = config.map_openai_params( + non_default_params={"thinking": {"type": "enabled", "budget_tokens": 15000}}, + optional_params={}, + model="gpt-4o", + drop_params=False, + ) + assert "reasoning_effort" not in optional_params + + def test_copilot_vision_request_header_with_image(): """Test that Copilot-Vision-Request header is added when messages contain images""" config = GithubCopilotConfig()