diff --git a/litellm/llms/openai/responses/transformation.py b/litellm/llms/openai/responses/transformation.py index 527ae4a9d49..12814286f6c 100644 --- a/litellm/llms/openai/responses/transformation.py +++ b/litellm/llms/openai/responses/transformation.py @@ -4,6 +4,9 @@ import httpx import litellm from litellm._logging import verbose_logger +from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( + _safe_convert_created_field, +) from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig from litellm.secret_managers.main import get_secret_str from litellm.types.llms.openai import * @@ -11,7 +14,6 @@ from litellm.types.responses.main import * from litellm.types.router import GenericLiteLLMParams from ..common_utils import OpenAIError -from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import _safe_convert_created_field if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj @@ -47,6 +49,8 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): "top_p", "truncation", "user", + "service_tier", + "safety_identifier", "extra_headers", "extra_query", "extra_body", diff --git a/litellm/responses/main.py b/litellm/responses/main.py index 07459547a0a..b5f18cf9e1a 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -243,6 +243,8 @@ async def aresponses( top_p: Optional[float] = None, truncation: Optional[Literal["auto", "disabled"]] = None, user: Optional[str] = None, + service_tier: Optional[str] = None, + safety_identifier: Optional[str] = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Optional[Dict[str, Any]] = None, @@ -294,6 +296,8 @@ async def aresponses( extra_body=extra_body, timeout=timeout, custom_llm_provider=custom_llm_provider, + service_tier=service_tier, + safety_identifier=safety_identifier, **kwargs, ) @@ -350,6 +354,8 @@ def responses( top_p: Optional[float] = None, truncation: Optional[Literal["auto", "disabled"]] = None, user: Optional[str] = None, + service_tier: Optional[str] = None, + safety_identifier: Optional[str] = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Optional[Dict[str, Any]] = None, diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 1955bfac5f8..bc3cfeee6a3 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -965,6 +965,8 @@ class ResponsesAPIOptionalRequestParams(TypedDict, total=False): top_p: Optional[float] truncation: Optional[Literal["auto", "disabled"]] user: Optional[str] + service_tier: Optional[str] + safety_identifier: Optional[str] prompt: Optional[PromptObject] diff --git a/tests/llm_responses_api_testing/test_openai_responses_api.py b/tests/llm_responses_api_testing/test_openai_responses_api.py index 87488f67e7a..5cd515be232 100644 --- a/tests/llm_responses_api_testing/test_openai_responses_api.py +++ b/tests/llm_responses_api_testing/test_openai_responses_api.py @@ -1308,3 +1308,90 @@ async def test_store_field_transformation(): assert response.created_at == 1751443898, "created_at should maintain the same value after conversion" +@pytest.mark.asyncio +async def test_aresponses_service_tier_and_safety_identifier(): + """ + Test that service_tier and safety_identifier parameters are correctly sent in the request body + when using litellm.aresponses. + """ + mock_response = { + "id": "resp_01234567890abcdef", + "object": "response", + "created_at": 1753060947, + "status": "completed", + "error": None, + "incomplete_details": None, + "instructions": None, + "max_output_tokens": None, + "model": "gpt-4o-2024-05-13", + "output": [ + { + "type": "text", + "id": "out_01234567890abcdef", + "text": "This is a test response with service tier and safety identifier.", + } + ], + "parallel_tool_calls": True, + "previous_response_id": None, + "reasoning": None, + "store": True, + "temperature": 1.0, + "text": {"format": {"type": "text"}}, + "tool_choice": "auto", + "tools": [], + "top_p": 1.0, + "truncation": "disabled", + "usage": { + "input_tokens": 15, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens": 25, + "output_tokens_details": {"reasoning_tokens": 0}, + "total_tokens": 40, + }, + "user": None, + "metadata": {}, + } + + class MockResponse: + def __init__(self, json_data, status_code): + self._json_data = json_data + self.status_code = status_code + self.text = json.dumps(json_data) + + def json(self): + return self._json_data + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new_callable=AsyncMock, + ) as mock_post: + # Configure the mock to return our response + mock_post.return_value = MockResponse(mock_response, 200) + + litellm._turn_on_debug() + litellm.set_verbose = True + + # Call aresponses with service_tier and safety_identifier + response = await litellm.aresponses( + model="openai/gpt-4o", + input="Test with service tier and safety identifier", + service_tier="flex", + safety_identifier="123", + ) + + # Verify the request was made correctly + mock_post.assert_called_once() + request_body = mock_post.call_args.kwargs["json"] + print("request_body=", json.dumps(request_body, indent=4, default=str)) + + # Validate that both parameters are present in the request body + assert request_body["service_tier"] == "flex", "service_tier should be 'flex' in request body" + assert request_body["safety_identifier"] == "123", "safety_identifier should be '123' in request body" + assert request_body["model"] == "gpt-4o" + assert request_body["input"] == "Test with service tier and safety identifier" + + # Validate the response + print("Response:", json.dumps(response, indent=4, default=str)) + + +