Prototype of span / event / metric deduplication and context passing for inference span - #663
Prototype of span / event / metric deduplication and context passing for inference span#663DylanRussell wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
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.contexthelpers to store/retrieve inference attributes inContext. - 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.
Pull request dashboard statusWaiting on reviewers · refreshed 2026-09-12 00:28 UTC Review the latest changes. Status above doesn't look right?
|
lmolkova
left a comment
There was a problem hiding this comment.
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.
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.
SGTM
I don't follow this exactly -- can you clarify ?
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) |
|
I did a small prototype to show noop/nestedinferenceinvocation to avoid fragile flag, PTAL: |
| # _invalidate_metric_attributes whenever an input changes. | ||
| self._cached_metric_attributes: dict[str, AttributeValue] | None = None | ||
|
|
||
| existing_attrs = get_inference_attributes() |
There was a problem hiding this comment.
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.
Description
For
InferenceInvocationswe 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
attributeson 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_contentto 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
How has this been tested?
Unit tests
Checklist