Skip to content

test(llmobs): subset-match metadata in assert_llmobs_span_event - #7642

Merged
Yun-Kim merged 4 commits into
mainfrom
yun-kim/llmobs-metadata-subset-match
Sep 4, 2026
Merged

test(llmobs): subset-match metadata in assert_llmobs_span_event#7642
Yun-Kim merged 4 commits into
mainfrom
yun-kim/llmobs-metadata-subset-match

Conversation

@Yun-Kim

@Yun-Kim Yun-Kim commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Motivation

assert_llmobs_span_event compares the whole span event by equality, so metadata must match exactly. When a tracer starts emitting a new provider field it lands on every span of that kind, failing every test for that integration at once — for a purely additive change.

That's blocking DataDog/dd-trace-py#19752 (adds finish_reason/stop_reason): 4 jobs + the system-tests finished gate are red only because the actual metadata gained one key. The alternative is editing ~23 metadata dicts, and repeating that for every tracer and future addition.

Changes

Assert metadata as a shallow subset — declared keys must still match exactly, unlisted extras are tolerated — and leave mock.ANY in its place so the whole-event equality still passes.

Same semantics as tags in _assert_tags_span_event_tags, and as dd-trace-py's own assert_llmobs_span_data.

Nothing else is relaxed: the rest of the event stays exact equality, and the metadata=None branch is untouched (all 33 call sites pass metadata=, so it's unused today).

Tradeoff: a test can no longer assert a declared key is the only metadata present. Notably metadata={} (4 google_genai tests) now means "metadata present, any value" rather than "exactly {}". Happy to add an opt-in metadata_exact=True if you'd rather keep that available.

Testing

Exercised the real helper against: exact match, the finish_reason/stop_reason extras, wrong declared value, missing declared key, differing nested dict, empty metadata, mock.ANY as a declared value, ignore_values=["meta.metadata"], non-mutation of the caller's event, and metadata=None still rejecting stray metadata. Wrong values and missing keys still fail. mypy + ruff clean.

Real signal is dd-trace-py#19752 going green against this branch; not repinned yet since this should land first.


🤖 Generated with Claude Code

Claude session: acefbd3c-6218-49bb-88c2-c77f319c0b78
Resume: claude --resume acefbd3c-6218-49bb-88c2-c77f319c0b78

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

tests/integration_frameworks/llm/utils.py                               @DataDog/ml-observability

@Yun-Kim
Yun-Kim force-pushed the yun-kim/llmobs-metadata-subset-match branch from 9ad62c6 to 809d575 Compare September 2, 2026 21:06
Comment thread tests/integration_frameworks/llm/utils.py Outdated
@Yun-Kim
Yun-Kim force-pushed the yun-kim/llmobs-metadata-subset-match branch from 809d575 to ae467e2 Compare September 2, 2026 21:12
assert_llmobs_span_event compares the whole span event by equality, so metadata
is matched exactly. Metadata is the one part of the payload that grows on its
own: when a tracer starts surfacing a new provider field it lands on every span
of that kind, and every test covering that integration fails at once — for a
change that is additive by design.

Assert metadata as a shallow subset and leave mock.ANY in its place on the
expected side, so declared keys still have to match exactly while unlisted
extras are tolerated. Tags already work this way in
_assert_tags_span_event_tags, and dd-trace-py's own assert_llmobs_span_data
treats metadata/tags/metrics as a shallow subset.

Nothing else is relaxed: the rest of the event stays exact equality, and the
metadata=None path is unchanged (all 33 call sites in the llm suites pass
metadata=, so that branch is unused today).

Unblocks DataDog/dd-trace-py#19752, which adds finish_reason/stop_reason to
openai, litellm and anthropic LLM span metadata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Yun-Kim
Yun-Kim force-pushed the yun-kim/llmobs-metadata-subset-match branch from ae467e2 to b4124e2 Compare September 2, 2026 21:14
@Yun-Kim
Yun-Kim marked this pull request as ready for review September 2, 2026 21:16
@Yun-Kim
Yun-Kim requested a review from a team as a code owner September 2, 2026 21:16

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4124e2d8a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread tests/integration_frameworks/llm/utils.py Outdated
@datadog-official

datadog-official Bot commented Sep 2, 2026

Copy link
Copy Markdown

Tests

All CI checks and tests passed.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: dd51b1b | Docs | View more details | Give us feedback!

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

failures related to codex comment it seems.

edit: i had previously commented about something i had misread/misunderstood, i think otherwise this is good!

Yun-Kim and others added 3 commits September 3, 2026 15:38
test_openai_llmobs.py:322 and :723 pass mock.ANY as the whole metadata
argument, not a dict. The subset comparison called .items() on it and raised
AttributeError before the span was validated, so both OpenAI error-case tests
hard-errored in the INTEGRATION_FRAMEWORKS scenario.

Only subset-compare when metadata is a dict. The wildcard keeps its previous
meaning: metadata must be present, any value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Yun-Kim
Yun-Kim merged commit 6694e9c into main Sep 4, 2026
439 checks passed
@Yun-Kim
Yun-Kim deleted the yun-kim/llmobs-metadata-subset-match branch September 4, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants