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:
allenliang2022 2026-08-13 17:54:30 +08:00
parent 1c1c96441f
commit 8aa15d6d75
2 changed files with 57 additions and 16 deletions

View file

@ -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,
)

View file

@ -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