@@ -19,7 +19,13 @@ import { BaseMessage } from "@langchain/core/messages";
1919import { LLMResult } from "@langchain/core/outputs" ;
2020import { Serialized } from "@langchain/core/load/serializable" ;
2121import { ChainValues } from "@langchain/core/utils/types" ;
22- import { Tracer , trace , SpanKind , SpanStatusCode , context } from "@opentelemetry/api" ;
22+ import {
23+ Tracer ,
24+ trace ,
25+ SpanKind ,
26+ SpanStatusCode ,
27+ context ,
28+ } from "@opentelemetry/api" ;
2329import { SpanAttributes } from "@traceloop/ai-semantic-conventions" ;
2430
2531interface SpanData {
@@ -28,10 +34,9 @@ interface SpanData {
2834 runId : string ;
2935}
3036
31-
3237export class TraceloopCallbackHandler extends BaseCallbackHandler {
3338 name = "traceloop_callback_handler" ;
34-
39+
3540 private tracer : Tracer ;
3641 private spans : Map < string , SpanData > = new Map ( ) ;
3742 private traceContent : boolean ;
@@ -50,14 +55,14 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
5055 extraParams ?: Record < string , unknown > ,
5156 tags ?: string [ ] ,
5257 metadata ?: Record < string , unknown > ,
53- runName ?: string
58+ runName ?: string ,
5459 ) : Promise < void > {
5560 const className = llm . id ?. [ llm . id . length - 1 ] || "unknown" ;
5661 const modelName = this . extractModelName ( llm ) ;
5762 const vendor = this . detectVendor ( llm ) ;
5863 const spanBaseName = this . convertClassNameToSpanName ( className ) ;
59-
60- // Create both a task span and an LLM span like Python implementation
64+
65+ // Create both a task span and an LLM span like Python implementation
6166 const taskSpanName = `${ spanBaseName } .task` ;
6267 const taskSpan = this . tracer . startSpan ( taskSpanName , {
6368 kind : SpanKind . CLIENT ,
@@ -71,17 +76,26 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
7176 if ( this . traceContent ) {
7277 const flatMessages = messages . flat ( ) ;
7378 taskSpan . setAttributes ( {
74- "traceloop.entity.input" : JSON . stringify ( flatMessages . map ( m => ( {
75- role : m . _getType ( ) ,
76- content : typeof m . content === 'string' ? m . content : JSON . stringify ( m . content )
77- } ) ) ) ,
79+ "traceloop.entity.input" : JSON . stringify (
80+ flatMessages . map ( ( m ) => ( {
81+ role : m . _getType ( ) ,
82+ content :
83+ typeof m . content === "string"
84+ ? m . content
85+ : JSON . stringify ( m . content ) ,
86+ } ) ) ,
87+ ) ,
7888 } ) ;
7989 }
8090
8191 // Create LLM span as child of task span
82- const llmSpan = this . tracer . startSpan ( `${ spanBaseName } .completion` , {
83- kind : SpanKind . CLIENT ,
84- } , trace . setSpan ( context . active ( ) , taskSpan ) ) ;
92+ const llmSpan = this . tracer . startSpan (
93+ `${ spanBaseName } .completion` ,
94+ {
95+ kind : SpanKind . CLIENT ,
96+ } ,
97+ trace . setSpan ( context . active ( ) , taskSpan ) ,
98+ ) ;
8599
86100 const flatMessages = messages . flat ( ) ;
87101 llmSpan . setAttributes ( {
@@ -96,7 +110,10 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
96110 const role = this . mapMessageTypeToRole ( message . _getType ( ) ) ;
97111 llmSpan . setAttributes ( {
98112 [ `${ SpanAttributes . LLM_PROMPTS } .${ idx } .role` ] : role ,
99- [ `${ SpanAttributes . LLM_PROMPTS } .${ idx } .content` ] : typeof message . content === 'string' ? message . content : JSON . stringify ( message . content ) ,
113+ [ `${ SpanAttributes . LLM_PROMPTS } .${ idx } .content` ] :
114+ typeof message . content === "string"
115+ ? message . content
116+ : JSON . stringify ( message . content ) ,
100117 } ) ;
101118 } ) ;
102119 }
@@ -113,14 +130,14 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
113130 _extraParams ?: Record < string , unknown > ,
114131 _tags ?: string [ ] ,
115132 _metadata ?: Record < string , unknown > ,
116- _runName ?: string
133+ _runName ?: string ,
117134 ) : Promise < void > {
118135 const className = llm . id ?. [ llm . id . length - 1 ] || "unknown" ;
119136 const modelName = this . extractModelName ( llm ) ;
120137 const vendor = this . detectVendor ( llm ) ;
121138 const spanBaseName = this . convertClassNameToSpanName ( className ) ;
122139 const spanName = `${ spanBaseName } .task` ;
123-
140+
124141 const span = this . tracer . startSpan ( spanName , {
125142 kind : SpanKind . CLIENT ,
126143 } ) ;
@@ -150,7 +167,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
150167 runId : string ,
151168 _parentRunId ?: string ,
152169 _tags ?: string [ ] ,
153- _extraParams ?: Record < string , unknown >
170+ _extraParams ?: Record < string , unknown > ,
154171 ) : Promise < void > {
155172 // End both LLM and task spans
156173 const llmSpanData = this . spans . get ( `${ runId } _llm` ) ;
@@ -159,12 +176,17 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
159176 if ( llmSpanData ) {
160177 const { span : llmSpan } = llmSpanData ;
161178
162- if ( this . traceContent && output . generations && output . generations . length > 0 ) {
179+ if (
180+ this . traceContent &&
181+ output . generations &&
182+ output . generations . length > 0
183+ ) {
163184 output . generations . forEach ( ( generation , idx ) => {
164185 if ( generation && generation . length > 0 ) {
165186 llmSpan . setAttributes ( {
166187 [ `${ SpanAttributes . LLM_COMPLETIONS } .${ idx } .role` ] : "assistant" ,
167- [ `${ SpanAttributes . LLM_COMPLETIONS } .${ idx } .content` ] : generation [ 0 ] . text ,
188+ [ `${ SpanAttributes . LLM_COMPLETIONS } .${ idx } .content` ] :
189+ generation [ 0 ] . text ,
168190 } ) ;
169191 }
170192 } ) ;
@@ -183,14 +205,15 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
183205 [ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS ] : usage . output_tokens ,
184206 } ) ;
185207 }
186- const totalTokens = ( usage . input_tokens || 0 ) + ( usage . output_tokens || 0 ) ;
208+ const totalTokens =
209+ ( usage . input_tokens || 0 ) + ( usage . output_tokens || 0 ) ;
187210 if ( totalTokens > 0 ) {
188211 llmSpan . setAttributes ( {
189212 [ SpanAttributes . LLM_USAGE_TOTAL_TOKENS ] : totalTokens ,
190213 } ) ;
191214 }
192215 }
193-
216+
194217 // Also check for tokenUsage format (for compatibility)
195218 if ( output . llmOutput ?. tokenUsage ) {
196219 const usage = output . llmOutput . tokenUsage ;
@@ -201,7 +224,8 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
201224 }
202225 if ( usage . completionTokens ) {
203226 llmSpan . setAttributes ( {
204- [ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS ] : usage . completionTokens ,
227+ [ SpanAttributes . LLM_USAGE_COMPLETION_TOKENS ] :
228+ usage . completionTokens ,
205229 } ) ;
206230 }
207231 if ( usage . totalTokens ) {
@@ -218,8 +242,12 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
218242
219243 if ( taskSpanData ) {
220244 const { span : taskSpan } = taskSpanData ;
221-
222- if ( this . traceContent && output . generations && output . generations . length > 0 ) {
245+
246+ if (
247+ this . traceContent &&
248+ output . generations &&
249+ output . generations . length > 0
250+ ) {
223251 const completions = output . generations . map ( ( generation , _idx ) => {
224252 if ( generation && generation . length > 0 ) {
225253 return generation [ 0 ] . text ;
@@ -242,7 +270,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
242270 runId : string ,
243271 _parentRunId ?: string ,
244272 _tags ?: string [ ] ,
245- _extraParams ?: Record < string , unknown >
273+ _extraParams ?: Record < string , unknown > ,
246274 ) : Promise < void > {
247275 // Same as handleLLMEnd for chat models
248276 return this . handleLLMEnd ( output , runId , _parentRunId , _tags , _extraParams ) ;
@@ -253,7 +281,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
253281 runId : string ,
254282 _parentRunId ?: string ,
255283 _tags ?: string [ ] ,
256- _extraParams ?: Record < string , unknown >
284+ _extraParams ?: Record < string , unknown > ,
257285 ) : Promise < void > {
258286 // End both spans on error
259287 const llmSpanData = this . spans . get ( `${ runId } _llm` ) ;
@@ -284,11 +312,11 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
284312 _tags ?: string [ ] ,
285313 metadata ?: Record < string , unknown > ,
286314 runType ?: string ,
287- runName ?: string
315+ runName ?: string ,
288316 ) : Promise < void > {
289317 const chainName = chain . id ?. [ chain . id . length - 1 ] || "unknown" ;
290318 const spanName = `${ chainName } .workflow` ;
291-
319+
292320 const span = this . tracer . startSpan ( spanName , {
293321 kind : SpanKind . CLIENT ,
294322 } ) ;
@@ -312,7 +340,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
312340 runId : string ,
313341 _parentRunId ?: string ,
314342 _tags ?: string [ ] ,
315- _kwargs ?: { inputs ?: Record < string , unknown > }
343+ _kwargs ?: { inputs ?: Record < string , unknown > } ,
316344 ) : Promise < void > {
317345 const spanData = this . spans . get ( runId ) ;
318346 if ( ! spanData ) return ;
@@ -335,7 +363,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
335363 runId : string ,
336364 _parentRunId ?: string ,
337365 _tags ?: string [ ] ,
338- _kwargs ?: { inputs ?: Record < string , unknown > }
366+ _kwargs ?: { inputs ?: Record < string , unknown > } ,
339367 ) : Promise < void > {
340368 const spanData = this . spans . get ( runId ) ;
341369 if ( ! spanData ) return ;
@@ -354,11 +382,11 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
354382 _parentRunId ?: string ,
355383 _tags ?: string [ ] ,
356384 _metadata ?: Record < string , unknown > ,
357- _runName ?: string
385+ _runName ?: string ,
358386 ) : Promise < void > {
359387 const toolName = tool . id ?. [ tool . id . length - 1 ] || "unknown" ;
360388 const spanName = `${ toolName } .task` ;
361-
389+
362390 const span = this . tracer . startSpan ( spanName , {
363391 kind : SpanKind . CLIENT ,
364392 } ) ;
@@ -381,7 +409,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
381409 output : any ,
382410 runId : string ,
383411 _parentRunId ?: string ,
384- _tags ?: string [ ]
412+ _tags ?: string [ ] ,
385413 ) : Promise < void > {
386414 const spanData = this . spans . get ( runId ) ;
387415 if ( ! spanData ) return ;
@@ -403,7 +431,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
403431 err : Error ,
404432 runId : string ,
405433 _parentRunId ?: string ,
406- _tags ?: string [ ]
434+ _tags ?: string [ ] ,
407435 ) : Promise < void > {
408436 const spanData = this . spans . get ( runId ) ;
409437 if ( ! spanData ) return ;
@@ -418,39 +446,38 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
418446 private extractModelName ( llm : Serialized ) : string {
419447 // Extract from class hierarchy - last element is usually the class name
420448 const className = llm . id ?. [ llm . id . length - 1 ] || "unknown" ;
421-
449+
422450 // For BedrockChat, try to get the actual model name
423451 if ( className === "BedrockChat" ) {
424452 // The model name might be available in kwargs - cast to any to access kwargs
425453 const llmAny = llm as any ;
426454 const modelId = llmAny . kwargs ?. model || llmAny . kwargs ?. model_id ;
427- if ( modelId && typeof modelId === ' string' ) {
455+ if ( modelId && typeof modelId === " string" ) {
428456 // Extract clean model name from full ID (e.g., "us.anthropic.claude-3-7-sonnet-20250219-v1:0" -> "claude-3-7-sonnet")
429- const parts = modelId . split ( '.' ) ;
457+ const parts = modelId . split ( "." ) ;
430458 if ( parts . length >= 3 ) {
431- const modelPart = parts . slice ( 2 ) . join ( '.' ) . split ( ':' ) [ 0 ] ; // Remove region and version
432- return modelPart . replace ( ' -20250219-v1' , '' ) ; // Clean up version suffix
459+ const modelPart = parts . slice ( 2 ) . join ( "." ) . split ( ":" ) [ 0 ] ; // Remove region and version
460+ return modelPart . replace ( " -20250219-v1" , "" ) ; // Clean up version suffix
433461 }
434462 return modelId ;
435463 }
436464 }
437-
465+
438466 return className ;
439467 }
440468
441469 private convertClassNameToSpanName ( className : string ) : string {
442470 // Convert PascalCase to lowercase with dots
443471 // BedrockChat -> bedrock.chat
444472 // ChatOpenAI -> chat.openai
445- return className
446- . replace ( / ( [ A - Z ] ) / g, ( match , char , index ) => {
447- return index === 0 ? char . toLowerCase ( ) : `.${ char . toLowerCase ( ) } ` ;
448- } ) ;
473+ return className . replace ( / ( [ A - Z ] ) / g, ( match , char , index ) => {
474+ return index === 0 ? char . toLowerCase ( ) : `.${ char . toLowerCase ( ) } ` ;
475+ } ) ;
449476 }
450477
451478 private detectVendor ( llm : Serialized ) : string {
452479 const className = llm . id ?. [ llm . id . length - 1 ] || "" ;
453-
480+
454481 // Follow Python implementation - map class names to vendors
455482 if ( className . includes ( "OpenAI" ) || className . includes ( "GPT" ) ) {
456483 return "OpenAI" ;
@@ -477,7 +504,7 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
477504 if ( className . includes ( "Cohere" ) ) {
478505 return "Cohere" ;
479506 }
480-
507+
481508 return "LangChain" ;
482509 }
483510
@@ -496,4 +523,4 @@ export class TraceloopCallbackHandler extends BaseCallbackHandler {
496523 return messageType ;
497524 }
498525 }
499- }
526+ }
0 commit comments