fix(chatgpt): preserve requested service tier in Responses calls

This commit is contained in:
Steffan Wolter 2026-09-07 12:51:15 +02:00
parent 1cac8bd9ab
commit 08757c5e75
2 changed files with 59 additions and 28 deletions

View file

@ -104,7 +104,15 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
"truncation",
}
return {k: v for k, v in request.items() if k in allowed_keys}
service_tier: Final[object] = request.get("service_tier")
return {
**{k: v for k, v in request.items() if k in allowed_keys},
**(
{"service_tier": "priority" if service_tier == "fast" else service_tier}
if service_tier in ("default", "priority", "fast")
else {}
),
}
def transform_response_api_response(
self,

View file

@ -6,6 +6,7 @@ Source: litellm/llms/chatgpt/responses/transformation.py
import json
from collections.abc import Generator
from typing import Final
from unittest.mock import MagicMock, patch
import httpx
@ -30,6 +31,46 @@ def local_model_cost_map(monkeypatch: pytest.MonkeyPatch) -> Generator[None, Non
class TestChatGPTResponsesAPITransformation:
@pytest.mark.parametrize(
("requested_tier", "expected_tier"),
[("default", "default"), ("priority", "priority"), ("fast", "priority")],
)
@pytest.mark.parametrize("effort", ["low", "high"])
def test_chatgpt_preserves_service_tier(self, requested_tier: str, expected_tier: str, effort: str) -> None:
config: Final = ChatGPTResponsesAPIConfig()
request: Final = config.transform_responses_api_request(
model="chatgpt/gpt-5.6-sol",
input=[{"role": "user", "content": "Reply with OK"}],
response_api_optional_request_params={
"service_tier": requested_tier,
"reasoning": {"effort": effort},
"max_output_tokens": 16,
"prompt_cache_options": {"ttl": "30m"},
},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert request["service_tier"] == expected_tier
assert request["reasoning"] == {"effort": effort}
assert request["stream"] is True
assert request["store"] is False
assert "max_output_tokens" not in request
assert "prompt_cache_options" not in request
@pytest.mark.parametrize("requested_tier", [None, "auto", "flex", "unknown"])
def test_chatgpt_does_not_introduce_unsupported_service_tier(self, requested_tier: str | None) -> None:
config: Final = ChatGPTResponsesAPIConfig()
request: Final = config.transform_responses_api_request(
model="chatgpt/gpt-5.6-sol",
input=[{"role": "user", "content": "Reply with OK"}],
response_api_optional_request_params={} if requested_tier is None else {"service_tier": requested_tier},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert "service_tier" not in request
@pytest.mark.parametrize(
"model_name",
[
@ -92,14 +133,10 @@ class TestChatGPTResponsesAPITransformation:
url = config.get_complete_url(api_base=None, litellm_params={})
assert url == "https://chatgpt.example.com/responses"
custom_url = config.get_complete_url(
api_base="https://custom.chatgpt.com", litellm_params={}
)
custom_url = config.get_complete_url(api_base="https://custom.chatgpt.com", litellm_params={})
assert custom_url == "https://custom.chatgpt.com/responses"
url_with_slash = config.get_complete_url(
api_base="https://chatgpt.example.com/", litellm_params={}
)
url_with_slash = config.get_complete_url(api_base="https://chatgpt.example.com/", litellm_params={})
assert url_with_slash == "https://chatgpt.example.com/responses"
@patch("litellm.llms.chatgpt.responses.transformation.Authenticator")
@ -162,9 +199,7 @@ class TestChatGPTResponsesAPITransformation:
"user": "user_123",
"temperature": 0.2,
"top_p": 0.9,
"context_management": [
{"type": "compaction", "compact_threshold": 200000}
],
"context_management": [{"type": "compaction", "compact_threshold": 200000}],
"metadata": {"foo": "bar"},
"max_output_tokens": 123,
"stream_options": {"include_usage": True},
@ -203,9 +238,7 @@ class TestChatGPTResponsesAPITransformation:
("chatgpt/gpt-5.3-codex", "gpt-5.3-codex"),
],
)
def test_chatgpt_non_stream_sse_response_parsing(
self, model_name: str, response_model: str
):
def test_chatgpt_non_stream_sse_response_parsing(self, model_name: str, response_model: str):
config = ChatGPTResponsesAPIConfig()
response_payload = {
"id": "resp_test",
@ -228,9 +261,7 @@ class TestChatGPTResponsesAPITransformation:
"",
]
)
raw_response = httpx.Response(
200, headers={"content-type": "text/event-stream"}, text=sse_body
)
raw_response = httpx.Response(200, headers={"content-type": "text/event-stream"}, text=sse_body)
logging_obj = MagicMock()
parsed = config.transform_response_api_response(
@ -248,9 +279,7 @@ class TestChatGPTResponsesAPITransformation:
("chatgpt/gpt-5.3-codex", "gpt-5.3-codex"),
],
)
def test_chatgpt_non_stream_sse_response_recovers_output_items(
self, model_name: str, response_model: str
):
def test_chatgpt_non_stream_sse_response_recovers_output_items(self, model_name: str, response_model: str):
config = ChatGPTResponsesAPIConfig()
response_payload = {
"id": "resp_test",
@ -273,9 +302,7 @@ class TestChatGPTResponsesAPITransformation:
"",
]
)
raw_response = httpx.Response(
200, headers={"content-type": "text/event-stream"}, text=sse_body
)
raw_response = httpx.Response(200, headers={"content-type": "text/event-stream"}, text=sse_body)
logging_obj = MagicMock()
parsed = config.transform_response_api_response(
@ -315,9 +342,7 @@ class TestChatGPTResponsesAPITransformation:
"",
]
)
raw_response = httpx.Response(
200, headers={"content-type": "text/event-stream"}, text=sse_body
)
raw_response = httpx.Response(200, headers={"content-type": "text/event-stream"}, text=sse_body)
logging_obj = MagicMock()
parsed = config.transform_response_api_response(
@ -350,9 +375,7 @@ class TestChatGPTResponsesAPITransformation:
"",
]
)
raw_response = httpx.Response(
502, headers={"content-type": "text/event-stream"}, text=sse_body
)
raw_response = httpx.Response(502, headers={"content-type": "text/event-stream"}, text=sse_body)
logging_obj = MagicMock()
with pytest.raises(OpenAIError) as exc_info: