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
3902d51
feat(pg): trace pool connection-acquire wait time (#8914)
BridgeAR Jul 30, 2026
752bcc2
docs(llmobs): correct both agent skills against the codebase (#9545)
BridgeAR Jul 30, 2026
348044d
chore(deps): bump the test-versions group across 1 directory with 2 u…
dependabot[bot] Jul 31, 2026
4ba0b99
chore(deps): bump the ai-and-llm group across 1 directory with 7 upda…
dependabot[bot] Jul 31, 2026
ae5bfe5
chore(deps): bump @types/node (#9615)
dependabot[bot] Jul 31, 2026
b38d310
chore(deps): bump the cloud-and-messaging group across 1 directory wi…
dependabot[bot] Jul 31, 2026
ed890d6
perf(loader): defer rewriter initialization (#9502)
BridgeAR Jul 31, 2026
e7dd605
fix(dns): preserve shorthand rrtype with options (#9624)
BridgeAR Jul 31, 2026
461c231
fix(express): preserve legacy middleware dispatch semantics (#9351)
BridgeAR Aug 1, 2026
f22f637
chore(deps): bump the test-versions group across 1 directory with 6 u…
dependabot[bot] Aug 3, 2026
eb3ac68
chore(deps): bump the testing-and-build group across 1 directory with…
dependabot[bot] Aug 3, 2026
28b8b21
feat(vitest): add zero-config test impact analysis (#9604)
juan-fernandez Aug 3, 2026
d9c66b9
chore(deps): bump mongoose from 8.23.1 to 8.24.1 in /benchmark/sirun/…
dependabot[bot] Aug 3, 2026
0f0fab6
chore(deps): bump the test-versions group across 1 directory with 3 u…
dependabot[bot] Aug 3, 2026
d7e6923
refactor(lint): simplify regexes and enable the actionlint shellcheck…
BridgeAR Aug 3, 2026
5445ae8
test(playwright): stabilize flaky integration tests (#9650)
juan-fernandez Aug 3, 2026
b0d7909
feat(test-optimization): report managed tests in CI logs (#9605)
juan-fernandez Aug 3, 2026
2466392
fix(test-optimization): reject symlinked coverage reports (#9335)
BridgeAR Aug 3, 2026
1e2ed12
test(test-optimization): split Vitest TIA tests (#9652)
juan-fernandez Aug 3, 2026
352bad4
fix(test-optimization): preserve managed report details (#9653)
juan-fernandez Aug 3, 2026
3b2f388
chore(eslint): enable rules that are clean or trivially fixable (#9553)
BridgeAR Aug 3, 2026
7596bb3
feat(test-optimization): improve validation runbook reliability (#9584)
juan-fernandez Aug 3, 2026
7e2a872
fix(mysql): preserve query callback arguments (#9630)
BridgeAR Aug 3, 2026
0abde56
fix(loader): install the import-in-the-middle matcher under --loader …
BridgeAR Aug 3, 2026
ad3bc7f
test(agent): reset the propagation-hash config on close (#9562)
BridgeAR Aug 3, 2026
f568c83
test(coverage): group equivalent V8 profiles before conversion (#9655)
BridgeAR Aug 3, 2026
0b04786
feat(types): expose plugin name and options types (#9637)
BridgeAR Aug 3, 2026
9667b77
chore(runtime-metrics): support Node 24 iteration metrics (#9662)
pabloerhard Aug 3, 2026
7ee1c9e
chore(deps): bump the npm_and_yarn group across 1 directory with 2 up…
dependabot[bot] Aug 3, 2026
bdd8ba5
chore(deps): bump undici (#9663)
dependabot[bot] Aug 3, 2026
59122c2
chore(deps): bump openai (#9664)
dependabot[bot] Aug 4, 2026
08a9b9f
feat(test-optimization): support WebdriverIO Jasmine reporting (#9607)
juan-fernandez Aug 4, 2026
fb06935
fix(test-optimization): align EFD retries with duration policies (#9382)
BridgeAR Aug 4, 2026
994a596
fix(test-optimization): handle bounded validation edge cases (#9658)
juan-fernandez Aug 4, 2026
532b494
fix(test-optimization): ignore line coverage for Vitest TIA (#9672)
juan-fernandez Aug 4, 2026
fba0f76
fix(next): trace compiled route runtimes (#9627)
wconti27 Aug 4, 2026
a83dfb2
fix(test-optimization): explain when TIA skips all tests (#9671)
juan-fernandez Aug 4, 2026
74cdb8c
chore(deps): bump pnpm (#9666)
dependabot[bot] Aug 4, 2026
dd23f84
chore(eslint): update Unicorn and enable iteration fallback style (#9…
BridgeAR Aug 4, 2026
31bc229
chore(eslint): enable JSDoc parameter and type checks (#9566)
BridgeAR Aug 4, 2026
2c8a4ee
chore(deps): bump the gh-actions-packages group across 2 directories …
dependabot[bot] Aug 5, 2026
664fdc0
fix(test-optimization): allow null skippable coverage metadata (#9677)
juan-fernandez Aug 5, 2026
9bc08a2
fix(playwright): avoid requesting page fixture in cleanup hook (#9681)
juan-fernandez Aug 5, 2026
e5f8847
chore(deps): bump pprof-format from 2.2.3 to 2.3.0 in /vendor in the …
dependabot[bot] Aug 5, 2026
0f19632
fix(test-optimization): preserve Jest parameters across retries (#9693)
juan-fernandez Aug 5, 2026
3822631
fix(test-optimization): disable telemetry in test workers (#9687)
juan-fernandez Aug 5, 2026
e14f658
test(ai): add preserve OpenTelemetry span receiver test (#9309)
crysmags Aug 5, 2026
644bada
fix(test-optimization): support older Jest and Mocha versions (#9695)
juan-fernandez Aug 5, 2026
d32009d
fix(next): preserve http endpoint for parent spans (#9682)
wconti27 Aug 5, 2026
20728c8
test(test-optimization): respect v5 Cypress support range (#9697)
juan-fernandez Aug 5, 2026
4f938c0
refactor(appsec): slim down appsec index file (#9670)
IlyasShabi Aug 5, 2026
e78e5e3
feat(llmobs): add submit feedback method for submitting end-user feed…
ddog-thibault-nadin Aug 5, 2026
f5097b9
fix(test-optimization): support Jest 24 retry registration (#9699)
juan-fernandez Aug 5, 2026
8f0d2f1
v5.120.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