feat(types): add output_text property to ResponsesAPIResponse (#18491)

Add the output_text convenience property to ResponsesAPIResponse that
aggregates all output_text items from the output list, matching the
OpenAI SDK's Response.output_text behavior.

The property iterates through output items, collects text content from
message-type outputs, and returns them concatenated into a single
string. Returns empty string if no output_text content exists.

Handles both dict and Pydantic model access patterns for compatibility
with different output formats.

Fixes #18470

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: yurekami <yurekami@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ゆり
2026-01-04 03:08:32 +08:00
committed by GitHub
parent dc62cdb300
commit 9ba27d85ce
2 changed files with 167 additions and 0 deletions
+33
View File
@@ -1197,6 +1197,39 @@ class ResponsesAPIResponse(BaseLiteLLMOpenAIResponseObject):
# Define private attributes using PrivateAttr
_hidden_params: dict = PrivateAttr(default_factory=dict)
@property
def output_text(self) -> str:
"""
Convenience property that aggregates all `output_text` items from the `output` list.
If no `output_text` content blocks exist, then an empty string is returned.
This matches the OpenAI SDK's Response.output_text behavior.
"""
texts: List[str] = []
for output_item in self.output:
# Handle both dict and object access patterns
if isinstance(output_item, dict):
item_type = output_item.get("type")
content = output_item.get("content", [])
else:
item_type = getattr(output_item, "type", None)
content = getattr(output_item, "content", [])
if item_type == "message":
for content_item in content:
if isinstance(content_item, dict):
content_type = content_item.get("type")
text = content_item.get("text", "")
else:
content_type = getattr(content_item, "type", None)
text = getattr(content_item, "text", "") or ""
if content_type == "output_text":
texts.append(text)
return "".join(texts)
class ResponsesAPIStreamEvents(str, Enum):
"""
@@ -35,3 +35,137 @@ def test_output_item_added_event():
assert event.sequence_number == 4
assert event.output_index == 1
assert event.item is None
class TestResponsesAPIResponseOutputText:
"""Tests for the output_text property on ResponsesAPIResponse"""
def test_output_text_with_single_message(self):
"""Test output_text with a single message containing text output"""
from litellm.types.llms.openai import ResponsesAPIResponse
response = ResponsesAPIResponse(
id="resp_123",
created_at=1234567890,
output=[
{
"type": "message",
"id": "msg_123",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Hello, world!",
}
],
}
],
)
assert response.output_text == "Hello, world!"
def test_output_text_with_multiple_messages(self):
"""Test output_text with multiple messages aggregates all text"""
from litellm.types.llms.openai import ResponsesAPIResponse
response = ResponsesAPIResponse(
id="resp_123",
created_at=1234567890,
output=[
{
"type": "message",
"id": "msg_1",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "First part. ",
}
],
},
{
"type": "message",
"id": "msg_2",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Second part.",
}
],
},
],
)
assert response.output_text == "First part. Second part."
def test_output_text_with_no_text_content(self):
"""Test output_text returns empty string when no output_text content exists"""
from litellm.types.llms.openai import ResponsesAPIResponse
response = ResponsesAPIResponse(
id="resp_123",
created_at=1234567890,
output=[
{
"type": "function_call",
"id": "call_123",
"status": "completed",
"name": "get_weather",
"arguments": "{}",
}
],
)
assert response.output_text == ""
def test_output_text_with_mixed_content(self):
"""Test output_text only aggregates output_text type content"""
from litellm.types.llms.openai import ResponsesAPIResponse
response = ResponsesAPIResponse(
id="resp_123",
created_at=1234567890,
output=[
{
"type": "message",
"id": "msg_1",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "The weather is sunny. ",
},
{
"type": "refusal",
"refusal": "I cannot do that.",
},
],
},
{
"type": "function_call",
"id": "call_123",
"status": "completed",
"name": "get_weather",
"arguments": "{}",
},
],
)
assert response.output_text == "The weather is sunny. "
def test_output_text_with_empty_output(self):
"""Test output_text returns empty string with empty output list"""
from litellm.types.llms.openai import ResponsesAPIResponse
response = ResponsesAPIResponse(
id="resp_123",
created_at=1234567890,
output=[],
)
assert response.output_text == ""