Skip to content

Add a SuppressedInferenceInvocation class to handle nested InferenceInvocation operations - #765

Open
DylanRussell wants to merge 33 commits into
open-telemetry:mainfrom
DylanRussell:suppressed_inference_invocation
Open

DylanRussell wants to merge 33 commits into
open-telemetry:mainfrom
DylanRussell:suppressed_inference_invocation

Conversation

@DylanRussell

@DylanRussell DylanRussell commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Description

Use class SuppressedInferenceInvocation in place of InferenceInvocation when 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

  • New feature (non-breaking change which adds functionality)

How has this been tested?

Unit tests

Checklist

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added
  • Documentation updated

DylanRussell and others added 26 commits September 9, 2026 20:42
…_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
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 21, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting 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):

  • Top-level threads: 1, 2, 3
Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Should this be with reviewers? Comment /dashboard route:reviewers to route it to them.
  • Anything wrong — including the routing? Report it with what you expected; it helps us improve the dashboard.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 High severity · 1 Medium severity · 1 Low severity

Open (4)
What changed in this PR

Adds suppressed nested inference invocations to prevent duplicate telemetry while preserving context and stream enrichment.

Changes:

  • Introduces SuppressedInferenceInvocation and 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.

Comment thread util/opentelemetry-util-genai/src/opentelemetry/util/genai/stream.py Outdated

@lmolkova lmolkova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@DylanRussell

DylanRussell commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

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.

@lmolkova

Copy link
Copy Markdown
Member

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.

@DylanRussell

Copy link
Copy Markdown
Contributor Author

Both adk and litellm have native instrumentation that emit inference spans and will be installed alongside libraries we instrument in this repo that also emit inference spans.

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..

@DylanRussell

Copy link
Copy Markdown
Contributor Author

I'll ask someone from adk to take a look

@DylanRussell

Copy link
Copy Markdown
Contributor Author

Merging the attributes back into the class is nice to do

@lmolkova

Copy link
Copy Markdown
Member

Both adk and litellm have native instrumentation that emit inference spans and will be installed alongside libraries we instrument in this repo that also emit inference spans.

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..

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.

@lmolkova

Copy link
Copy Markdown
Member

@DylanRussell created two side-by-side prototypes of native instrumentation that interacts with utils

  1. using typed object
  2. using dicts

97cadb5

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants