@@ -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