Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/agents/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,12 @@ def extract_last_content(cls, message: TResponseOutputItem) -> str:
return ""
last_content = message.content[-1]
if isinstance(last_content, ResponseOutputText):
return last_content.text
# ``last_content.text`` is typed as ``str`` per the Responses API schema,
# but provider gateways (e.g. LiteLLM) and ``model_construct`` paths during
# streaming have been observed surfacing ``None``. Coerce so callers relying
# on the ``-> str`` return type don't see a ``None``. Same rationale as
# ``extract_text`` below.
return last_content.text or ""
Comment thread
ioleksiuk marked this conversation as resolved.
elif isinstance(last_content, ResponseOutputRefusal):
return last_content.refusal
else:
Expand Down
24 changes: 24 additions & 0 deletions tests/utils/test_pretty_print_and_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ def test_text_message_outputs_handles_none_text_across_items():
assert ItemHelpers.text_message_outputs(items) == "world"


def _make_output_message(text: str | None) -> ResponseOutputMessage:
return ResponseOutputMessage.model_construct(
id="msg_1",
role="assistant",
status="completed",
content=[ResponseOutputText.model_construct(type="output_text", text=text, annotations=[])],
)


def test_extract_last_content_returns_empty_string_for_none_text():
"""extract_last_content is declared `-> str` and must not return None even if
the underlying ResponseOutputText.text is None (observed via LiteLLM gateways
and ``model_construct`` paths during streaming, per items.py:714-720)."""
msg = _make_output_message(None)
result = ItemHelpers.extract_last_content(msg)
assert isinstance(result, str)
assert result == ""


def test_extract_last_content_returns_text_normally():
msg = _make_output_message("hello")
assert ItemHelpers.extract_last_content(msg) == "hello"


def _make_run_error_details(n_input: int = 0, n_output: int = 0) -> RunErrorDetails:
return RunErrorDetails(
input="hi",
Expand Down
Loading