diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index cec34712c1..e021f4c16d 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -87,6 +87,7 @@ class LiteLLMCompletionResponsesConfig: "previous_response_id", "stream", "temperature", + "text", "tool_choice", "tools", "top_p", @@ -109,6 +110,14 @@ class LiteLLMCompletionResponsesConfig: tools, web_search_options = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( responses_api_request.get("tools") or [] # type: ignore ) + + response_format = None + text_param = responses_api_request.get("text") + if text_param: + response_format = LiteLLMCompletionResponsesConfig._transform_text_format_to_response_format( + text_param + ) + litellm_completion_request: dict = { "messages": LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages( input=input, @@ -126,6 +135,7 @@ class LiteLLMCompletionResponsesConfig: "metadata": kwargs.get("metadata"), "service_tier": kwargs.get("service_tier"), "web_search_options": web_search_options, + "response_format": response_format, # litellm specific params "custom_llm_provider": custom_llm_provider, "extra_headers": extra_headers, @@ -441,14 +451,14 @@ class LiteLLMCompletionResponsesConfig: ), index=0, ) - + # Create an assistant message with the tool call chat_completion_response_message = ChatCompletionResponseMessage( tool_calls=[tool_call], role="assistant", content=None, # Function calls don't have content ) - + return [chat_completion_response_message] @staticmethod @@ -626,19 +636,19 @@ class LiteLLMCompletionResponsesConfig: ) -> ResponsesAPIStatus: """ Map chat completion finish_reason to responses API status. - + Chat completion finish_reason values include: "stop", "length", "tool_calls", "content_filter", "function_call" Responses API status values are: "completed", "failed", "in_progress", "cancelled", "queued", "incomplete" - + Args: finish_reason: The finish_reason from a chat completion response - + Returns: The corresponding responses API status value (one of ResponsesAPIStatus) """ if finish_reason is None: return "completed" - + # Map finish reasons to status if finish_reason in ["stop", "tool_calls", "function_call"]: return "completed" @@ -664,7 +674,7 @@ class LiteLLMCompletionResponsesConfig: choices: List[Choices] = getattr(chat_completion_response, "choices", []) if choices and len(choices) > 0: finish_reason = choices[0].finish_reason - + responses_api_response: ResponsesAPIResponse = ResponsesAPIResponse( id=chat_completion_response.id, created_at=chat_completion_response.created, @@ -845,7 +855,7 @@ class LiteLLMCompletionResponsesConfig: transformed_annotations = LiteLLMCompletionResponsesConfig._transform_chat_completion_annotations_to_response_output_annotations( annotations=annotations ) - + return OutputText( type="output_text", text=message.content, @@ -895,15 +905,68 @@ class LiteLLMCompletionResponsesConfig: output_tokens=0, total_tokens=0, ) - + response_usage = ResponseAPIUsage( input_tokens=usage.prompt_tokens, output_tokens=usage.completion_tokens, total_tokens=usage.total_tokens, ) - + # Preserve cost field if it exists (for streaming usage with cost calculation) if hasattr(usage, "cost") and usage.cost is not None: setattr(response_usage, "cost", usage.cost) - + return response_usage + + @staticmethod + def _transform_text_format_to_response_format( + text_param: Union[Dict[str, Any], Any], + ) -> Optional[Dict[str, Any]]: + """ + Transform Responses API text.format parameter to Chat Completion response_format parameter. + + Responses API text parameter structure: + { + "format": { + "type": "json_schema", + "name": "schema_name", + "schema": {...}, + "strict": True + } + } + + Chat Completion response_format structure: + { + "type": "json_schema", + "json_schema": { + "name": "schema_name", + "schema": {...}, + "strict": True + } + } + """ + if not text_param: + return None + + if isinstance(text_param, dict): + format_param = text_param.get("format") + if format_param and isinstance(format_param, dict): + format_type = format_param.get("type") + + if format_type == "json_schema": + return { + "type": "json_schema", + "json_schema": { + "name": format_param.get("name", "response_schema"), + "schema": format_param.get("schema", {}), + "strict": format_param.get("strict", False), + } + } + elif format_type == "json_object": + return { + "type": "json_object" + } + elif format_type == "text": + return None + + return None