11import { NodeSDK } from "@opentelemetry/sdk-node" ;
2- import {
3- SimpleSpanProcessor ,
4- BatchSpanProcessor ,
5- SpanProcessor ,
6- ReadableSpan ,
7- } from "@opentelemetry/sdk-trace-node" ;
2+ import { SpanProcessor } from "@opentelemetry/sdk-trace-node" ;
83import { baggageUtils } from "@opentelemetry/core" ;
9- import { Span , context , diag } from "@opentelemetry/api" ;
4+ import { context , diag } from "@opentelemetry/api" ;
105import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto" ;
116import { Resource } from "@opentelemetry/resources" ;
127import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions" ;
138import { Instrumentation } from "@opentelemetry/instrumentation" ;
149import { InitializeOptions } from "../interfaces" ;
15- import {
16- ASSOCATION_PROPERTIES_KEY ,
17- ENTITY_NAME_KEY ,
18- WORKFLOW_NAME_KEY ,
19- } from "./tracing" ;
2010import { Telemetry } from "../telemetry/telemetry" ;
2111import { _configuration } from "../configuration" ;
22- import {
23- CONTEXT_KEY_ALLOW_TRACE_CONTENT ,
24- SpanAttributes ,
25- } from "@traceloop/ai-semantic-conventions" ;
12+ import { CONTEXT_KEY_ALLOW_TRACE_CONTENT } from "@traceloop/ai-semantic-conventions" ;
2613import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic" ;
2714import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai" ;
2815import { AzureOpenAIInstrumentation } from "@traceloop/instrumentation-azure" ;
@@ -38,9 +25,10 @@ import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
3825import { ChromaDBInstrumentation } from "@traceloop/instrumentation-chromadb" ;
3926import { QdrantInstrumentation } from "@traceloop/instrumentation-qdrant" ;
4027import { TogetherInstrumentation } from "@traceloop/instrumentation-together" ;
28+ import { createSpanProcessor } from "./span-processor" ;
4129
4230let _sdk : NodeSDK ;
43- let _spanProcessor : SimpleSpanProcessor | BatchSpanProcessor ;
31+ let _spanProcessor : SpanProcessor ;
4432let openAIInstrumentation : OpenAIInstrumentation | undefined ;
4533let anthropicInstrumentation : AnthropicInstrumentation | undefined ;
4634let azureOpenAIInstrumentation : AzureOpenAIInstrumentation | undefined ;
@@ -273,117 +261,14 @@ export const startTracing = (options: InitializeOptions) => {
273261 url : `${ options . baseUrl } /v1/traces` ,
274262 headers,
275263 } ) ;
276- _spanProcessor = options . disableBatch
277- ? new SimpleSpanProcessor ( traceExporter )
278- : new BatchSpanProcessor ( traceExporter ) ;
279-
280- _spanProcessor . onStart = ( span : Span ) => {
281- const workflowName = context . active ( ) . getValue ( WORKFLOW_NAME_KEY ) ;
282- if ( workflowName ) {
283- span . setAttribute (
284- SpanAttributes . TRACELOOP_WORKFLOW_NAME ,
285- workflowName as string ,
286- ) ;
287- }
288-
289- const entityName = context . active ( ) . getValue ( ENTITY_NAME_KEY ) ;
290- if ( entityName ) {
291- span . setAttribute (
292- SpanAttributes . TRACELOOP_ENTITY_PATH ,
293- entityName as string ,
294- ) ;
295- }
296-
297- const associationProperties = context
298- . active ( )
299- . getValue ( ASSOCATION_PROPERTIES_KEY ) ;
300- if ( associationProperties ) {
301- for ( const [ key , value ] of Object . entries ( associationProperties ) ) {
302- span . setAttribute (
303- `${ SpanAttributes . TRACELOOP_ASSOCIATION_PROPERTIES } .${ key } ` ,
304- value ,
305- ) ;
306- }
307- }
308- } ;
309-
310- const originalOnEnd = _spanProcessor . onEnd ?. bind ( _spanProcessor ) ;
311- _spanProcessor . onEnd = ( span : ReadableSpan ) => {
312- // Vercel AI Adapters
313- const attributes = span . attributes ;
314-
315- // Adapt span names
316- const nameMap : Record < string , string > = {
317- "ai.generateText.doGenerate" : "ai.generateText.generate" ,
318- "ai.streamText.doStream" : "ai.streamText.stream" ,
319- } ;
320- if ( span . name in nameMap ) {
321- // Unfortuantely, the span name is not writable as this is not the intended behavior
322- // but it is a workaround to set the correct span name
323- ( span as any ) . name = nameMap [ span . name ] ;
324- }
325-
326- if ( "ai.response.text" in attributes ) {
327- attributes [ `${ SpanAttributes . LLM_COMPLETIONS } .0.content` ] =
328- attributes [ "ai.response.text" ] ;
329- attributes [ `${ SpanAttributes . LLM_COMPLETIONS } .0.role` ] = "assistant" ;
330- delete attributes [ "ai.response.text" ] ;
331- }
332-
333- if ( "ai.prompt.messages" in attributes ) {
334- try {
335- const messages = JSON . parse ( attributes [ "ai.prompt.messages" ] as string ) ;
336- messages . forEach (
337- ( msg : { role : string ; content : any } , index : number ) => {
338- attributes [ `${ SpanAttributes . LLM_PROMPTS } .${ index } .content` ] =
339- typeof msg . content === "string"
340- ? msg . content
341- : JSON . stringify ( msg . content ) ;
342- attributes [ `${ SpanAttributes . LLM_PROMPTS } .${ index } .role` ] =
343- msg . role ;
344- } ,
345- ) ;
346- delete attributes [ "ai.prompt.messages" ] ;
347- } catch ( e ) {
348- //Skip if JSON parsing fails
349- }
350- }
351-
352- if ( "ai.usage.promptTokens" in attributes ) {
353- attributes [ `${ SpanAttributes . LLM_USAGE_PROMPT_TOKENS } ` ] =
354- attributes [ "ai.usage.promptTokens" ] ;
355- delete attributes [ "ai.usage.promptTokens" ] ;
356- }
357-
358- if ( "ai.usage.completionTokens" in attributes ) {
359- attributes [ `${ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS } ` ] =
360- attributes [ "ai.usage.completionTokens" ] ;
361- delete attributes [ "ai.usage.completionTokens" ] ;
362- }
363-
364- if (
365- attributes [ `${ SpanAttributes . LLM_USAGE_PROMPT_TOKENS } ` ] &&
366- attributes [ `${ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS } ` ]
367- ) {
368- attributes [ `${ SpanAttributes . LLM_USAGE_TOTAL_TOKENS } ` ] =
369- Number ( attributes [ `${ SpanAttributes . LLM_USAGE_PROMPT_TOKENS } ` ] ) +
370- Number ( attributes [ `${ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS } ` ] ) ;
371- }
372-
373- originalOnEnd ?.( span ) ;
374- } ;
375-
376- if ( options . exporter ) {
377- Telemetry . getInstance ( ) . capture ( "tracer:init" , {
378- exporter : "custom" ,
379- processor : options . disableBatch ? "simple" : "batch" ,
380- } ) ;
381- } else {
382- Telemetry . getInstance ( ) . capture ( "tracer:init" , {
383- exporter : options . baseUrl ?? "" ,
384- processor : options . disableBatch ? "simple" : "batch" ,
385- } ) ;
386- }
264+
265+ _spanProcessor = createSpanProcessor ( {
266+ apiKey : options . apiKey ,
267+ baseUrl : options . baseUrl ,
268+ disableBatch : options . disableBatch ,
269+ exporter : traceExporter ,
270+ headers,
271+ } ) ;
387272
388273 const spanProcessors : SpanProcessor [ ] = [ _spanProcessor ] ;
389274 if ( options . processor ) {
0 commit comments