Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f41c34a
feat(pg): trace pool connection-acquire wait time (#8914)
BridgeAR Jul 30, 2026
d34202a
docs(llmobs): correct both agent skills against the codebase (#9545)
BridgeAR Jul 30, 2026
e659cbb
chore(deps): bump the test-versions group across 1 directory with 2 u…
dependabot[bot] Jul 31, 2026
cc42178
chore(deps): bump the ai-and-llm group across 1 directory with 7 upda…
dependabot[bot] Jul 31, 2026
a628e54
chore(deps): bump @types/node (#9615)
dependabot[bot] Jul 31, 2026
ebef038
chore(deps): bump the cloud-and-messaging group across 1 directory wi…
dependabot[bot] Jul 31, 2026
7c4e309
perf(loader): defer rewriter initialization (#9502)
BridgeAR Jul 31, 2026
4154867
fix(dns): preserve shorthand rrtype with options (#9624)
BridgeAR Jul 31, 2026
a71f347
fix(express): preserve legacy middleware dispatch semantics (#9351)
BridgeAR Aug 1, 2026
b38779a
chore(deps): bump the test-versions group across 1 directory with 6 u…
dependabot[bot] Aug 3, 2026
bf085aa
chore(deps): bump the testing-and-build group across 1 directory with…
dependabot[bot] Aug 3, 2026
4bba782
feat(vitest): add zero-config test impact analysis (#9604)
juan-fernandez Aug 3, 2026
fd7a836
chore(deps): bump mongoose from 8.23.1 to 8.24.1 in /benchmark/sirun/…
dependabot[bot] Aug 3, 2026
e12df12
chore(deps): bump the test-versions group across 1 directory with 3 u…
dependabot[bot] Aug 3, 2026
5ad222a
refactor(lint): simplify regexes and enable the actionlint shellcheck…
BridgeAR Aug 3, 2026
c48aff3
test(playwright): stabilize flaky integration tests (#9650)
juan-fernandez Aug 3, 2026
dd9658f
feat(test-optimization): report managed tests in CI logs (#9605)
juan-fernandez Aug 3, 2026
a49b305
fix(test-optimization): reject symlinked coverage reports (#9335)
BridgeAR Aug 3, 2026
d70cb1c
test(test-optimization): split Vitest TIA tests (#9652)
juan-fernandez Aug 3, 2026
dc8e0b8
fix(test-optimization): preserve managed report details (#9653)
juan-fernandez Aug 3, 2026
ff59639
chore(eslint): enable rules that are clean or trivially fixable (#9553)
BridgeAR Aug 3, 2026
110cb89
feat(test-optimization): improve validation runbook reliability (#9584)
juan-fernandez Aug 3, 2026
5ddffc1
fix(mysql): preserve query callback arguments (#9630)
BridgeAR Aug 3, 2026
548da2c
fix(loader): install the import-in-the-middle matcher under --loader …
BridgeAR Aug 3, 2026
fe34d11
test(agent): reset the propagation-hash config on close (#9562)
BridgeAR Aug 3, 2026
72c99bb
test(coverage): group equivalent V8 profiles before conversion (#9655)
BridgeAR Aug 3, 2026
6af89c9
feat(types): expose plugin name and options types (#9637)
BridgeAR Aug 3, 2026
1e12b17
chore(runtime-metrics): support Node 24 iteration metrics (#9662)
pabloerhard Aug 3, 2026
81c832b
chore(deps): bump the npm_and_yarn group across 1 directory with 2 up…
dependabot[bot] Aug 3, 2026
ccb28ac
chore(deps): bump undici (#9663)
dependabot[bot] Aug 3, 2026
d1ad91c
chore(deps): bump openai (#9664)
dependabot[bot] Aug 4, 2026
ab1fc1b
feat(test-optimization): support WebdriverIO Jasmine reporting (#9607)
juan-fernandez Aug 4, 2026
913dee9
fix(test-optimization): align EFD retries with duration policies (#9382)
BridgeAR Aug 4, 2026
73c9444
fix(test-optimization): handle bounded validation edge cases (#9658)
juan-fernandez Aug 4, 2026
213b114
fix(test-optimization): ignore line coverage for Vitest TIA (#9672)
juan-fernandez Aug 4, 2026
d1f1b82
fix(next): trace compiled route runtimes (#9627)
wconti27 Aug 4, 2026
fcd0742
fix(test-optimization): explain when TIA skips all tests (#9671)
juan-fernandez Aug 4, 2026
e0224f0
chore(deps): bump pnpm (#9666)
dependabot[bot] Aug 4, 2026
28a0b8b
chore(eslint): update Unicorn and enable iteration fallback style (#9…
BridgeAR Aug 4, 2026
9711d48
chore(eslint): enable JSDoc parameter and type checks (#9566)
BridgeAR Aug 4, 2026
0f38ef6
chore(deps): bump the gh-actions-packages group across 2 directories …
dependabot[bot] Aug 5, 2026
ea51412
fix(test-optimization): allow null skippable coverage metadata (#9677)
juan-fernandez Aug 5, 2026
0e54a9c
fix(playwright): avoid requesting page fixture in cleanup hook (#9681)
juan-fernandez Aug 5, 2026
2593ea9
chore(deps): bump pprof-format from 2.2.3 to 2.3.0 in /vendor in the …
dependabot[bot] Aug 5, 2026
6b6da6d
fix(test-optimization): preserve Jest parameters across retries (#9693)
juan-fernandez Aug 5, 2026
e47226b
fix(test-optimization): disable telemetry in test workers (#9687)
juan-fernandez Aug 5, 2026
4ccdc93
test(ai): add preserve OpenTelemetry span receiver test (#9309)
crysmags Aug 5, 2026
4b403e5
fix(test-optimization): support older Jest and Mocha versions (#9695)
juan-fernandez Aug 5, 2026
731497f
fix(next): preserve http endpoint for parent spans (#9682)
wconti27 Aug 5, 2026
dac398b
test(test-optimization): respect v5 Cypress support range (#9697)
juan-fernandez Aug 5, 2026
ed9d91f
refactor(appsec): slim down appsec index file (#9670)
IlyasShabi Aug 5, 2026
9213051
feat(llmobs): add submit feedback method for submitting end-user feed…
ddog-thibault-nadin Aug 5, 2026
8e14c5b
fix(test-optimization): support Jest 24 retry registration (#9699)
juan-fernandez Aug 5, 2026
e7443a4
v6.9.0
juan-fernandez Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
159 changes: 52 additions & 107 deletions .agents/skills/llmobs-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,97 +5,77 @@ description: |
in dd-trace-js. Triggers: "add LLMObs support", "instrument chat
completions / streaming / embeddings / agent runs / orchestration / tool
calls / retrieval", "LLMObsPlugin", "getLLMObsSpanRegisterOptions",
"setLLMObsTags", "LlmObsCategory", "LlmObsSpanKind", any provider tag
"setLLMObsTags", "SPAN_KINDS", "span kind", any provider tag
("openai" / "anthropic" / "genai" / "google" / "langchain" / "langgraph" /
"ai-sdk" llmobs), "VCR cassettes".
"ai" llmobs), "VCR cassettes".
---

# LLM Observability Integration Skill

This skill covers creating LLMObs plugins that instrument LLM library operations and emit span events. Supported operations: chat completions (streaming and non-streaming), embeddings, agent runs, orchestration (workflows / graphs), tool calls, retrieval (RAG / vector DB).
This skill covers creating LLMObs plugins that instrument LLM library operations and emit span events. Supported
operations: chat completions (streaming and non-streaming), embeddings, agent runs, orchestration (workflows /
graphs), tool calls, retrieval (RAG / vector DB).

## Read Upstream Source First

LLM libraries iterate fast — six-month-old assumptions about an SDK's response shape, streaming contract, or tool-call format are usually wrong. Before category detection or any plugin work, read the upstream library's source for the installed version (`versions/<lib>@<range>/node_modules/<lib>`). The category decision tree below depends on facts the source carries (does this package make HTTP calls? does it orchestrate? does it support multiple providers?). See [apm-integrations § Read Upstream Source First](../apm-integrations/SKILL.md#read-upstream-source-first) for the shallow-clone / `npm pack` shapes.
LLM libraries iterate fast — six-month-old assumptions about an SDK's response shape, streaming contract, or tool-call
format are usually wrong. Before category detection or any plugin work, read the upstream library's source for the
installed version (`versions/<lib>@<range>/node_modules/<lib>`). The shape checklist below depends on facts the
source carries (does this package make HTTP calls? does it orchestrate? does it support multiple providers?). See
[apm-integrations § Read Upstream Source First](../apm-integrations/SKILL.md#read-upstream-source-first) for the
shallow-clone / `npm pack` shapes.

## Core Concepts

### 1. LLMObsPlugin Base Class

All LLMObs plugins extend `LLMObsPlugin`. Two methods must be implemented:
Leaf plugins extend `LLMObsPlugin` and implement two methods:

- `getLLMObsSpanRegisterOptions(ctx)` — returns `{ modelProvider, modelName, kind, name }`.
- `setLLMObsTags(ctx)` — extracts and tags input / output messages, token metrics, and model metadata.
- `getLLMObsSpanRegisterOptions(ctx)` — returns a required `kind` plus any available name, model and session fields.
- `setLLMObsTags(ctx)` — tags the operation's input, output, metrics, and metadata.

Lifecycle: `start(ctx)` registers the span and captures context; the wrapped operation runs; `asyncEnd(ctx)` calls `setLLMObsTags()`; `end(ctx)` restores the parent.
A composite root such as `ai/index.js` extends `CompositePlugin` and selects leaf plugins.

See [references/plugin-architecture.md](references/plugin-architecture.md) for the full implementation surface.

### 2. Package Category System

**CRITICAL:** Every integration must be classified into one category using the `LlmObsCategory` enum. This determines test strategy and implementation approach.

#### LlmObsCategory Enum Values

- **`LlmObsCategory.LLM_CLIENT`** - Direct API wrappers (openai, anthropic, genai)
- Signs: Makes HTTP calls to LLM provider endpoints, requires API keys
- Test strategy: VCR with real API calls via proxy
- Instrumentation: Hook chat/completion methods

- **`LlmObsCategory.MULTI_PROVIDER`** - Multi-provider frameworks (ai-sdk, langchain)
- Signs: Supports multiple LLM providers via configuration, wraps LLM_CLIENT libraries
- Test strategy: VCR with real API calls via proxy
- Instrumentation: Hook provider abstraction layer
On the usual promise-backed channel, `start(ctx)` registers the span and captures context, `end(ctx)` restores the
parent after the wrapped call returns, and `asyncEnd(ctx)` calls `setLLMObsTags()` after the operation settles.

- **`LlmObsCategory.ORCHESTRATION`** - Workflow managers (langgraph)
- Signs: Graph/workflow execution, state management, NO direct HTTP to LLM providers
- Test strategy: Pure function tests, NO VCR, NO real API calls
- Instrumentation: Hook workflow lifecycle (invoke, stream, run)
- **Special:** Tests should use actual LLM as orchestration node (not mock responses)

- **`LlmObsCategory.INFRASTRUCTURE`** - Protocols/servers (MCP)
- Signs: Protocol implementation, server/client architecture, transport layers
- Test strategy: Mock server tests
- Instrumentation: Hook protocol handlers

#### Decision Tree
See [references/plugin-architecture.md](references/plugin-architecture.md) for the full implementation surface.

Answer these questions by reading the code:
### 2. Package Shape

1. **Does the package make direct HTTP calls to LLM provider endpoints?**
- YES → Go to question 2
- NO → Go to question 3
**Settle each instrumented surface's shape before writing anything** — it decides which methods to hook and how the
operation gets its response. These are working categories for reasoning, not constants in the codebase, so classify
by reading the source rather than looking for an enum.

2. **Does it support multiple LLM providers via configuration?**
- YES → **`LlmObsCategory.MULTI_PROVIDER`**
- NO → **`LlmObsCategory.LLM_CLIENT`**
- **LLM client** — owns the provider endpoint, transport and authentication (openai, anthropic, genai). Hook the
chat / completion methods.
- **Multi-provider** — accepts provider implementations behind one surface (ai, langchain). The providers may live
in separate packages. Hook the provider abstraction layer.
- **Orchestration** — runs a graph or workflow and holds state, with no provider HTTP of its own (langgraph). Hook the
workflow lifecycle (invoke, stream, run).
- **Infrastructure** — implements a protocol across a client / server split (modelcontextprotocol-sdk). Hook the
protocol handlers.

3. **Does it implement workflow/graph orchestration with state management?**
- YES → **`LlmObsCategory.ORCHESTRATION`**
- NO → **`LlmObsCategory.INFRASTRUCTURE`**
The shape decides the response source and test harness. The instrumented operation decides its span kind and fields.
Hybrid packages such as `ai` and LangChain must be classified per operation. Test strategy per shape lives in
[llmobs-testing](../llmobs-testing/SKILL.md).

See [references/category-detection.md](references/category-detection.md) for detailed heuristics and examples.
See [references/category-detection.md](references/category-detection.md) for heuristics and worked examples.

### 3. LLM Span Kinds

Use the `LlmObsSpanKind` enum:

- **`LlmObsSpanKind.LLM`** - Chat completions, text generation
- **`LlmObsSpanKind.WORKFLOW`** - Graph/chain execution
- **`LlmObsSpanKind.AGENT`** - Agent runs
- **`LlmObsSpanKind.TOOL`** - Tool/function calls
- **`LlmObsSpanKind.EMBEDDING`** - Embedding generation
- **`LlmObsSpanKind.RETRIEVAL`** - Vector DB/RAG retrieval

**Most common:** Use `'llm'` for chat completions/text generation in LLM_CLIENT and MULTI_PROVIDER categories.
`SPAN_KINDS` in `packages/dd-trace/src/llmobs/constants/tags.js` lists `llm`, `agent`, `workflow`, `task`, `tool`,
`embedding`, `retrieval`. Chat completions and text generation are `llm`; graph or chain execution is `workflow`;
agent runs are `agent`; vector-DB and RAG lookups are `retrieval`. Only the public SDK validates against that list,
so a plugin may register a kind outside it — `ai` v7 and claude-agent-sdk both use `step`.

### 4. Message Extraction

All plugins must convert provider-specific message formats to the standard format:
`llm` operations convert provider-specific messages to the tagger's message shape:

**Standard format:** `[{content: string, role: string}]`
**Common shape:** `[{ content?: string, role: string, toolCalls?: object[], toolResults?: object[] }]`

**Common roles:** `'user'`, `'assistant'`, `'system'`, `'tool'`
`role` defaults to an empty string. Tool-call or tool-result-only messages may omit `content`.

**Provider-specific handling:**
- OpenAI: Direct format match, handle `function_call` and `tool_calls`
Expand All @@ -107,49 +87,14 @@ See [references/message-extraction.md](references/message-extraction.md) for pro

## Implementation Steps

1. **Detect package category** (REQUIRED FIRST STEP)
- Follow decision tree above
- Output: category, confidence, reasoning

2. **Create plugin file**
- Location: `packages/dd-trace/src/llmobs/plugins/{integration}/index.js`
- Extend: `LLMObsPlugin` base class
- Implement: Required methods per plugin architecture

3. **Implement `getLLMObsSpanRegisterOptions(ctx)`**
- Extract model provider and name from context
- Determine span kind (usually `'llm'`)
- Return registration options object

4. **Implement `setLLMObsTags(ctx)`**
- Extract input messages from `ctx.arguments`
- Extract output messages from `ctx.result`
- Extract token metrics (input_tokens, output_tokens, total_tokens)
- Extract metadata (temperature, max_tokens, etc.)
- Tag span using `this._tagger` methods

5. **Handle edge cases**
- Streaming responses (if applicable)
- Error cases (empty output messages)
- Non-standard message formats
- Missing metadata

See [references/plugin-architecture.md](references/plugin-architecture.md) for step-by-step implementation guide.

## Plugin Registration

All plugins must export an array:

**Static properties required:**
- `integration` - Integration name (e.g., 'openai')
- `id` - Unique plugin ID (e.g., 'llmobs_openai')
- `prefix` - Channel prefix (e.g., 'tracing:apm:openai:chat')

## References

For detailed information, see:

- [references/plugin-architecture.md](references/plugin-architecture.md) - Complete plugin structure, implementation steps, helper methods
- [references/category-detection.md](references/category-detection.md) - Package classification heuristics and detection process
- [references/message-extraction.md](references/message-extraction.md) - Provider-specific message format patterns
- [references/reference-implementations.md](references/reference-implementations.md) - Working plugin examples (Anthropic, Google GenAI)
1. **Map each surface's response source and operation kind**, from the upstream source rather than the package name.
2. **Create leaf plugins under `packages/dd-trace/src/llmobs/plugins/{integration}/`** extending `LLMObsPlugin`.
3. **Implement `getLLMObsSpanRegisterOptions(ctx)`** — span kind plus any available name, model and session fields.
4. **Implement `setLLMObsTags(ctx)`** — input, output, metrics and metadata from the fields the instrumentation
publishes on `ctx`, tagged through `this._tagger`.
5. **Cover the edges**: streaming, kind-specific error output, non-standard formats, absent metadata.

Export the class itself when the package needs one plugin (openai, anthropic, genai), or an array when several
operations each need their own (langchain, langgraph, modelcontextprotocol-sdk, claude-agent-sdk). Use a
`CompositePlugin` root when one integration selects between child implementations, as `ai` does. The required static
fields and the rest of the surface are in [references/plugin-architecture.md](references/plugin-architecture.md).
Loading
Loading