mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-11 13:04:17 +00:00
fix: Support text.format parameter in Responses API for providers without native ResponsesAPIConfig (#16023)
Fixes #15995 When using the Responses API with providers that don't have a native ResponsesAPIConfig implementation (like Gemini, Anthropic, Cohere, etc.), the text.format parameter was being ignored. This happened because these providers fall back to using the LiteLLMCompletionTransformationHandler, which converts Responses API requests to Chat Completion API requests. Changes: - Added 'text' to the list of supported parameters in LiteLLMCompletionResponsesConfig.get_supported_openai_params() - Added transformation logic to convert text.format (Responses API format) to response_format (Chat Completion API format) in transform_responses_api_request_to_chat_completion_request() - Created _transform_text_format_to_response_format() method to handle the conversion between the two format structures The transformation supports: - json_schema: Converts to Chat Completion's json_schema format with proper nested structure - json_object: Converts to Chat Completion's json_object mode - text: Returns None (default text format) This fix benefits all providers that use the fallback transformation handler, ensuring consistent behavior with OpenAI's native Responses API implementation.
This commit is contained in:
committed by
GitHub
parent
b0a2e08a60
commit
29f0ed223a
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user