Skip to content

fix(tracing): preserve subagent trace context - #904

Open
linhongyu510 wants to merge 4 commits into
volcengine:mainfrom
linhongyu510:fix/preserve-subagent-trace-context
Open

linhongyu510 wants to merge 4 commits into
volcengine:mainfrom
linhongyu510:fix/preserve-subagent-trace-context

Conversation

@linhongyu510

@linhongyu510 linhongyu510 commented Aug 15, 2026 •

Copy link
Copy Markdown

Summary

Fixes #549.

The in-memory span processor attached custom invocation and agent values to the OpenTelemetry context, but did not set the started span as the current span. A nested agent could therefore start without the parent agent span and receive an independent trace ID.

This change:

  • places invocation and agent spans into the attached OTel context with set_span_in_context
  • stores context tokens by span ID because on_end receives a ReadableSpan
  • detaches the matching token when each span ends, restoring the parent context
  • cleans up tokens for RECORD_ONLY spans even though they are not exported
  • keeps cleanup in finally so exporter failures cannot leak context

Validation

tests/tracing/test_inmemory_exporter.py now covers:

  • invocation → parent agent → child agent trace inheritance
  • concurrent asyncio task isolation
  • exceptional agent termination
  • nested non-agent spans
  • ReadableSpan cleanup in on_end
  • unsampled RECORD_ONLY cleanup

Results:

  • target tests — 6 passed
  • Ruff check and format check
  • git diff --check

AI assistance

This change was developed with AI assistance. I reviewed the ContextVar lifecycle, verified the patch with real OpenTelemetry spans, and ran the checks above.

@linhongyu510
linhongyu510 force-pushed the fix/preserve-subagent-trace-context branch from 01286b6 to 73b9eec Compare August 18, 2026 02:22
@linhongyu510

Copy link
Copy Markdown
Author

Recovery check on current main (43f958d8): patch replays cleanly; tracing regression 6 passed, Ruff/format/diff-check pass. Happy to update the branch if preferred.

Reconcile volcengine#904 with upstream main's bounded deque retention refactor:
keep main's evolved _InMemoryExporter/_InMemoryExporter.__init__ and
for_provider, while retaining the subagent trace-context fix
(set_span_in_context + ContextVar token cleanup). Update the RECORD_ONLY
assertion to be type-agnostic to the deque retention. Tests: 6 passed.
Resolve the inmemory_exporter.py import conflict by keeping the
set_span_in_context import required by the subagent trace-context fix.
Upstream had already adopted the bounded span-cache refactor, so only the
context-token lifecycle change from this branch is retained on top of main.

Local verification: tests/tracing/test_inmemory_exporter.py (6) and
tests/test_inmemory_exporter.py (12) green; ruff 0.11.12 check/format clean.
@linhongyu510

Copy link
Copy Markdown
Author

Merge & local verification

Merged the latest main (86c93529) into this branch in an independent worktree (regular merge, no rebase / force / --no-verify).

  • Conflict: only veadk/tracing/telemetry/exporters/inmemory_exporter.py (import block). Resolution kept this branch's from opentelemetry.trace import set_span_in_context, which the on_start context fix relies on; main had already adopted the bounded span-cache refactor, so the retained delta here is the context-token lifecycle change (_context_tokens keyed by span_id, try/finally detach on sampled/unsampled/raising spans).
  • Target tests: tests/tracing/test_inmemory_exporter.py — 6 passed.
  • Related regression: tests/test_inmemory_exporter.py — 12 passed.
  • Lint/format: ruff==0.11.12 check + ruff format --check clean on the touched files; module imports/compiles.
  • Hermetic: in-memory OpenTelemetry only — no network, credentials, GPU, or external service.

No CI checks are reported on this fork head; the above was verified locally against the pinned dependency set.

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

Projects

None yet

1 participant