Skip to content

fix(core): fix streamed response span losing response/input when consumer stops at completed event - #4692

Open
XuQuanxin04 wants to merge 1 commit into
openai:mainfrom
XuQuanxin04:fix/streamed-response-span-io
Open

fix(core): fix streamed response span losing response/input when consumer stops at completed event#4692
XuQuanxin04 wants to merge 1 commit into
openai:mainfrom
XuQuanxin04:fix/streamed-response-span-io

Conversation

@XuQuanxin04

Copy link
Copy Markdown

Summary

When streaming with OpenAIResponsesModel, the model response and input were attached to the response_span only after the yield loop finished (lines 769-771). A consumer that stops iterating at the terminal response.completed event closes the generator at the yield, so that post-loop assignment never runs. The span is then exported with response and input set to None, even when sensitive-data capture is enabled (ModelTracing.ENABLED).

The SDK's own run loop consumes the stream exactly this way — run_single_turn_streamed wraps the model stream in aclosing(...) and breaks once it sees ResponseCompletedEvent — so the common streamed path silently lost the model I/O on the span.

This is asymmetric with three places that already populate the span before yielding for exactly this reason:

  • the non-streamed get_response path sets response/input inside the span (lines 612-614);
  • the sibling Chat Completions adapter populates its generation span before yielding the terminal event;
  • the failure path in this same method records the error before yielding (with a comment about the GeneratorExit hazard).

The usage field was already handled correctly before the yield; only response/input were deferred.

Fix

Attach response and input to the span inside the ResponseCompletedEvent branch, gated on tracing.include_data(), before the terminal event is yielded — mirroring the existing usage assignment right above it. Redaction when data capture is disabled is unchanged.

Test plan

Added test_stream_span_records_io_when_consumer_stops_at_completed, which mirrors the existing chatcompletions test test_stream_span_is_recorded_for_a_consumer_that_stops_at_the_terminal_event. It monkeypatches _fetch_response to emit a single response.completed, stops iterating at that event (as the run loop does), closes the generator, and asserts the response span carries non-None response, input, and usage.

  • Without the fix: response/input are None (reproduced).
  • With the fix: all three are populated.
  • Also verified that with ModelTracing.ENABLED_WITHOUT_DATA, response/input remain None (redaction preserved).
  • pytest tests/models/test_openai_responses.py — 156 passed
  • pytest tests/models/test_openai_chatcompletions_stream.py tests/test_provider_span_errors.py tests/tracing/ tests/test_agent_runner_streamed.py — 389 passed, 7 skipped
  • ruff format --check and ruff check clean on changed files.

No issue number; this is a self-contained observability correctness fix found while reading the span code.

Checks

  • I've added new tests, if relevant
  • I've run .agents/skills/code-change-verification/scripts/run.sh — could not run the bash script end-to-end on this Windows/Git-Bash host (it manages POSIX process groups via setsid/kill -TERM -PID); I ran its constituent steps directly (ruff format, ruff check, and the full relevant pytest suites above).
  • I've confirmed all verification steps pass

When streaming Responses, the model I/O was attached to the response
span only after the yield loop finished. A consumer that stops at the
terminal response.completed event closes the generator at the yield, so
that post-loop assignment never runs and the span is exported with
response and input left as None, even when sensitive-data capture is
enabled.

The SDK own run loop consumes the stream this way (aclosing + break on
ResponseCompletedEvent), so the common streamed path lost the model I/O.
This is also asymmetric with the non-streamed get_response path, the
sibling Chat Completions adapter, and the failure path in this method,
all of which attach data before yielding.

Attach response/input before yielding the completed event when data
capture is enabled, mirroring the existing usage handling.

Co-Authored-By: Claude <noreply@anthropic.com>
@seratch seratch changed the title Fix streamed response span losing response/input when consumer stops at completed event fix(core): fix streamed response span losing response/input when consumer stops at completed event Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants