Skip to content

Commit 5e71d09

Browse files
authored
fix: guard None text in ItemHelpers.extract_last_content (#3394)
1 parent cb7211b commit 5e71d09

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

src/agents/items.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,12 @@ def extract_last_content(cls, message: TResponseOutputItem) -> str:
684684
return ""
685685
last_content = message.content[-1]
686686
if isinstance(last_content, ResponseOutputText):
687-
return last_content.text
687+
# ``last_content.text`` is typed as ``str`` per the Responses API schema,
688+
# but provider gateways (e.g. LiteLLM) and ``model_construct`` paths during
689+
# streaming have been observed surfacing ``None``. Coerce so callers relying
690+
# on the ``-> str`` return type don't see a ``None``. Same rationale as
691+
# ``extract_text`` below.
692+
return last_content.text or ""
688693
elif isinstance(last_content, ResponseOutputRefusal):
689694
return last_content.refusal
690695
else:

tests/utils/test_pretty_print_and_items.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,30 @@ def test_text_message_outputs_handles_none_text_across_items():
3838
assert ItemHelpers.text_message_outputs(items) == "world"
3939

4040

41+
def _make_output_message(text: str | None) -> ResponseOutputMessage:
42+
return ResponseOutputMessage.model_construct(
43+
id="msg_1",
44+
role="assistant",
45+
status="completed",
46+
content=[ResponseOutputText.model_construct(type="output_text", text=text, annotations=[])],
47+
)
48+
49+
50+
def test_extract_last_content_returns_empty_string_for_none_text():
51+
"""extract_last_content is declared `-> str` and must not return None even if
52+
the underlying ResponseOutputText.text is None (observed via LiteLLM gateways
53+
and ``model_construct`` paths during streaming, per items.py:714-720)."""
54+
msg = _make_output_message(None)
55+
result = ItemHelpers.extract_last_content(msg)
56+
assert isinstance(result, str)
57+
assert result == ""
58+
59+
60+
def test_extract_last_content_returns_text_normally():
61+
msg = _make_output_message("hello")
62+
assert ItemHelpers.extract_last_content(msg) == "hello"
63+
64+
4165
def _make_run_error_details(n_input: int = 0, n_output: int = 0) -> RunErrorDetails:
4266
return RunErrorDetails(
4367
input="hi",

0 commit comments

Comments
 (0)