fix(github-copilot): route per-model on /v1/responses based on model info

This commit is contained in:
codgician 2026-05-02 11:37:30 +08:00
parent 50ef2d51a2
commit 07e8f49d36
No known key found for this signature in database
4 changed files with 441 additions and 179 deletions

View file

@ -22,6 +22,7 @@ from litellm.types.llms.openai import (
)
from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import LlmProviders
from litellm.utils import _get_model_info_helper
from ..authenticator import Authenticator
from ..common_utils import (
@ -38,6 +39,53 @@ else:
LiteLLMLoggingObj = Any
def github_copilot_supports_responses_api(model: str) -> bool:
"""
Resolve whether to use the native Responses API for a github_copilot model.
Copilot's /v1/responses endpoint is per-model (only some models like
gpt-5.5, gpt-5.4, gpt-5.4-mini opt in upstream). The Responses API config
is registered for github_copilot provider-wide, so this function gates
per-model to keep /v1/responses calls from failing upstream on chat-only
Copilot models like claude-opus-4.7 or gemini-3.1-pro-preview.
The router calls ``litellm.register_model`` for every proxy deployment,
which merges the user's per-deployment ``model_info`` (e.g. ``mode: chat``
to force a chat-only override) into ``litellm.model_cost`` before any
request runs. ``_get_model_info_helper`` therefore returns merged data
with user overrides already applied.
Resolution order (first match wins):
1. ``mode == "responses"`` → True (positive opt-in; user or catalog).
2. ``mode == "chat"`` → False (explicit opt-out wins over endpoint
declarations, letting users force the bridge for dual-endpoint models).
3. ``"/v1/responses"`` in ``supported_endpoints`` → True.
4. Otherwise → False (conservative default; the bridge always works
because every Copilot model supports /chat/completions).
Catalog lookup raising (model not registered) → False (conservative).
"""
try:
info = _get_model_info_helper(model=model, custom_llm_provider="github_copilot")
except Exception as e:
verbose_logger.debug(
"github_copilot_supports_responses_api: get_model_info failed "
"for %s: %s",
model,
e,
)
return False
mode = info.get("mode")
if mode == "responses":
return True
if mode == "chat":
return False
endpoints = info.get("supported_endpoints")
return isinstance(endpoints, list) and "/v1/responses" in endpoints
class GithubCopilotResponsesAPIConfig(OpenAIResponsesAPIConfig):
"""
Configuration for GitHub Copilot's Responses API.

View file

@ -8575,7 +8575,13 @@ class ProviderConfigManager:
elif litellm.LlmProviders.XAI == provider:
return litellm.XAIResponsesAPIConfig()
elif litellm.LlmProviders.GITHUB_COPILOT == provider:
return litellm.GithubCopilotResponsesAPIConfig()
from litellm.llms.github_copilot.responses.transformation import (
github_copilot_supports_responses_api,
)
if model is None or github_copilot_supports_responses_api(model=model):
return litellm.GithubCopilotResponsesAPIConfig()
return None
elif litellm.LlmProviders.CHATGPT == provider:
return litellm.ChatGPTResponsesAPIConfig()
elif litellm.LlmProviders.LITELLM_PROXY == provider:

View file

@ -17142,53 +17142,98 @@
},
"github_copilot/claude-haiku-4.5": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 16000,
"max_tokens": 16000,
"max_input_tokens": 136000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-opus-4.5": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 16000,
"max_tokens": 16000,
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-opus-4.6-fast": {
"github_copilot/claude-opus-4.6": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 16000,
"max_tokens": 16000,
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-opus-41": {
"github_copilot/claude-opus-4.7": {
"litellm_provider": "github_copilot",
"max_input_tokens": 80000,
"max_output_tokens": 16000,
"max_tokens": 16000,
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_vision": true
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-opus-4.7-high": {
"litellm_provider": "github_copilot",
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-opus-4.7-xhigh": {
"litellm_provider": "github_copilot",
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/claude-sonnet-4": {
"litellm_provider": "github_copilot",
@ -17200,21 +17245,41 @@
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-sonnet-4.5": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 16000,
"max_tokens": 16000,
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/claude-sonnet-4.6": {
"litellm_provider": "github_copilot",
"max_input_tokens": 168000,
"max_output_tokens": 32000,
"max_tokens": 32000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/gemini-2.5-pro": {
"litellm_provider": "github_copilot",
@ -17223,86 +17288,50 @@
"max_tokens": 64000,
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/gemini-3-pro-preview": {
"github_copilot/gemini-3-flash-preview": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/gpt-3.5-turbo": {
"github_copilot/gemini-3.1-pro-preview": {
"litellm_provider": "github_copilot",
"max_input_tokens": 16384,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true
},
"github_copilot/gpt-3.5-turbo-0613": {
"litellm_provider": "github_copilot",
"max_input_tokens": 16384,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true
},
"github_copilot/gpt-4": {
"litellm_provider": "github_copilot",
"max_input_tokens": 32768,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true
},
"github_copilot/gpt-4-0613": {
"litellm_provider": "github_copilot",
"max_input_tokens": 32768,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true
},
"github_copilot/gpt-4-o-preview": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 4096,
"max_tokens": 4096,
"max_input_tokens": 136000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_parallel_function_calling": true
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/gpt-4.1": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_input_tokens": 64000,
"max_output_tokens": 16384,
"max_tokens": 16384,
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
},
"github_copilot/gpt-4.1-2025-04-14": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 16384,
"max_tokens": 16384,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
},
"github_copilot/gpt-41-copilot": {
"litellm_provider": "github_copilot",
"mode": "completion"
},
"github_copilot/gpt-4o": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
@ -17310,129 +17339,48 @@
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_vision": true
},
"github_copilot/gpt-4o-2024-05-13": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_vision": true
},
"github_copilot/gpt-4o-2024-08-06": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 16384,
"max_tokens": 16384,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true
},
"github_copilot/gpt-4o-2024-11-20": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 16384,
"max_tokens": 16384,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_vision": true
},
"github_copilot/gpt-4o-mini": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true
},
"github_copilot/gpt-4o-mini-2024-07-18": {
"litellm_provider": "github_copilot",
"max_input_tokens": 64000,
"max_output_tokens": 4096,
"max_tokens": 4096,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true
},
"github_copilot/gpt-5": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"supported_endpoints": [
"/v1/chat/completions",
"/v1/responses"
],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
},
"github_copilot/gpt-5-mini": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
},
"github_copilot/gpt-5.1": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"mode": "responses",
"supported_endpoints": [
"/v1/chat/completions",
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
},
"github_copilot/gpt-5.1-codex-max": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/responses"
],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true
},
"github_copilot/gpt-5.2": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/chat/completions",
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/gpt-5.3-codex": {
"github_copilot/gpt-5.2-codex": {
"litellm_provider": "github_copilot",
"max_input_tokens": 128000,
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
@ -17440,9 +17388,83 @@
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true
"supports_vision": true,
"supports_reasoning": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/gpt-5.3-codex": {
"litellm_provider": "github_copilot",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/gpt-5.4": {
"litellm_provider": "github_copilot",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/responses",
"/v1/chat/completions"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/gpt-5.4-mini": {
"litellm_provider": "github_copilot",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true,
"supports_none_reasoning_effort": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/gpt-5.5": {
"litellm_provider": "github_copilot",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": [
"/v1/responses"
],
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_parallel_function_calling": true,
"supports_response_schema": true,
"supports_vision": true,
"supports_reasoning": true,
"supports_none_reasoning_effort": true,
"supports_xhigh_reasoning_effort": true
},
"github_copilot/text-embedding-3-small": {
"litellm_provider": "github_copilot",

View file

@ -14,6 +14,8 @@ from unittest.mock import patch, MagicMock
sys.path.insert(0, os.path.abspath("../../../../.."))
import pytest
import litellm
from litellm.litellm_core_utils.get_model_cost_map import get_model_cost_map
from litellm.types.utils import LlmProviders
from litellm.utils import ProviderConfigManager
from litellm.llms.github_copilot.responses.transformation import (
@ -22,13 +24,26 @@ from litellm.llms.github_copilot.responses.transformation import (
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
@pytest.fixture(autouse=True)
def use_local_model_cost_map(monkeypatch: pytest.MonkeyPatch):
"""Pin litellm.model_cost to the bundled local backup so tests don't depend
on remote catalog fetches (and don't change behavior across remote refreshes)."""
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
monkeypatch.setattr(
litellm, "model_cost", get_model_cost_map(url=litellm.model_cost_map_url)
)
litellm.add_known_models(model_cost_map=litellm.model_cost)
class TestGithubCopilotResponsesAPITransformation:
"""Test GitHub Copilot Responses API configuration and transformations"""
def test_github_copilot_provider_config_registration(self):
"""Test that GitHub Copilot provider returns GithubCopilotResponsesAPIConfig"""
"""Test that GitHub Copilot provider returns the native Responses API
config for a Responses-capable catalog model. Exercises the full stack:
catalog lookup -> github_copilot_supports_responses_api -> native config."""
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/gpt-5.1-codex",
model="github_copilot/gpt-5.3-codex",
provider=LlmProviders.GITHUB_COPILOT,
)
@ -373,3 +388,174 @@ class TestGithubCopilotResponsesAPITransformation:
# Non-reasoning items should pass through unchanged
assert result == message_item
class TestGithubCopilotResponsesAPIRouting:
"""``ProviderConfigManager.get_provider_responses_api_config`` for github_copilot
returns the native Responses config only when the model has ``mode=responses``
in the (already-merged) model info; otherwise returns None so the dispatcher
routes through the chat-completions translation bridge."""
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_returns_config_when_mode_is_responses(self, mock_get_info):
"""``mode=responses`` returns native config."""
mock_get_info.return_value = {"mode": "responses"}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-responses-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert isinstance(config, GithubCopilotResponsesAPIConfig)
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_returns_none_when_mode_is_chat(self, mock_get_info):
"""``mode=chat`` returns None so dispatcher uses bridge."""
mock_get_info.return_value = {"mode": "chat"}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-chat-only-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert config is None
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_returns_none_when_mode_is_unset_and_no_endpoints(self, mock_get_info):
"""Entry without ``mode`` and without ``supported_endpoints`` returns None
(conservative default)."""
mock_get_info.return_value = {}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert config is None
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_returns_config_when_mode_unset_but_endpoints_have_responses(
self, mock_get_info
):
"""``mode`` unset but ``supported_endpoints`` declaring /v1/responses
returns native config (endpoint-list fallback for stale-but-correct
catalog entries that lack ``mode``)."""
mock_get_info.return_value = {
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"]
}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert isinstance(config, GithubCopilotResponsesAPIConfig)
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_mode_chat_overrides_endpoints_with_responses(self, mock_get_info):
"""``mode=chat`` is a hard opt-out: forces bridge even when
``supported_endpoints`` includes /v1/responses. Lets users force the
bridge for dual-endpoint models without clearing endpoint metadata."""
mock_get_info.return_value = {
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert config is None
def test_returns_config_when_model_is_none(self):
"""Follow-up GET/DELETE operations pass model=None and keep the native
config path (no per-model lookup is possible)."""
config = ProviderConfigManager.get_provider_responses_api_config(
model=None,
provider=LlmProviders.GITHUB_COPILOT,
)
assert isinstance(config, GithubCopilotResponsesAPIConfig)
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_returns_none_when_get_model_info_raises(self, mock_get_info):
"""Catalog lookup failure (model not registered) returns None
(conservative default; bridge handles unknown models safely)."""
mock_get_info.side_effect = Exception("model not in catalog")
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/never-seen-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert config is None
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_user_override_via_register_model(self, mock_get_info):
"""User-supplied per-deployment ``model_info`` flows through
``litellm.register_model`` (called by the router) into the merged
catalog read by ``_get_model_info_helper``. Setting ``mode=responses``
for a model whose catalog entry says ``mode=chat`` therefore opts in
to native dispatch without any per-call argument plumbing."""
mock_get_info.return_value = {"mode": "responses"}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-chat-only-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert isinstance(config, GithubCopilotResponsesAPIConfig)
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_realistic_chat_only_entry_returns_none(self, mock_get_info):
"""Realistic ``model_prices_and_context_window.json`` shape for a
chat-only Copilot model (e.g. github_copilot/gemini-3.1-pro-preview)
returns None so /v1/responses calls fall back to the bridge."""
mock_get_info.return_value = {
"litellm_provider": "github_copilot",
"max_input_tokens": 136000,
"max_output_tokens": 64000,
"max_tokens": 64000,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supports_function_calling": True,
"supports_tool_choice": True,
"supports_parallel_function_calling": True,
"supports_vision": True,
"supports_reasoning": True,
}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-chat-only-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert config is None
@patch(
"litellm.llms.github_copilot.responses.transformation._get_model_info_helper"
)
def test_realistic_responses_only_entry_returns_config(self, mock_get_info):
"""Realistic catalog entry for a Responses-only Copilot model
(e.g. github_copilot/gpt-5.5) returns the native config."""
mock_get_info.return_value = {
"litellm_provider": "github_copilot",
"max_input_tokens": 272000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "responses",
"supported_endpoints": ["/v1/responses"],
"supports_function_calling": True,
"supports_tool_choice": True,
"supports_parallel_function_calling": True,
"supports_response_schema": True,
"supports_vision": True,
"supports_reasoning": True,
"supports_none_reasoning_effort": True,
"supports_xhigh_reasoning_effort": True,
}
config = ProviderConfigManager.get_provider_responses_api_config(
model="github_copilot/some-responses-only-model",
provider=LlmProviders.GITHUB_COPILOT,
)
assert isinstance(config, GithubCopilotResponsesAPIConfig)