Skip to content

Prototype of span / event / metric deduplication and context passing for inference span - #663

Open
DylanRussell wants to merge 10 commits into
open-telemetry:mainfrom
DylanRussell:prototype_adding_on_context
Open

Prototype of span / event / metric deduplication and context passing for inference span#663
DylanRussell wants to merge 10 commits into
open-telemetry:mainfrom
DylanRussell:prototype_adding_on_context

Conversation

@DylanRussell

@DylanRussell DylanRussell commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Description

For InferenceInvocations we put an attributes dict onto the context to suppress duplicate downstream instrumentation from emitting spans / events / metrics, and instead have them just update the context with additional attributes if they want.

On the request path we could have "downstream" instrumentations execute exactly as normal, and in utils we check the attributes on the context. If a request attribute is already present on the context, we don't override it. If it isn't on the context we add it.

On the response path the "downstream" instrumentation goes first, so all response attributes will get added to the context.. The upstream instrumentation should probably iterate over the attributes in the context and only apply attributes it hasn't already set on the span.

Another tricky thing is the content capture attributes since those differ for spans / events, I think we leave those off the context and assume the "upstream" instrumentation will set those -- we could set InferenceInvocation.should_capture_content to false on downstream instrumentations if the context is set, to avoid a bit of work in the instrumentations.

We are putting all other span / event attributes onto the context (span / event attributes are the same excluding content capture attributes). We have a hardcoded a set of these attributes that should be applied on metrics -- we also look for token count attributes on the context.

There is a "metric_attributes" property on _invocation.. for now I'm not putting it onto the context, but maybe we want metric attributes to be passed similar to span/event attributes, we just need a way to differentiate them.. We could use a special prefix key on the attributes dict to do this.

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

Copilot AI lite review requested due to automatic review settings September 9, 2026 21:02
@DylanRussell
DylanRussell requested a review from a team as a code owner September 9, 2026 21:02
@DylanRussell
DylanRussell marked this pull request as draft September 9, 2026 21:02

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Adds a GenAI inference context mechanism that allows nested inference invocations to deduplicate spans/events/metrics while still propagating/enriching shared inference attributes via OpenTelemetry context.

Changes:

  • Introduces opentelemetry.util.genai.context helpers to store/retrieve inference attributes in Context.
  • Updates invocation lifecycle to support “already started upstream” nested inference calls (dedup), including attribute/metric enrichment from context.
  • Adds/updates unit tests covering context propagation, nested inference dedup/enrichment, and workflow parent/child span relationships.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
util/opentelemetry-util-genai/tests/test_utils.py Updates parent/child relationship test to use workflow span as parent.
util/opentelemetry-util-genai/tests/test_context.py Adds tests for inference attribute context, nested dedup/enrichment, and metrics behavior.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/context.py New module providing context key + get/set helpers for inference attributes.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_invocation.py Adds already_started flow, early-return start/finish behavior, and streaming changes for dedup scenarios.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py Implements publishing to context, context-based enrichment, and dedup finish behavior for nested inference.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/init.py Re-exports context helpers as part of the public package surface.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 10, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-09-12 00:28 UTC

Review the latest changes.

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.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

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

Great start!

Some ideas I was thinking of:

  • we should keep typed properties rather than raw attribute in a dict
  • we need to make it flexible and work for other invocations eventually
  • it should be easy to read and maintain, so having something like noop/nestedInferenceInvocation would help with it. It would just proxy setters to inner typed attribute representation
  • it should be completely invisible to instrumentation - it does not need to know about suppression like it does not care about parent spans.

Comment thread util/opentelemetry-util-genai/src/opentelemetry/util/genai/__init__.py Outdated
@DylanRussell

Copy link
Copy Markdown
Contributor Author

we should keep typed properties rather than raw attribute in a dict

My main concern with this one is it makes it hard to use for instrumentations outside this repo.. I see some benefit to this because we can pass unserialized data structures around the instrumentations, but I don't see a use case for that yet.

we need to make it flexible and work for other invocations eventually

SGTM

it should be easy to read and maintain, so having something like noop/nestedInferenceInvocation would help with it. It would just proxy setters to inner typed attribute representation

I don't follow this exactly -- can you clarify ?

it should be completely invisible to instrumentation - it does not need to know about suppression like it does not care about parent spans.

Mostly SGTM.. It might be interesting for instrumentations to know so they can avoid doing costly work (i'm thinking of the content-capture parsing, but maybe there's other stuff too)

@lmolkova

Copy link
Copy Markdown
Member

I did a small prototype to show noop/nestedinferenceinvocation to avoid fragile flag, PTAL:

lmolkova@e0de3d878

# _invalidate_metric_attributes whenever an input changes.
self._cached_metric_attributes: dict[str, AttributeValue] | None = None

existing_attrs = get_inference_attributes()

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.

from the discussion we had on Tue call, I think the apprach we discussed was:

  • outer just adds empty object on the context
  • inner adds attributes
  • on finish outer reconciles its attributes with inner

There is an edge case: when there are multiple inners. Both would write to the same object - this is an edge case that I don't think we need to care much about, so they can just overwrite each other.

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