-
[OPIK-6770] [SDK] [DOCS] fix: link in-process OpenTelemetry spans to the active @opik.track trace (#7006)
发布于
2026-06-10 14:14:16 +00:00 - [OPIK-6770] [SDK] fix: auto-link in-process OTel spans to the active @opik.track trace
OpikSpanProcessor only propagated Opik IDs that were already present on a
parent OTel span (via attach_to_parent) or in W3C baggage. Spans emitted by
libraries that produce their own OTel spans (e.g. logfire/PydanticAI) inside an
@opik.track function had no such parent, so they landed in a separate synthetic
trace instead of nesting under the tracked span.Add an in-process fallback: when neither the parent span nor baggage carry Opik
IDs, the processor reads the active @opik.track context and stamps
opik.trace_id / opik.parent_span_id / opik.span_id on the subtree root.
Descendants then chain via the existing parent-attribute path. Distributed
flows (parent attrs, baggage) keep precedence, so cross-process tracing is
unchanged.Tests: processor unit coverage for the main flows (generic multi-tracer,
nested track, baggage precedence, worker-thread boundary, async), an
opentelemetry e2e for native+OTel composition, and a pydantic_ai+logfire e2e
reproducing the ticket. Wire the pydantic_ai suite into CI.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- docs(otel): document in-process @track + OTel auto-linking
OpikSpanProcessor now also links OTel spans to the active @opik.track context
in the same process. Update the OpenTelemetry Python SDK and distributed-traces
pages (the previous "only attached subtrees" wording was inaccurate) and add a
"Combining with @track" section to the PydanticAI integration pages (v1 + v2).Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- fix(otel): address code review — error-path semantics, docs, test hardening
- processor: on invalid (non-UUIDv7) baggage, leave the span standalone instead
of falling back to the local @track context — a broken upstream distributed
context should not be silently absorbed into an unrelated local trace - test: lock that behavior; assert the PydanticAI OTel root span is named
"agent run" (not parentage alone); move the bespoke multi-tracer provider to a
yield fixture so teardown runs on failure; drop redundant parens - docs: make the @track PydanticAI snippets runnable (sync + invoked), modernize
result.data -> result.output on both pages, add trailing newline
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- fix(otel): only fall back to @track context when no Opik signal exists
Address PR review: the in-process @opik.track fallback previously fired whenever
baggage had no opik.trace_id, even if a partial/broken Opik payload was present
(e.g. opik.span_id without opik.trace_id in the parent span or baggage). That
could silently reattach a span carrying a broken distributed context to an
unrelated local trace. The fallback now runs only when there is no Opik signal
at all; any partial payload leaves the span standalone (with a warning).Add unit coverage for the partial-baggage case, and an e2e test exercising two
sequential PydanticAI agent calls under one @track entrypoint — both 'agent run'
subtrees nest as siblings with their own model span and no cross-linking.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- test(otel): use verify_span helpers in the two-agent-call e2e assertions
Address PR review: replace the hand-rolled by_id/roots span-structure checks in
the two-agent-call test with tests.e2e.verifiers.verify_span for the entrypoint,
each 'agent run', and each model span. search_spans is kept only to discover the
backend-minted span ids and assert the 5-span completeness/one-child invariant.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- test(otel): e2e for cross-origin parent-child nesting (nested @track + OTel)
Add an integration test interleaving native @opik.track spans with logfire/
PydanticAI OTel spans at different depths: a nested inner_step under an outer
entrypoint, each making an agent call. Asserts each OTel 'agent run' attaches to
the innermost active native span (one under inner_step, one under outer), so
parent-child links are preserved across origins within one trace. Deterministic
(TestModel), polls to a fixed 6-span count — no sleeps.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- fix(sdk): stop global time.sleep patch hanging the unit suite; add pytest timeout
The Python 3.10 unit job hung for 20 min at the rate-limit retry tests. Root
cause: the search/rate-limit tests patched the GLOBAL time.sleep
(rest_helpers.time.sleep). A leaked QueueConsumer daemon thread paces its loop
with time.sleep, so a no-op global sleep turned it into a busy loop that
starved the interpreter under 3.10's GIL scheduling.Fix #1: route the rate-limit retry delay through rest_helpers._sleep and patch
that narrow indirection in the tests, leaving the global time.sleep (and
background-thread pacing) untouched.Fix #3: add pytest-timeout and run the unit job with
--timeout=120 --timeout-method=thread, so a future hang fails fast with all
thread stacks dumped instead of a silent 20-min cancel.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com
下载附件