mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(github_copilot): gate reasoning params on capability, not on the model id
Copilot also serves Gemini, Grok and MAI models that advertise reasoning_effort, but the param was only forwarded when the model id contained "claude", so every other family had it dropped despite the API accepting it. thinking stays Claude-only since it is Anthropic-native.
This commit is contained in:
parent
1c1c96441f
commit
8aa15d6d75
2 changed files with 57 additions and 16 deletions
|
|
@ -17,7 +17,7 @@ from ..common_utils import (
|
|||
get_copilot_default_headers,
|
||||
)
|
||||
|
||||
_CLAUDE_REASONING_PARAMS: Final = frozenset({"thinking", "reasoning_effort"})
|
||||
_REASONING_PARAMS: Final = frozenset({"thinking", "reasoning_effort"})
|
||||
|
||||
|
||||
class GithubCopilotConfig(OpenAIConfig):
|
||||
|
|
@ -116,23 +116,22 @@ class GithubCopilotConfig(OpenAIConfig):
|
|||
"""
|
||||
Get supported OpenAI parameters for GitHub Copilot.
|
||||
|
||||
For Claude models that support extended thinking (Claude 4 family and Claude 3-7), includes thinking and reasoning_effort parameters.
|
||||
For other models, returns standard OpenAI parameters (which may include reasoning_effort for o-series models).
|
||||
Claude models that support extended thinking accept both thinking and reasoning_effort.
|
||||
Other reasoning-capable Copilot models (Gemini, Grok, gpt-5 family) accept reasoning_effort only.
|
||||
"""
|
||||
from litellm.utils import supports_reasoning
|
||||
|
||||
# Get base OpenAI parameters
|
||||
base_params: Final = super().get_supported_openai_params(model)
|
||||
normalized: Final = model.lower()
|
||||
|
||||
# Add Claude-specific parameters for models that support extended thinking
|
||||
if "claude" in model.lower() and supports_reasoning(
|
||||
model=model.lower(),
|
||||
):
|
||||
if "thinking" not in base_params:
|
||||
base_params.append("thinking")
|
||||
# reasoning_effort is not included by parent for Claude models, so add it
|
||||
if "reasoning_effort" not in base_params:
|
||||
base_params.append("reasoning_effort")
|
||||
if not supports_reasoning(model=normalized):
|
||||
return base_params
|
||||
|
||||
# thinking is Anthropic-native; only Claude accepts it
|
||||
if "claude" in normalized and "thinking" not in base_params:
|
||||
base_params.append("thinking")
|
||||
if "reasoning_effort" not in base_params:
|
||||
base_params.append("reasoning_effort")
|
||||
|
||||
return base_params
|
||||
|
||||
|
|
@ -145,13 +144,13 @@ class GithubCopilotConfig(OpenAIConfig):
|
|||
) -> dict:
|
||||
# OpenAIConfig routes non-o-series/non-gpt-5 models to OpenAIGPTConfig, whose
|
||||
# whitelist has neither key, so advertising them alone dropped them (#25666)
|
||||
supported: Final = frozenset(self.get_supported_openai_params(model)) & _CLAUDE_REASONING_PARAMS
|
||||
claude_params: Final = {k: v for k, v in non_default_params.items() if k in supported}
|
||||
supported: Final = frozenset(self.get_supported_openai_params(model)) & _REASONING_PARAMS
|
||||
reasoning_params: Final = {k: v for k, v in non_default_params.items() if k in supported}
|
||||
remaining: Final = {k: v for k, v in non_default_params.items() if k not in supported}
|
||||
|
||||
return super().map_openai_params(
|
||||
non_default_params=remaining,
|
||||
optional_params={**optional_params, **claude_params},
|
||||
optional_params={**optional_params, **reasoning_params},
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1050,3 +1050,45 @@ def test_map_openai_params_preserves_standard_openai_params_for_claude():
|
|||
)
|
||||
assert mapped.get("temperature") == 0.5
|
||||
assert mapped.get("max_tokens") == 128
|
||||
|
||||
|
||||
def test_non_claude_reasoning_model_forwards_reasoning_effort():
|
||||
"""Reasoning-capable non-Claude Copilot models must also keep reasoning_effort.
|
||||
|
||||
Copilot serves Gemini, Grok and MAI models that advertise reasoning_effort in
|
||||
its own model catalog, but the provider previously gated the param on the
|
||||
model id containing "claude", so every other family had it dropped while the
|
||||
upstream API would have accepted it.
|
||||
"""
|
||||
config = GithubCopilotConfig()
|
||||
model = "gemini-2.5-pro"
|
||||
|
||||
with patch(
|
||||
"litellm.utils.supports_reasoning",
|
||||
return_value=True,
|
||||
):
|
||||
supported = config.get_supported_openai_params(model)
|
||||
assert "reasoning_effort" in supported
|
||||
# thinking is Anthropic-native and must stay Claude-only
|
||||
assert "thinking" not in supported
|
||||
|
||||
mapped = config.map_openai_params(
|
||||
non_default_params={"reasoning_effort": "high"},
|
||||
optional_params={},
|
||||
model=model,
|
||||
drop_params=False,
|
||||
)
|
||||
assert mapped.get("reasoning_effort") == "high"
|
||||
|
||||
|
||||
def test_non_reasoning_model_keeps_reasoning_effort_out():
|
||||
"""A model the catalog reports as non-reasoning must not gain the param."""
|
||||
config = GithubCopilotConfig()
|
||||
|
||||
with patch(
|
||||
"litellm.utils.supports_reasoning",
|
||||
return_value=False,
|
||||
):
|
||||
supported = config.get_supported_openai_params("gpt-4o")
|
||||
assert "reasoning_effort" not in supported
|
||||
assert "thinking" not in supported
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue