Add a SuppressedInferenceInvocation class to handle nested InferenceInvocation operations - #765
DylanRussell wants to merge 33 commits into
Conversation
…_context # Conflicts: # instrumentation/opentelemetry-instrumentation-genai-bedrock/tests/test_converse.py # instrumentation/opentelemetry-instrumentation-genai-bedrock/tests/test_invoke_model.py
…_context # Conflicts: # util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py # util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py
Pull request dashboard statusWaiting on the author · refreshed 2026-09-23 22:42 UTC Resolve merge conflicts. Respond to 3 review items (e.g. link a commit, explain why not, ask a follow-up): Status above doesn't look right?
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical issues remain in suppressed invocation construction and stream metadata processing.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (4)
What changed in this PR
Adds suppressed nested inference invocations to prevent duplicate telemetry while preserving context and stream enrichment.
Changes:
- Introduces
SuppressedInferenceInvocationand context propagation. - Updates stream handling for nested invocations.
- Adds tests, fixture isolation, Bedrock updates, and changelog coverage.
| File | Summary |
|---|---|
util/opentelemetry-util-genai/tests/test_utils.py |
Updates span relationship tests. |
util/opentelemetry-util-genai/tests/test_stream.py |
Tests suppressed stream processing. |
util/opentelemetry-util-genai/tests/test_context.py |
Tests nested invocation propagation. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/stream.py |
Adjusts nested stream handling. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/invocation.py |
Exports the new invocation type. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py |
Adds context and lifecycle support. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py |
Implements suppressed invocations and enrichment. |
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_context.py |
Adds inference context helpers. |
util/opentelemetry-util-genai/.changelog/663.added |
Documents context propagation. |
util/opentelemetry-test-util-genai/src/opentelemetry/test_util_genai/fixtures.py |
Adds context isolation. |
instrumentation/opentelemetry-instrumentation-genai-bedrock/tests/test_invoke_model.py |
Updates Bedrock invocation tests. |
instrumentation/opentelemetry-instrumentation-genai-bedrock/tests/test_converse.py |
Updates Bedrock converse tests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
lmolkova
left a comment
There was a problem hiding this comment.
thanks for putting this together!
I'm thinking about an approach where we use strongly typed context data and consolidate invocations at finish time.
Pros:
- Strict typing and explicit fields make precedence and validation much easier to reason about.
- Clean and contained lifecycle: publish on inner finish, enrich on outer finish.
- Plays nicely with Weaver codegen since the data container maps directly to schema models.
Cons:
- More manual boilerplate to map fields today (Weaver codegen in PR #702 will generate most of this anyway).
- Adding new semconv attributes requires updating the dataclass until codegen is in place.
Here is a proof of concept:
lmolkova@eb38238
lmk what you think
|
That link doesn't work for me. Precedence already seems very clear to me -- outer instrumentation always overrides inner. Inner only wins when outer hasn't set an attribute. Typing what's in the context seems a little overkill to me, it will probably make it harder for instrumentations outside the repo to use it. I dont think we gain much by typing it within the repo either -- it seems safe to assume the type and value is valid, our own code is typed already and just set the values. ultimately we just merge the two attribute maps and one map overrides the other. |
|
sorry for the broken link, here's the right one lmolkova@7dcc59d can you help me understand why and how instrumentation outside of this repo would care about internal details of suppression without taking dependency on util? It would need to put a very specific dict with very specific keys under internal context object. It's already tight enough coupling that would not let suppression work reliably if one of the libs does not use util. What we gain is the same logic for the same properties. request.model becomes attribute on both - spans and metrics, no need for two different places to know that. We also don't need to copy things over between dicts of unknown nature. |
|
Both Asking them to take a new dependency (which requires a newish version of the otel api/sdk) is a bigger ask than just reading / writing to the context which i think is straightforward without utils. Maybe they wouldn't do it in either case but we should verify that.. |
|
I'll ask someone from adk to take a look |
|
Merging the attributes back into the class is nice to do |
in order to participate in suppression, they would need to at least have access to the context key, which needs an implicit or explicit dependency. They can achieve suppression with minimal level of pain, but participating in merging attributes is an extra that I don't think is achievable without proper dependency on util. Having dictionaries with bespoke structure (even if documented) is implicit dependency with nothing guaranteeing any form of compatibility. I'd prefer to draw a line here - suppression of inner layers can be done easily, merging is a cherry on top. |
|
@DylanRussell created two side-by-side prototypes of native instrumentation that interacts with utils
Either way it's possible and relatively easy to do, but with optional / guarded importing it's more obvious what you do - we can keep the API public and enforce back-compat. With dictionaries everything is stringly type, brittle, and hard to enforce |



Description
Use class
SuppressedInferenceInvocationin place ofInferenceInvocationwhen an instrumentation starts an inference invocation that has already been started by a instrumentation higher up in the call stack..Same logic that is in #663 but uses a new child class to represent inner invocations, makes it more clear what inner invocations are responsible for -- mostly just updating the context.
Fixes # (issue)
Type of change
How has this been tested?
Unit tests
Checklist