@reaatech/otel-genai-semconv-vertexai
Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.
Transparent instrumentation for the Google Generative Language (Vertex AI) SDK . Wraps model.generateContent() to emit OpenTelemetry GenAI semantic convention spans with GCP project/location metadata, generation config attributes, candidate events, and cost tracking for Gemini models.
Installation
npm install @reaatech/otel-genai-semconv-vertexai
# or
pnpm add @reaatech/otel-genai-semconv-vertexai
Feature Overview
Zero-config instrumentation — call instrument(model) once, every generateContent() call is traced
GCP metadata — automatically attaches gcp.project_id and gcp.location when configured
Generation config mapping — temperature, topP, topK, maxOutputTokens, stopSequences, and more mapped to OTel attributes
Candidate events — each response candidate emits a gen_ai.choice event with text content and finish reason
System instruction tracking — system instructions are captured as gen_ai.system.message events
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 generateContent() method
Dual ESM/CJS output — works with import and require
Quick Start
import { VertexAIInstrumentation } from "@reaatech/otel-genai-semconv-vertexai" ;
const instrumentation = new VertexAIInstrumentation ({
trackCosts: true ,
projectId: "my-gcp-project" ,
location: "us-central1" ,
});
instrumentation. instrument (model);
const response = await model. generateContent ({
contents: [{ role: "user" , parts: [{ text: "What is OpenTelemetry?" }] }],
});
// Each call now emits OTel spans with gen_ai.* attributes
Captured Attributes
Request Attributes
Attribute Source Description gen_ai.request.modelModel name Model identifier gen_ai.request.temperaturegenerationConfig.temperatureSampling temperature gen_ai.request.top_pgenerationConfig.topPTop-p sampling gen_ai.request.top_kgenerationConfig.topKTop-k sampling gen_ai.request.max_tokensgenerationConfig.maxOutputTokensMax output tokens gen_ai.request.stop_sequencesgenerationConfig.stopSequencesStop sequences gen_ai.request.candidates_per_promptgenerationConfig.candidateCountNumber of candidates gen_ai.request.presence_penaltygenerationConfig.presencePenaltyPresence penalty gen_ai.request.frequency_penaltygenerationConfig.frequencyPenaltyFrequency penalty gen_ai.request.tool_namesrequest.tools[].functionDeclarations[].nameTool names gen_ai.provider.namehardcoded gcp.vertex_ai
GCP Metadata (when configured)
Attribute Source Description gcp.project_idconfig.projectIdGCP project identifier gcp.locationconfig.locationGCP region
Response Attributes
Attribute Source Description gen_ai.response.modelresponse.modelVersionModel version used gen_ai.response.finish_reasonscandidates[].finishReason (mapped)Mapped to OTel finish reasons gen_ai.usage.input_tokensusageMetadata.promptTokenCountInput token count gen_ai.usage.output_tokensusageMetadata.candidatesTokenCountOutput token count
Finish Reason Mapping
Vertex AI’s finishReason values are mapped to OTel:
Vertex AI OTel STOPstopMAX_TOKENSlengthSAFETYcontent_filterRECITATIONcontent_filterOTHERunknown
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 instruction in the request gen_ai.user.messageUser content parts in the request gen_ai.assistant.messageAssistant content parts gen_ai.choiceEach candidate (with index, finish_reason, text content)
API Reference
VertexAIInstrumentation (class)
Constructor
new VertexAIInstrumentation ({
captureRequestHeaders?: boolean;
captureResponseHeaders ?: boolean;
trackCosts ?: boolean;
pricing ?: Record < string, PricingInfo>;
projectId?: string;
location ?: string;
onStart ?: (span : Span , request : GenerateContentRequest ) => void ;
onEnd ?: (span : Span , response : GenerateContentResponse ) => void ;
})
Methods
Method Description instrument(model)Wrap model.generateContent() with instrumentation uninstrument(model)Restore the original generateContent() method
VertexAITokenCounter (class)
Character-based token estimation for Vertex AI models:
const counter = new VertexAITokenCounter ();
counter. countTokens ( "Hello, world!" , "gemini-pro" );
counter. countContentsTokens (contents, "gemini-pro" );
counter. clearCache ();
Attribute Mappers
import { mapVertexAIRequest, mapVertexAIResponse, mapVertexAIError } from "@reaatech/otel-genai-semconv-vertexai" ;
const requestAttrs = mapVertexAIRequest (request, "gemini-pro" );
const responseAttrs = mapVertexAIResponse (response);
const errorAttrs = mapVertexAIError (apiError);
Configuration
GCP Project and Location
new VertexAIInstrumentation ({
projectId: "my-gcp-project" ,
location: "us-central1" ,
}). instrument (model);
Lifecycle Hooks
new VertexAIInstrumentation ({
onStart : (span, request) => {
span. setAttribute ( "vertexai.candidate_count" , request.generationConfig?.candidateCount ?? 1 );
},
onEnd : (span, response) => {
span. setAttribute ( "vertexai.model_version" , response.modelVersion);
},
}). instrument (model);
Usage Patterns
String Input (Auto-Normalized)
// The instrumentation automatically normalizes string input:
const response = await model. generateContent ( "What is OpenTelemetry?" );
// Internally converted to { contents: [{ role: "user", parts: [{ text: "..." }] }] }
Multi-Turn Conversation
const response = await model. generateContent ({
contents: [
{ role: "user" , parts: [{ text: "What is OpenTelemetry?" }] },
{ role: "assistant" , parts: [{ text: "OpenTelemetry is..." }] },
{ role: "user" , parts: [{ text: "Tell me more about tracing." }] },
],
});
// Each message emits the appropriate gen_ai.*.message event
Related Packages
License
MIT