Skip to content

feat(tracing): propagate context to LLM providers#108

Merged
dialupdisaster merged 4 commits into
mainfrom
feat/llm-trace-context-propagation
Jul 23, 2026
Merged

feat(tracing): propagate context to LLM providers#108
dialupdisaster merged 4 commits into
mainfrom
feat/llm-trace-context-propagation

Conversation

@dialupdisaster

Copy link
Copy Markdown
Contributor

Summary

  • inject W3C traceparent and tracestate headers into outbound LLM requests for explicitly configured opencode provider IDs
  • correlate each chat.headers invocation with the live opencode.llm span using session, parent message, agent, model, and provider metadata
  • add OPENCODE_TRACE_PROPAGATION_PROVIDERS and the matching tracePropagationProviders plugin option, with explicit * wildcard support
  • keep arbitrary headers and W3C baggage out of scope

Why

Self-hosted gateways such as LiteLLM and vLLM can continue an incoming W3C trace, but the plugin's LLM spans and gateway spans are currently disconnected. Propagating the current opencode.llm span context produces one distributed trace without introducing a general-purpose outbound-header mechanism.

Propagation is disabled by default. Provider IDs act as the explicit trust boundary:

export OPENCODE_TRACE_PROPAGATION_PROVIDERS="company-litellm,vllm"

Static provider-specific headers remain configurable through opencode's native provider.options.headers setting.

Safety

  • skips providers that are not explicitly configured
  • requires matching session, parent message, agent, model, and provider metadata
  • avoids associating concurrent title-generation requests with the primary LLM span
  • conditionally removes request metadata so late completions cannot clear a newer request
  • clears pending propagation metadata when sessions end

Testing

  • bun run typecheck
  • bun test (323 pass)
  • bun run lint
  • bun run check:jsdoc-coverage (85.25%)

Supersedes #101.
Related to #100.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
  • [P2] src/handlers/message.ts:471setBoundedMap only bounds map keys, not the per-key array. Repeated incomplete assistant messages sharing one parent can grow llmRequestContexts[requestKey] indefinitely, bypassing MAX_PENDING. Bound the array or store each request as its own bounded map entry, and add a capacity test.

Unable to run tests because bun is unavailable in the environment.

@dialupdisaster

Copy link
Copy Markdown
Contributor Author

Addressed in d755fe3. Correlation now retains multiple live LLM contexts for the same session/parent message and selects the matching context by agent, model, and provider. Completion removes only its own message context. Added coverage that invokes chat.headers for two concurrently live requests and verifies each receives its own traceparent.

All local checks pass: typecheck, lint, JSDoc coverage, and 324 tests.

@dialupdisaster dialupdisaster self-assigned this Jul 23, 2026
@dialupdisaster

Copy link
Copy Markdown
Contributor Author

Also folded in 0af2755 to fix the unrelated Discord Events startup failure from run 30041475563. The caller grants permissions: {}, but the reusable workflow attempted to elevate the token to repository read scopes, which GitHub rejects before creating jobs. The called workflow now also uses permissions: {}; it only reads the event context and posts directly to Discord, so no GitHub API permissions are needed.

Validated with the repository pre-commit workflow checks and bun run typecheck. The old run cannot validate the new commit because this workflow only triggers on pull_request.opened; the next issue, PR, or release event will exercise the corrected permission chain.

@dialupdisaster

Copy link
Copy Markdown
Contributor Author

Addressed the updated Codex finding from run 30048048618 in ba7a3af. When stale and current live contexts have identical session/parent/agent/model/provider metadata, propagation now selects the most recently started matching span instead of the oldest.

chat.headers does not expose the assistant message ID, but opencode starts the current assistant span immediately before preparing its request and serializes LLM steps within a session; retries reuse the same span. Added a same-metadata stale/current test that verifies the newer span ID is injected.

All checks pass, including 325 tests.

@dialupdisaster
dialupdisaster merged commit 8827e33 into main Jul 23, 2026
7 checks passed
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.

[Feature]: Inject custom headers into outgoing LLM requests (OPENCODE_OUTBOUND_HEADERS)

1 participant