Repository navigation
[opentelemetry-util-genai] Add shared agent stream wrappers and migrate agent instrumentations - #1033
Open
eternalcuriouslearner wants to merge 17 commits into
Conversation
Add SyncAgentStreamWrapper and AsyncAgentStreamWrapper so agent instrumentations share streamed output accumulation, structured content serialization, context suspension and finalization. Part of open-telemetry#595. Assisted-by: Claude Opus 5.5
Assisted-by: Claude Opus 5.5
Pull request dashboard statusWaiting on reviewers · refreshed 2026-11-04 00:39 UTC Review the latest changes. Status above doesn't look right?
|
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unbounded output buffering can exhaust memory, while conversion, initialization, and cleanup failures can disrupt streams or leave invocations unfinished.
6 open issues
Bound captured stream output to prevent unbounded memory growth · New Ignore content conversion failures without interrupting streams · New Preserve JSON-compatible mapping keys and null values · New Stop buffering text after message output takes precedence · New Close abandoned streams before finalizing invocation context · New Finalize invocation when stream initialization fails · New
What changed in this PR
Adds shared synchronous and asynchronous agent/workflow stream wrappers to opentelemetry-util-genai, addressing the reusable stream-handling portion of #595. Consumer migrations remain separate.
Changes:
- Adds output accumulation, structured-content serialization, and invocation lifecycle handling without inference-only streaming metrics.
- Tests output handling, context isolation, errors, closure, and abandonment.
- Documents the subclass API and adds a release note.
| File | Description |
|---|---|
util/opentelemetry-util-genai/tests/test_agent_stream.py |
Tests agent/workflow stream behavior and telemetry. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/stream.py |
Adds sync/async wrappers with shared output handling. |
util/opentelemetry-util-genai/README.rst |
Documents wrapper usage and output-reporting hooks. |
util/opentelemetry-util-genai/.changelog/1033.added |
Announces the new stream wrappers. |
Review effort: Balanced
- Skip None and unserializable output instead of raising into the stream. - Stop accumulating text once output messages take precedence. - Fail the invocation and restore context if wrapping the stream raises. Assisted-by: Claude Opus 5.5
This was referenced Nov 3, 2026
Assisted-by: Claude Opus 5.5
- Use the base wrapper's invocation reference instead of a second copy. - Share init-failure handling and output setup through the mixin. - Drop a capture check that cannot change the result and unused test helpers. Assisted-by: Claude Opus 5.5
Instrumentations serialize structured content to text on non-streamed paths too, so make the helper public in utils instead of private to the stream module. Assisted-by: Claude Opus 5.5
Dropping the last reference at break finalizes the stream before the caller's span starts, so the tests passed even without suspension. Assisted-by: Claude Opus 5.5
…appers Build the agent and workflow stream wrappers on util-genai's SyncAgentStreamWrapper / AsyncAgentStreamWrapper and replace the local format_content with util's content_to_text, dropping the duplicated buffering, serialization and finalization. Stream spans are now suspended between chunks, and agent streams report finish reasons on gen_ai.response.finish_reasons. Assisted-by: Claude Opus 5.5
Assisted-by: Claude Opus 5.5
… wrapper Build QueryHandlerStreamWrapper on util-genai's AsyncAgentStreamWrapper, so the invoke_agent invocation is passed to the base wrapper and suspended between chunks instead of staying current in the caller's context. Fixes open-telemetry#386. Assisted-by: Claude Opus 5.5
Assisted-by: Claude Opus 5.5
Assisted-by: Claude Opus 5.5
…eam wrapper Build _AgentRunStreamWrapper on util-genai's SyncAgentStreamWrapper so the invoke_agent invocation is suspended between chunks instead of staying current in the caller's context. Assisted-by: Claude Opus 5.5
…eam wrapper Build the streamed agent run wrapper on util-genai's SyncAgentStreamWrapper and drop its own finalize-once flag, destructor and BaseException handling, which the base wrapper already provides. The invoke_agent invocation is now suspended between steps. Assisted-by: Claude Opus 5.5
Resolve smolagents conflicts with open-telemetry#1006: keep the shared agent stream wrapper and carry over closing an abandoned run inside the agent span. Assisted-by: Claude Opus 5.5
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Final-answer handling can leave generators and spans open or interrupt delivery, while other changes mishandle content capture, scalar serialization, and early stream closure.
5 open issues
6 resolved since last review
Bound captured stream output to prevent unbounded memory growth Finalize invocation when stream initialization fails Close abandoned streams before finalizing invocation context Stop buffering text after message output takes precedence Preserve JSON-compatible mapping keys and null values Ignore content conversion failures without interrupting streams
Review effort: Balanced
- smolagents: close a held run generator even after the final answer ended the span, and always stop the invocation if recording the answer fails. - agno: report no finish reason for a run the caller closed before it completed, using a new _self_exhausted flag on the base wrappers. - qwen-agent: skip output conversion when content capture is off. - util: return scalars such as Decimal and Path from content_to_text without JSON quoting. Assisted-by: Claude Opus 5.5
A completion event marks the run as finished even when it carries no content, so a caller closing right after it keeps the stop finish reason. Assisted-by: Claude Opus 5.5
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes #595, fixes #386. Adds
SyncAgentStreamWrapper/AsyncAgentStreamWrapperand a publicutils.content_to_textto util-genai, and moves the agno, qwenpaw, qwen-agent and smolagents agent/workflow stream wrappers onto them. Util and the migrations are in one PR on purpose, so the API can be reviewed alongside every consumer.Why: each package hand-rolled output buffering, structured-content serialization and finalization. The ones that did not pass their invocation to the base wrapper also kept
invoke_agentcurrent in the caller's context while a stream was held unconsumed, so unrelated caller spans nested under it (#386). With #880 merged, agent invocations can use the base wrapper directly.Behavior changes:
gen_ai.response.finish_reasonsinstead of the deprecated per-messagefinish_reason.Known gaps:
>= 1.4b0.dev;tests/requirements.oldest.txtinstalls util from the tree until 1.4b0 ships (as langchain does).🤖 Generated with Claude Code