diff --git a/src/agents/items.py b/src/agents/items.py index 50a017c221..c761cc221f 100644 --- a/src/agents/items.py +++ b/src/agents/items.py @@ -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 "" elif isinstance(last_content, ResponseOutputRefusal): return last_content.refusal else: diff --git a/tests/utils/test_pretty_print_and_items.py b/tests/utils/test_pretty_print_and_items.py index 34939df368..ab0fd6b821 100644 --- a/tests/utils/test_pretty_print_and_items.py +++ b/tests/utils/test_pretty_print_and_items.py @@ -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",