feat: Add span origin provenance#2226
Open
Stephen Belanger (Qard) wants to merge 1 commit into
Open
Conversation
Stephen Belanger (Qard)
force-pushed
the
agent/span-origin-provenance
branch
2 times, most recently
from
July 15, 2026 16:43
d25d857 to
efa7db3
Compare
Stephen Belanger (Qard)
marked this pull request as ready for review
July 15, 2026 17:11
Stephen Belanger (Qard)
requested review from
Abhijeet Prasad (AbhiPrasad) and
Luca Forstner (lforst)
July 15, 2026 17:11
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efa7db3e0a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
Stephen Belanger (Qard)
force-pushed
the
agent/span-origin-provenance
branch
2 times, most recently
from
July 16, 2026 11:44
fb49185 to
f3358a5
Compare
Stephen Belanger (Qard)
force-pushed
the
agent/span-origin-provenance
branch
from
July 16, 2026 12:55
f3358a5 to
8ccc89e
Compare
Abhijeet Prasad (AbhiPrasad)
pushed a commit
to braintrustdata/braintrust-sdk-python
that referenced
this pull request
Jul 16, 2026
## Summary Every span an integration creates now carries a stable identifier in `context.span_origin.instrumentation.name` — `openai-auto`, `anthropic-auto`, `temporal-auto`, and so on — matching the naming convention in the Braintrust instrumentation spec's `span_origin` example. Previously all integration spans were tagged with the shared `braintrust-python-logger` default, which made per-integration filtering on `span_origin` impossible. Builds on #570 (which added the `span_origin` scaffolding). ## API surface `start_span` (and every provider-level `start_span` method) gains an `internal: dict | None = None` kwarg reserved for Braintrust SDK internals. Integrations pass `internal={"instrumentation": "<provider>-auto"}` to stamp the span origin; the `internal` name signals to external callers that they shouldn't touch it and its keys can change without notice. ```python from braintrust import start_span with start_span(name="my-op", internal={"instrumentation": "my-integration-auto"}) as span: ... ``` - Threaded through every span-creation surface: top-level `start_span`, `Span.start_span`, `SpanImpl.start_span`, `Experiment.start_span`, `Dataset.start_span`, `Logger.start_span`, and `_NoopSpan.start_span`. - When set, `SpanImpl` reads `internal["instrumentation"]` and stamps it into `context.span_origin.instrumentation.name` via `merge_span_origin_context`. - When unset, spans fall back to the channel default — `braintrust-python-logger` for direct logging, `braintrust-python-otel` for the OTel processor. - **Does NOT propagate through the parent/child edge.** Each `start_span` call independently decides its own value, so a user's `@traced` scorer nested inside a wrapped provider call keeps the default identifier — the integration's name only lands on spans the integration itself opens. This matches the spec's "the module that _directly created_ the span" definition and mirrors how Sentry's `origin` and OTel's `InstrumentationScope` behave. Suggested for external Braintrust integrations authored outside this repo: ```python from braintrust.logger import start_span as _bt_start_span _INSTRUMENTATION = "my-provider-auto" def start_span(*args, **kwargs): internal = dict(kwargs.get("internal") or {}) internal.setdefault("instrumentation", _INSTRUMENTATION) kwargs["internal"] = internal return _bt_start_span(*args, **kwargs) ``` Every module-scope `start_span(...)` call then flows through the shadow with zero further edits. ## Integration migration (23 integrations) **21 shadow-based** (`adk`, `agentscope`, `agno`, `anthropic`, `autogen`, `bedrock_runtime`, `claude_agent_sdk`, `cohere`, `crewai`, `dspy`, `google_genai`, `huggingface_hub`, `instructor`, `langchain`, `litellm`, `livekit_agents`, `llamaindex`, `mistral`, `openai`, `openai_agents`, `openrouter`, `pydantic_ai`) — the shadow above at the top of each `tracing.py`/`callbacks.py`. **Explicit at call sites** (2 integrations): - `temporal/plugin.py`: `internal={"instrumentation": "temporal-auto"}` at each `logger.start_span(...)` site — Logger method calls don't go through the module-level shadow. - `openai_agents/tracing.py`: `internal={"instrumentation": _INSTRUMENTATION}` on the three `current_context.start_span(...)` / `self._logger.start_span(...)` / `parent.start_span(...)` sites. ## Tests - `py/src/braintrust/test_otel.py` — new tests covering `merge_span_origin_context` resolution and an end-to-end `SpanImpl` assertion that child spans do NOT inherit the parent's instrumentation name (child falls back to `braintrust-python-logger`). - Per-integration inline assertion: each of the 23 integrations' existing VCR tests now asserts `span["context"]["span_origin"]["instrumentation"]["name"] == "<provider>-auto"` in place, next to the real provider-facing coverage. If a shadow ever regresses, that specific integration's test suite fails with a clear signal. ## Docs - `.agents/skills/sdk-integrations/SKILL.md` gains a **Span origin** section documenting the shadow pattern, the no-inheritance rule, and the `<provider>-auto` naming convention. - Same file relaxes the earlier strict "metadata MUST NOT capture anything outside the spec" guidance to an allowlist-per-provider model — spec-defined keys must be captured, provider-specific detail fields can be included deliberately, no dumping raw request/response objects. - Spec reference now links to the actual URL (https://github.com/braintrustdata/braintrust-spec/blob/main/docs/instrumentation-guide.md). ## Validation - `nox -s test_core`: **647 passed, 66 skipped, 12 xfailed** - `nox -s test_types`: **16 passed** (pyright + mypy clean) - `nox -s pylint`: successful (also ran ruff format + ruff check --fix as part of pre-commit) - **30 provider `(latest)` sessions** — every one whose Python 3.14 wheels are available. Zero failures. `test_livekit_agents` and `test_crewai` skipped due to pre-existing Python 3.14 wheel gaps in `onnxruntime` / `pydantic-core`, unrelated to this change. ## Follow-ups - JS SDK's mirror PR (braintrustdata/braintrust-sdk-javascript#2226) is still open with only `braintrust-js-logger` / `braintrust-otel-js` hardcoded and no per-integration wiring. Aligning JS on the same `<provider>-auto` names is worth doing before this ships so cross-SDK dashboards work — the spec only gives `openai-auto` as an illustrative example, so a coordinated commit-to-a-convention is needed on both sides. - Naming convention isn't currently prescribed by the spec (`docs/instrumentation-guide.md` just shows `openai-auto` in a JSON blob). Worth a spec PR to make it normative once JS and Python agree. <!-- sfk:created-approved-by --> Created by abhijeet <!-- sfk:slack-thread --> [Slack thread](https://starfolkai.slack.com/archives/C0AQDETAVT3/p1784153314321549?thread_ts=1784153314.321549&cid=C0AQDETAVT3) --------- Co-authored-by: Starfolk <noreply@starfolk.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds span origin provenance to the TypeScript SDK direct logging and OpenTelemetry paths, including .env.braintrust environment detection.
Validation