Skip to content

fix(observability): emit user_id as span attribute in SpanExporter - #2152

Open
Christian-Sidak wants to merge 1 commit into
NVIDIA:developfrom
Christian-Sidak:fix/issue-2151
Open

fix(observability): emit user_id as span attribute in SpanExporter#2152
Christian-Sidak wants to merge 1 commit into
NVIDIA:developfrom
Christian-Sidak:fix/issue-2151

Conversation

@Christian-Sidak

@Christian-Sidak Christian-Sidak commented Aug 9, 2026

Copy link
Copy Markdown

Summary

  • ContextState.user_id was resolved and stored by the runtime but never read by SpanExporter._process_start_event, so no span ever carried user identity.
  • Add nat.user.id to the initial attributes dict alongside nat.conversation.id.
  • Set user.id on each span after creation, mirroring the existing session.id/conversation_id pattern, so Langfuse and other OTLP backends can attribute traces to the correct user.

Fixes #2151

Test plan

  • test_user_id_emitted_as_span_attribute: sets ContextState.user_id to "USER-42", exports a span, and asserts both nat.user.id and user.id attributes equal "USER-42".
  • test_user_id_absent_when_not_set: sets ContextState.user_id to None, exports a span, and asserts nat.user.id is "unknown" and user.id is absent (matching the session.id behavior).
  • Full existing test suite for test_span_exporter.py continues to pass (39 pass, 6 pre-existing fixture errors unchanged).

Summary by CodeRabbit

  • New Features

    • Observability spans now include the current user ID for improved trace context.
    • User identifiers are recorded using both standard and application-specific attributes when available.
    • Spans without a user ID are handled gracefully with an “unknown” value and no standard user attribute.
  • Tests

    • Added coverage for spans with and without user identifiers.

ContextState.user_id was stored but never read by the span exporter.
Add nat.user.id to the initial attributes dict and set user.id on each
span (mirroring the existing session.id/conversation_id pattern) so
Langfuse and other OTLP backends can attribute traces to the correct user.

Fixes NVIDIA#2151

Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
@Christian-Sidak
Christian-Sidak requested a review from a team as a code owner August 9, 2026 04:28
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The span exporter now records ContextState.user_id as nat.user.id and, when available, as user.id. Tests cover configured and unset user IDs.

Changes

Span user attribution

Layer / File(s) Summary
Context user ID span attributes
packages/nvidia_nat_core/src/nat/observability/exporter/span_exporter.py, packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py
The exporter emits nat.user.id, defaults missing values to "unknown", and conditionally emits user.id. Tests cover both configured and unset user IDs.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise, descriptive, uses imperative mood, and accurately describes the SpanExporter change.
Linked Issues check ✅ Passed The changes emit ContextState.user_id as nat.user.id and user.id, directly addressing issue #2151.
Out of Scope Changes check ✅ Passed All changes are limited to SpanExporter user attributes and tests required by issue #2151.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py (1)

665-665: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Quote code entities in the new docstrings.

Wrap user_id, ContextState, nat.user.id, and user.id in backticks. This prevents Vale false positives and keeps the new documentation consistent with repository rules.

As per coding guidelines, surround code entities with backticks to avoid Vale false positives in docstrings.

Proposed docstring fix
-        """user_id from ContextState is emitted as nat.user.id and user.id span attributes."""
+        """Emit `user_id` from `ContextState` as `nat.user.id` and `user.id` span attributes."""
...
-        """When user_id is not set, user.id is not added as a span attribute."""
+        """Verify that `user.id` is absent when `user_id` is not set."""

Also applies to: 687-687

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py`
at line 665, Update the new docstrings in the span exporter tests around the
user ID assertions to wrap the code entities `user_id`, `ContextState`,
`nat.user.id`, and `user.id` in backticks, including the corresponding docstring
at the additionally referenced location.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py`:
- Around line 664-684: Update test_user_id_emitted_as_span_attribute and the
related test at lines 686-706 so assertions derive the attribute key from
span_exporter’s configured prefix rather than hard-coding "nat.user.id", or
configure the fixture exporter with span_prefix="nat". Keep the user.id
assertion unchanged.

---

Nitpick comments:
In
`@packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py`:
- Line 665: Update the new docstrings in the span exporter tests around the user
ID assertions to wrap the code entities `user_id`, `ContextState`,
`nat.user.id`, and `user.id` in backticks, including the corresponding docstring
at the additionally referenced location.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bb2f4151-ba02-45c8-9f0d-d48ce81ba6dd

📥 Commits

Reviewing files that changed from the base of the PR and between 2618705 and 62ddbcd.

📒 Files selected for processing (2)
  • packages/nvidia_nat_core/src/nat/observability/exporter/span_exporter.py
  • packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py

Comment on lines +664 to +684
def test_user_id_emitted_as_span_attribute(self, span_exporter):
"""user_id from ContextState is emitted as nat.user.id and user.id span attributes."""
from nat.builder.context import ContextState

ctx_state = ContextState.get()
token = ctx_state.user_id.set("USER-42")
try:
event = create_intermediate_step(event_type=IntermediateStepType.LLM_START,
framework=LLMFrameworkEnum.LANGCHAIN,
name="test_llm",
event_timestamp=datetime.now().timestamp(),
data=StreamEventData(input="hi"),
metadata={})

span_exporter.export(event)
span = span_exporter._outstanding_spans[event.payload.UUID]

assert span.attributes.get("nat.user.id") == "USER-42"
assert span.attributes.get("user.id") == "USER-42"
finally:
ctx_state.user_id.reset(token)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the user-attribute assertions independent of NAT_SPAN_PREFIX.

The span_exporter fixture creates ConcreteSpanExporter() without a fixed span_prefix. The exporter then uses NAT_SPAN_PREFIX when it is set, but both tests require the literal nat.user.id. A non-default environment value makes these tests fail.

Use the exporter’s configured prefix or create the exporter with span_prefix="nat".

Proposed assertion fix
-            assert span.attributes.get("nat.user.id") == "USER-42"
+            assert span.attributes.get(f"{span_exporter._span_prefix}.user.id") == "USER-42"
...
-            assert span.attributes.get("nat.user.id") == "unknown"
+            assert span.attributes.get(f"{span_exporter._span_prefix}.user.id") == "unknown"

Also applies to: 686-706

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/nvidia_nat_core/tests/nat/observability/exporter/test_span_exporter.py`
around lines 664 - 684, Update test_user_id_emitted_as_span_attribute and the
related test at lines 686-706 so assertions derive the attribute key from
span_exporter’s configured prefix rather than hard-coding "nat.user.id", or
configure the fixture exporter with span_prefix="nat". Keep the user.id
assertion unchanged.

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.

ContextState.user_id is never emitted as a span attribute, missing in Langfuse

1 participant