mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
fix(chatgpt5.4): recover output text deltas when complete output is empty
This commit is contained in:
parent
14f6dd5c8c
commit
c5234f3db5
2 changed files with 68 additions and 0 deletions
|
|
@ -133,7 +133,9 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
)
|
||||
|
||||
completed_response = None
|
||||
completed_response_payload = None
|
||||
error_message = None
|
||||
output_text_parts = []
|
||||
for chunk in body_text.splitlines():
|
||||
stripped_chunk = CustomStreamWrapper._strip_sse_data_from_chunk(chunk)
|
||||
if not stripped_chunk:
|
||||
|
|
@ -150,10 +152,16 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
if not isinstance(parsed_chunk, dict):
|
||||
continue
|
||||
event_type = parsed_chunk.get("type")
|
||||
if event_type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA:
|
||||
content_part = parsed_chunk.get("delta", None)
|
||||
if isinstance(content_part, str) and content_part:
|
||||
output_text_parts.append(content_part)
|
||||
continue
|
||||
if event_type == ResponsesAPIStreamEvents.RESPONSE_COMPLETED:
|
||||
response_payload = parsed_chunk.get("response")
|
||||
if isinstance(response_payload, dict):
|
||||
response_payload = dict(response_payload)
|
||||
completed_response_payload = response_payload
|
||||
if "created_at" in response_payload:
|
||||
response_payload["created_at"] = _safe_convert_created_field(
|
||||
response_payload["created_at"]
|
||||
|
|
@ -178,6 +186,34 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
else:
|
||||
error_message = str(error_obj)
|
||||
|
||||
if (
|
||||
completed_response_payload is not None
|
||||
and not completed_response_payload.get("output")
|
||||
and len(output_text_parts) > 0
|
||||
):
|
||||
completed_response_payload["output"] = [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "".join(output_text_parts),
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
if "created_at" in completed_response_payload:
|
||||
completed_response_payload["created_at"] = _safe_convert_created_field(
|
||||
completed_response_payload["created_at"]
|
||||
)
|
||||
try:
|
||||
completed_response = ResponsesAPIResponse(**completed_response_payload)
|
||||
except Exception:
|
||||
completed_response = ResponsesAPIResponse.model_construct(
|
||||
**completed_response_payload
|
||||
)
|
||||
|
||||
if completed_response is None:
|
||||
raise OpenAIError(
|
||||
message=error_message or raw_response.text,
|
||||
|
|
|
|||
|
|
@ -201,3 +201,35 @@ class TestChatGPTResponsesAPITransformation:
|
|||
)
|
||||
|
||||
assert parsed.output_text == "Hello!"
|
||||
|
||||
def test_chatgpt_non_stream_sse_response_parsing_empty_completed_output(self):
|
||||
config = ChatGPTResponsesAPIConfig()
|
||||
response_payload = {
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1700000000,
|
||||
"status": "completed",
|
||||
"model": "gpt-5.4",
|
||||
"output": [],
|
||||
}
|
||||
sse_body = "\n".join(
|
||||
[
|
||||
f"data: {json.dumps({'type': 'response.output_text.delta', 'delta': 'Hel'})}",
|
||||
f"data: {json.dumps({'type': 'response.output_text.delta', 'delta': 'lo!'})}",
|
||||
f"data: {json.dumps({'type': 'response.completed', 'response': response_payload})}",
|
||||
"data: [DONE]",
|
||||
"",
|
||||
]
|
||||
)
|
||||
raw_response = httpx.Response(
|
||||
200, headers={"content-type": "text/event-stream"}, text=sse_body
|
||||
)
|
||||
logging_obj = MagicMock()
|
||||
|
||||
parsed = config.transform_response_api_response(
|
||||
model="chatgpt/gpt-5.4",
|
||||
raw_response=raw_response,
|
||||
logging_obj=logging_obj,
|
||||
)
|
||||
|
||||
assert parsed.output_text == "Hello!"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue