@reaatech/otel-genai-semconv-anthropic
Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.
Transparent instrumentation for the Anthropic Node.js SDK . Wraps client.messages.create() to emit OpenTelemetry GenAI semantic convention spans with request metadata, token usage including prompt caching, cost tracking, and streaming metrics for message delta events.
Installation
npm install @reaatech/otel-genai-semconv-anthropic
# or
pnpm add @reaatech/otel-genai-semconv-anthropic
Feature Overview
Zero-config instrumentation — call instrument(client) once, every messages.create() call is traced
Prompt caching awareness — tracks cache_read_input_tokens and cache_creation_input_tokens from Anthropic’s usage metadata
Streaming delta aggregation — merges message_start, content_block_delta, and message_delta events into a final Message with accumulated token counts
Tool use events — tool call content blocks emit gen_ai.tool_call span events with name and input
Double-instrumentation guard — calling instrument() twice is a safe no-op
Lifecycle hooks — onStart and onEnd callbacks for custom span attributes
Safe uninstrument — restores the original create method
Dual ESM/CJS output — works with import and require
Quick Start
import { AnthropicInstrumentation } from "@reaatech/otel-genai-semconv-anthropic" ;
import Anthropic from "@anthropic-ai/sdk" ;
const client = new Anthropic ({ apiKey: process.env.ANTHROPIC_API_KEY });
new AnthropicInstrumentation ({ trackCosts: true }). instrument (client);
const response = await client.messages. create ({
model: "claude-3-opus-20240229" ,
max_tokens: 200 ,
messages: [{ role: "user" , content: "What are the benefits of OpenTelemetry?" }],
});
// Each call now emits OTel spans with gen_ai.* attributes
Captured Attributes
Request Attributes
Attribute Source Description gen_ai.request.modelrequest.modelRequested model name gen_ai.request.max_tokensrequest.max_tokensMax tokens limit gen_ai.request.temperaturerequest.temperatureSampling temperature gen_ai.request.top_prequest.top_pTop-p sampling gen_ai.request.top_krequest.top_kTop-k sampling gen_ai.request.streamingrequest.streamStreaming flag gen_ai.request.stop_sequencesrequest.stop_sequencesStop sequences gen_ai.request.tool_namesrequest.toolsTool names gen_ai.provider.namehardcoded anthropic
Response Attributes
Attribute Source Description gen_ai.response.modelresponse.modelActual model used gen_ai.response.idresponse.idResponse identifier gen_ai.response.finish_reasonsresponse.stop_reason (mapped)Mapped to OTel finish reason gen_ai.usage.input_tokensresponse.usage.input_tokensInput token count gen_ai.usage.output_tokensresponse.usage.output_tokensOutput token count
Stop Reason Mapping
Anthropic’s stop_reason values are mapped to OTel finish_reason:
Anthropic OTel end_turnstopstop_sequencestopmax_tokenslengthtool_usetool_calls
Streaming Attributes
Attribute Description gen_ai.streaming.time_to_first_token_msLatency to first chunk gen_ai.streaming.total_duration_msTotal streaming duration gen_ai.streaming.chunk_countNumber of chunks received
Cost Attributes (when trackCosts: true)
Attribute Description llm.cost.totalTotal cost in USD llm.cost.inputInput token cost llm.cost.outputOutput token cost llm.cost.currencyCurrency code (always USD)
Span Events
Event When gen_ai.system.messageSystem prompt in the request gen_ai.user.messageUser messages in the request gen_ai.assistant.messageText content blocks in the response gen_ai.tool_callTool use content blocks (with tool_name, tool_input)
API Reference
AnthropicInstrumentation (class)
Constructor
new AnthropicInstrumentation ({
captureRequestHeaders?: boolean;
captureResponseHeaders ?: boolean;
trackCosts ?: boolean;
pricing ?: Record < string, PricingInfo>;
onStart?: (span : Span , request : MessageCreateParams ) => void ;
onEnd ?: (span : Span , response : Message ) => void ;
})
Methods
Method Description instrument(client)Wrap client.messages.create() with instrumentation uninstrument(client)Restore the original create() method
AnthropicTokenCounter (class)
Character-based token estimation for Anthropic models:
const counter = new AnthropicTokenCounter ();
counter. countTokens ( "Hello, world!" , "claude-3-opus-20240229" );
counter. countMessagesTokens (messages, "claude-3-opus-20240229" );
counter. clearCache ();
Attribute Mappers
import { mapAnthropicRequest, mapAnthropicResponse, mapAnthropicError } from "@reaatech/otel-genai-semconv-anthropic" ;
const requestAttrs = mapAnthropicRequest (messageParams);
const responseAttrs = mapAnthropicResponse (messageObject);
const errorAttrs = mapAnthropicError (apiError);
Configuration
Custom Pricing
new AnthropicInstrumentation ({
trackCosts: true ,
pricing: {
"claude-3-opus" : { input: 0.015 , output: 0.075 },
"claude-3-sonnet" : { input: 0.003 , output: 0.015 },
},
}). instrument (client);
Lifecycle Hooks
new AnthropicInstrumentation ({
onStart : (span, request) => {
if (request.metadata?.user_id) {
span. setAttribute ( "enduser.id" , request.metadata.user_id);
}
},
onEnd : (span, response) => {
span. setAttribute ( "response.stop_reason" , response.stop_reason);
},
}). instrument (client);
Usage Patterns
Streaming with Delta Aggregation
The instrumentation automatically aggregates streaming delta events into a final Message:
const stream = await client.messages. create ({
model: "claude-3-opus-20240229" ,
max_tokens: 500 ,
messages: [{ role: "user" , content: "Write a haiku" }],
stream: true ,
});
for await ( const event of stream) {
// Each event type (message_start, content_block_delta, message_delta) is tracked
}
// Span auto-finalizes with aggregated response attributes, tokens, and cost
Multi-Client
const instrumentation = new AnthropicInstrumentation ({ trackCosts: true });
const client1 = new Anthropic ({ apiKey: "..." , baseURL: "..." });
const client2 = new Anthropic ({ apiKey: "..." , baseURL: "..." });
instrumentation. instrument (client1);
instrumentation. instrument (client2);
Related Packages
License
MIT