Skip to content

Commit ba79c1a

Browse files
authored
Merge branch 'master' into otel-thread-context-writer
2 parents 319a093 + 6267703 commit ba79c1a

896 files changed

Lines changed: 67083 additions & 24056 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/llmobs-integration/SKILL.md

Lines changed: 52 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -5,97 +5,77 @@ description: |
55
in dd-trace-js. Triggers: "add LLMObs support", "instrument chat
66
completions / streaming / embeddings / agent runs / orchestration / tool
77
calls / retrieval", "LLMObsPlugin", "getLLMObsSpanRegisterOptions",
8-
"setLLMObsTags", "LlmObsCategory", "LlmObsSpanKind", any provider tag
8+
"setLLMObsTags", "SPAN_KINDS", "span kind", any provider tag
99
("openai" / "anthropic" / "genai" / "google" / "langchain" / "langgraph" /
10-
"ai-sdk" llmobs), "VCR cassettes".
10+
"ai" llmobs), "VCR cassettes".
1111
---
1212

1313
# LLM Observability Integration Skill
1414

15-
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).
15+
This skill covers creating LLMObs plugins that instrument LLM library operations and emit span events. Supported
16+
operations: chat completions (streaming and non-streaming), embeddings, agent runs, orchestration (workflows /
17+
graphs), tool calls, retrieval (RAG / vector DB).
1618

1719
## Read Upstream Source First
1820

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

2128
## Core Concepts
2229

2330
### 1. LLMObsPlugin Base Class
2431

25-
All LLMObs plugins extend `LLMObsPlugin`. Two methods must be implemented:
32+
Leaf plugins extend `LLMObsPlugin` and implement two methods:
2633

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

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

32-
See [references/plugin-architecture.md](references/plugin-architecture.md) for the full implementation surface.
33-
34-
### 2. Package Category System
35-
36-
**CRITICAL:** Every integration must be classified into one category using the `LlmObsCategory` enum. This determines test strategy and implementation approach.
37-
38-
#### LlmObsCategory Enum Values
39-
40-
- **`LlmObsCategory.LLM_CLIENT`** - Direct API wrappers (openai, anthropic, genai)
41-
- Signs: Makes HTTP calls to LLM provider endpoints, requires API keys
42-
- Test strategy: VCR with real API calls via proxy
43-
- Instrumentation: Hook chat/completion methods
44-
45-
- **`LlmObsCategory.MULTI_PROVIDER`** - Multi-provider frameworks (ai-sdk, langchain)
46-
- Signs: Supports multiple LLM providers via configuration, wraps LLM_CLIENT libraries
47-
- Test strategy: VCR with real API calls via proxy
48-
- Instrumentation: Hook provider abstraction layer
39+
On the usual promise-backed channel, `start(ctx)` registers the span and captures context, `end(ctx)` restores the
40+
parent after the wrapped call returns, and `asyncEnd(ctx)` calls `setLLMObsTags()` after the operation settles.
4941

50-
- **`LlmObsCategory.ORCHESTRATION`** - Workflow managers (langgraph)
51-
- Signs: Graph/workflow execution, state management, NO direct HTTP to LLM providers
52-
- Test strategy: Pure function tests, NO VCR, NO real API calls
53-
- Instrumentation: Hook workflow lifecycle (invoke, stream, run)
54-
- **Special:** Tests should use actual LLM as orchestration node (not mock responses)
55-
56-
- **`LlmObsCategory.INFRASTRUCTURE`** - Protocols/servers (MCP)
57-
- Signs: Protocol implementation, server/client architecture, transport layers
58-
- Test strategy: Mock server tests
59-
- Instrumentation: Hook protocol handlers
60-
61-
#### Decision Tree
42+
See [references/plugin-architecture.md](references/plugin-architecture.md) for the full implementation surface.
6243

63-
Answer these questions by reading the code:
44+
### 2. Package Shape
6445

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

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

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

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

7965
### 3. LLM Span Kinds
8066

81-
Use the `LlmObsSpanKind` enum:
82-
83-
- **`LlmObsSpanKind.LLM`** - Chat completions, text generation
84-
- **`LlmObsSpanKind.WORKFLOW`** - Graph/chain execution
85-
- **`LlmObsSpanKind.AGENT`** - Agent runs
86-
- **`LlmObsSpanKind.TOOL`** - Tool/function calls
87-
- **`LlmObsSpanKind.EMBEDDING`** - Embedding generation
88-
- **`LlmObsSpanKind.RETRIEVAL`** - Vector DB/RAG retrieval
89-
90-
**Most common:** Use `'llm'` for chat completions/text generation in LLM_CLIENT and MULTI_PROVIDER categories.
67+
`SPAN_KINDS` in `packages/dd-trace/src/llmobs/constants/tags.js` lists `llm`, `agent`, `workflow`, `task`, `tool`,
68+
`embedding`, `retrieval`. Chat completions and text generation are `llm`; graph or chain execution is `workflow`;
69+
agent runs are `agent`; vector-DB and RAG lookups are `retrieval`. Only the public SDK validates against that list,
70+
so a plugin may register a kind outside it — `ai` v7 and claude-agent-sdk both use `step`.
9171

9272
### 4. Message Extraction
9373

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

96-
**Standard format:** `[{content: string, role: string}]`
76+
**Common shape:** `[{ content?: string, role: string, toolCalls?: object[], toolResults?: object[] }]`
9777

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

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

10888
## Implementation Steps
10989

110-
1. **Detect package category** (REQUIRED FIRST STEP)
111-
- Follow decision tree above
112-
- Output: category, confidence, reasoning
113-
114-
2. **Create plugin file**
115-
- Location: `packages/dd-trace/src/llmobs/plugins/{integration}/index.js`
116-
- Extend: `LLMObsPlugin` base class
117-
- Implement: Required methods per plugin architecture
118-
119-
3. **Implement `getLLMObsSpanRegisterOptions(ctx)`**
120-
- Extract model provider and name from context
121-
- Determine span kind (usually `'llm'`)
122-
- Return registration options object
123-
124-
4. **Implement `setLLMObsTags(ctx)`**
125-
- Extract input messages from `ctx.arguments`
126-
- Extract output messages from `ctx.result`
127-
- Extract token metrics (input_tokens, output_tokens, total_tokens)
128-
- Extract metadata (temperature, max_tokens, etc.)
129-
- Tag span using `this._tagger` methods
130-
131-
5. **Handle edge cases**
132-
- Streaming responses (if applicable)
133-
- Error cases (empty output messages)
134-
- Non-standard message formats
135-
- Missing metadata
136-
137-
See [references/plugin-architecture.md](references/plugin-architecture.md) for step-by-step implementation guide.
138-
139-
## Plugin Registration
140-
141-
All plugins must export an array:
142-
143-
**Static properties required:**
144-
- `integration` - Integration name (e.g., 'openai')
145-
- `id` - Unique plugin ID (e.g., 'llmobs_openai')
146-
- `prefix` - Channel prefix (e.g., 'tracing:apm:openai:chat')
147-
148-
## References
149-
150-
For detailed information, see:
151-
152-
- [references/plugin-architecture.md](references/plugin-architecture.md) - Complete plugin structure, implementation steps, helper methods
153-
- [references/category-detection.md](references/category-detection.md) - Package classification heuristics and detection process
154-
- [references/message-extraction.md](references/message-extraction.md) - Provider-specific message format patterns
155-
- [references/reference-implementations.md](references/reference-implementations.md) - Working plugin examples (Anthropic, Google GenAI)
90+
1. **Map each surface's response source and operation kind**, from the upstream source rather than the package name.
91+
2. **Create leaf plugins under `packages/dd-trace/src/llmobs/plugins/{integration}/`** extending `LLMObsPlugin`.
92+
3. **Implement `getLLMObsSpanRegisterOptions(ctx)`** — span kind plus any available name, model and session fields.
93+
4. **Implement `setLLMObsTags(ctx)`** — input, output, metrics and metadata from the fields the instrumentation
94+
publishes on `ctx`, tagged through `this._tagger`.
95+
5. **Cover the edges**: streaming, kind-specific error output, non-standard formats, absent metadata.
96+
97+
Export the class itself when the package needs one plugin (openai, anthropic, genai), or an array when several
98+
operations each need their own (langchain, langgraph, modelcontextprotocol-sdk, claude-agent-sdk). Use a
99+
`CompositePlugin` root when one integration selects between child implementations, as `ai` does. The required static
100+
fields and the rest of the surface are in [references/plugin-architecture.md](references/plugin-architecture.md).

0 commit comments

Comments
 (0)