mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-02 04:21:34 +00:00
Merge pull request #16844 from Chesars/fix/response-format-to-text-format-bridge-conversion
fix: Support response_format parameter in completion -> responses bridge
This commit is contained in:
@@ -234,6 +234,11 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
||||
cast(List[Dict[str, Any]], value)
|
||||
)
|
||||
)
|
||||
elif key == "response_format":
|
||||
# Convert response_format to text.format
|
||||
text_format = self._transform_response_format_to_text_format(value)
|
||||
if text_format:
|
||||
responses_api_request["text"] = text_format # type: ignore
|
||||
elif key in ResponsesAPIOptionalRequestParams.__annotations__.keys():
|
||||
responses_api_request[key] = value # type: ignore
|
||||
elif key == "metadata":
|
||||
@@ -666,6 +671,63 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
||||
return Reasoning(effort="minimal")
|
||||
return None
|
||||
|
||||
def _transform_response_format_to_text_format(
|
||||
self, response_format: Union[Dict[str, Any], Any]
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Transform Chat Completion response_format parameter to Responses API text.format parameter.
|
||||
|
||||
Chat Completion response_format structure:
|
||||
{
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "schema_name",
|
||||
"schema": {...},
|
||||
"strict": True
|
||||
}
|
||||
}
|
||||
|
||||
Responses API text parameter structure:
|
||||
{
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"name": "schema_name",
|
||||
"schema": {...},
|
||||
"strict": True
|
||||
}
|
||||
}
|
||||
"""
|
||||
if not response_format:
|
||||
return None
|
||||
|
||||
if isinstance(response_format, dict):
|
||||
format_type = response_format.get("type")
|
||||
|
||||
if format_type == "json_schema":
|
||||
json_schema = response_format.get("json_schema", {})
|
||||
return {
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"name": json_schema.get("name", "response_schema"),
|
||||
"schema": json_schema.get("schema", {}),
|
||||
"strict": json_schema.get("strict", False),
|
||||
}
|
||||
}
|
||||
elif format_type == "json_object":
|
||||
return {
|
||||
"format": {
|
||||
"type": "json_object"
|
||||
}
|
||||
}
|
||||
elif format_type == "text":
|
||||
return {
|
||||
"format": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def _map_responses_status_to_finish_reason(self, status: Optional[str]) -> str:
|
||||
"""Map responses API status to chat completion finish_reason"""
|
||||
if not status:
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
Test for response_format to text.format conversion in completion -> responses bridge
|
||||
"""
|
||||
import pytest
|
||||
from litellm.completion_extras.litellm_responses_transformation.transformation import (
|
||||
LiteLLMResponsesTransformationHandler,
|
||||
)
|
||||
|
||||
|
||||
def test_transform_response_format_to_text_format_json_schema():
|
||||
"""Test conversion of response_format with json_schema to text.format"""
|
||||
handler = LiteLLMResponsesTransformationHandler()
|
||||
|
||||
# Chat Completion format
|
||||
response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "person_schema",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"age": {"type": "integer"}
|
||||
},
|
||||
"required": ["name", "age"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
"strict": True
|
||||
}
|
||||
}
|
||||
|
||||
# Convert to Responses API format
|
||||
result = handler._transform_response_format_to_text_format(response_format)
|
||||
|
||||
# Verify conversion
|
||||
assert result is not None
|
||||
assert "format" in result
|
||||
assert result["format"]["type"] == "json_schema"
|
||||
assert result["format"]["name"] == "person_schema"
|
||||
assert result["format"]["strict"] is True
|
||||
assert "schema" in result["format"]
|
||||
assert result["format"]["schema"]["type"] == "object"
|
||||
assert "properties" in result["format"]["schema"]
|
||||
|
||||
|
||||
def test_transform_response_format_to_text_format_json_object():
|
||||
"""Test conversion of response_format with json_object to text.format"""
|
||||
handler = LiteLLMResponsesTransformationHandler()
|
||||
|
||||
response_format = {
|
||||
"type": "json_object"
|
||||
}
|
||||
|
||||
result = handler._transform_response_format_to_text_format(response_format)
|
||||
|
||||
assert result is not None
|
||||
assert "format" in result
|
||||
assert result["format"]["type"] == "json_object"
|
||||
|
||||
|
||||
def test_transform_response_format_to_text_format_text():
|
||||
"""Test conversion of response_format with text to text.format"""
|
||||
handler = LiteLLMResponsesTransformationHandler()
|
||||
|
||||
response_format = {
|
||||
"type": "text"
|
||||
}
|
||||
|
||||
result = handler._transform_response_format_to_text_format(response_format)
|
||||
|
||||
assert result is not None
|
||||
assert "format" in result
|
||||
assert result["format"]["type"] == "text"
|
||||
|
||||
|
||||
def test_transform_response_format_to_text_format_none():
|
||||
"""Test that None input returns None"""
|
||||
handler = LiteLLMResponsesTransformationHandler()
|
||||
|
||||
result = handler._transform_response_format_to_text_format(None)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_transform_request_with_response_format():
|
||||
"""Test that transform_request correctly handles response_format parameter"""
|
||||
handler = LiteLLMResponsesTransformationHandler()
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "Extract person info: John Doe, 30 years old"}
|
||||
]
|
||||
|
||||
optional_params = {
|
||||
"response_format": {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "person_schema",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"age": {"type": "integer"}
|
||||
},
|
||||
"required": ["name", "age"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
"strict": True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
litellm_params = {}
|
||||
headers = {}
|
||||
|
||||
# Mock logging object
|
||||
class MockLoggingObj:
|
||||
pass
|
||||
|
||||
litellm_logging_obj = MockLoggingObj()
|
||||
|
||||
result = handler.transform_request(
|
||||
model="o3-pro",
|
||||
messages=messages,
|
||||
optional_params=optional_params,
|
||||
litellm_params=litellm_params,
|
||||
headers=headers,
|
||||
litellm_logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
||||
# Verify that text parameter was set with converted format
|
||||
assert "text" in result
|
||||
assert result["text"] is not None
|
||||
assert "format" in result["text"]
|
||||
assert result["text"]["format"]["type"] == "json_schema"
|
||||
assert result["text"]["format"]["name"] == "person_schema"
|
||||
assert "schema" in result["text"]["format"]
|
||||
Reference in New Issue
Block a user