11import { SeverityNumber } from "@opentelemetry/api-logs"
22import { SpanStatusCode , SpanKind , context , trace } from "@opentelemetry/api"
3- import type { AssistantMessage , EventMessageUpdated , EventMessagePartUpdated , ToolPart } from "@opencode-ai/sdk"
3+ import type {
4+ AssistantMessage ,
5+ EventMessageUpdated ,
6+ EventMessagePartUpdated ,
7+ ToolPart ,
8+ } from "@opencode-ai/sdk"
49import {
510 AGENT_NAME ,
611 INPUT_MIME_TYPE ,
@@ -27,8 +32,15 @@ import {
2732 TOOL_NAME ,
2833 TOOL_PARAMETERS ,
2934} from "@arizeai/openinference-semantic-conventions"
30- import { errorSummary , setBoundedMap , accumulateSessionTotals , isMetricEnabled , isTraceEnabled } from "../util.ts"
35+ import {
36+ errorSummary ,
37+ setBoundedMap ,
38+ accumulateSessionTotals ,
39+ isMetricEnabled ,
40+ isTraceEnabled ,
41+ } from "../util.ts"
3142import type { HandlerContext } from "../types.ts"
43+ import { resolveSessionTraceContext } from "./session.ts"
3244
3345const OPENINFERENCE_SPAN_KIND = SemanticConventions . OPENINFERENCE_SPAN_KIND
3446const LLM_FINISH_REASON = "llm.finish_reason"
@@ -58,37 +70,99 @@ export function handleMessageUpdated(e: EventMessageUpdated, ctx: HandlerContext
5870 const duration = assistant . time . completed - assistant . time . created
5971 const agent = ctx . sessionTotals . get ( sessionID ) ?. agent ?? "unknown"
6072
61- const totalTokens = assistant . tokens . input + assistant . tokens . output + assistant . tokens . reasoning
62- + assistant . tokens . cache . read + assistant . tokens . cache . write
73+ const totalTokens =
74+ assistant . tokens . input +
75+ assistant . tokens . output +
76+ assistant . tokens . reasoning +
77+ assistant . tokens . cache . read +
78+ assistant . tokens . cache . write
6379
6480 if ( isMetricEnabled ( "token.usage" , ctx ) ) {
6581 const { tokenCounter } = ctx . instruments
66- tokenCounter . add ( assistant . tokens . input , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "input" } )
67- tokenCounter . add ( assistant . tokens . output , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "output" } )
68- tokenCounter . add ( assistant . tokens . reasoning , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "reasoning" } )
69- tokenCounter . add ( assistant . tokens . cache . read , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "cacheRead" } )
70- tokenCounter . add ( assistant . tokens . cache . write , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "cacheCreation" } )
82+ tokenCounter . add ( assistant . tokens . input , {
83+ ...ctx . commonAttrs ,
84+ "session.id" : sessionID ,
85+ model : modelID ,
86+ agent,
87+ type : "input" ,
88+ } )
89+ tokenCounter . add ( assistant . tokens . output , {
90+ ...ctx . commonAttrs ,
91+ "session.id" : sessionID ,
92+ model : modelID ,
93+ agent,
94+ type : "output" ,
95+ } )
96+ tokenCounter . add ( assistant . tokens . reasoning , {
97+ ...ctx . commonAttrs ,
98+ "session.id" : sessionID ,
99+ model : modelID ,
100+ agent,
101+ type : "reasoning" ,
102+ } )
103+ tokenCounter . add ( assistant . tokens . cache . read , {
104+ ...ctx . commonAttrs ,
105+ "session.id" : sessionID ,
106+ model : modelID ,
107+ agent,
108+ type : "cacheRead" ,
109+ } )
110+ tokenCounter . add ( assistant . tokens . cache . write , {
111+ ...ctx . commonAttrs ,
112+ "session.id" : sessionID ,
113+ model : modelID ,
114+ agent,
115+ type : "cacheCreation" ,
116+ } )
71117 }
72118
73119 if ( isMetricEnabled ( "cost.usage" , ctx ) ) {
74- ctx . instruments . costCounter . add ( assistant . cost , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent } )
120+ ctx . instruments . costCounter . add ( assistant . cost , {
121+ ...ctx . commonAttrs ,
122+ "session.id" : sessionID ,
123+ model : modelID ,
124+ agent,
125+ } )
75126 }
76127
77128 if ( isMetricEnabled ( "cache.count" , ctx ) ) {
78129 if ( assistant . tokens . cache . read > 0 ) {
79- ctx . instruments . cacheCounter . add ( 1 , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "cacheRead" } )
130+ ctx . instruments . cacheCounter . add ( 1 , {
131+ ...ctx . commonAttrs ,
132+ "session.id" : sessionID ,
133+ model : modelID ,
134+ agent,
135+ type : "cacheRead" ,
136+ } )
80137 }
81138 if ( assistant . tokens . cache . write > 0 ) {
82- ctx . instruments . cacheCounter . add ( 1 , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent, type : "cacheCreation" } )
139+ ctx . instruments . cacheCounter . add ( 1 , {
140+ ...ctx . commonAttrs ,
141+ "session.id" : sessionID ,
142+ model : modelID ,
143+ agent,
144+ type : "cacheCreation" ,
145+ } )
83146 }
84147 }
85148
86149 if ( isMetricEnabled ( "message.count" , ctx ) ) {
87- ctx . instruments . messageCounter . add ( 1 , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , agent } )
150+ ctx . instruments . messageCounter . add ( 1 , {
151+ ...ctx . commonAttrs ,
152+ "session.id" : sessionID ,
153+ model : modelID ,
154+ agent,
155+ } )
88156 }
89157
90158 if ( isMetricEnabled ( "model.usage" , ctx ) ) {
91- ctx . instruments . modelUsageCounter . add ( 1 , { ...ctx . commonAttrs , "session.id" : sessionID , model : modelID , provider : providerID , agent } )
159+ ctx . instruments . modelUsageCounter . add ( 1 , {
160+ ...ctx . commonAttrs ,
161+ "session.id" : sessionID ,
162+ model : modelID ,
163+ provider : providerID ,
164+ agent,
165+ } )
92166 }
93167
94168 accumulateSessionTotals ( sessionID , totalTokens , assistant . cost , ctx )
@@ -129,7 +203,10 @@ export function handleMessageUpdated(e: EventMessageUpdated, ctx: HandlerContext
129203 duration_ms : duration ,
130204 } )
131205 if ( assistant . error ) {
132- msgSpan . setStatus ( { code : SpanStatusCode . ERROR , message : errorSummary ( assistant . error ) } )
206+ msgSpan . setStatus ( {
207+ code : SpanStatusCode . ERROR ,
208+ message : errorSummary ( assistant . error ) ,
209+ } )
133210 } else {
134211 msgSpan . setStatus ( { code : SpanStatusCode . OK } )
135212 }
@@ -256,9 +333,6 @@ export function handleMessagePartUpdated(e: EventMessagePartUpdated, ctx: Handle
256333 const toolSpan = isTraceEnabled ( "tool" , ctx )
257334 ? ( ( ) => {
258335 const sessionSpan = ctx . sessionSpans . get ( toolPart . sessionID )
259- const parentCtx = sessionSpan
260- ? trace . setSpan ( context . active ( ) , sessionSpan )
261- : context . active ( )
262336 return ctx . tracer . startSpan (
263337 `${ ctx . tracePrefix } tool.${ toolPart . tool } ` ,
264338 {
@@ -275,7 +349,7 @@ export function handleMessagePartUpdated(e: EventMessagePartUpdated, ctx: Handle
275349 ...ctx . commonAttrs ,
276350 } ,
277351 } ,
278- parentCtx ,
352+ resolveSessionTraceContext ( toolPart . sessionID , ctx ) ?? context . active ( ) ,
279353 )
280354 } ) ( )
281355 : undefined
@@ -285,7 +359,11 @@ export function handleMessagePartUpdated(e: EventMessagePartUpdated, ctx: Handle
285359 startMs : toolPart . state . time . start ,
286360 span : toolSpan ,
287361 } )
288- ctx . log ( "debug" , "otel: tool span started" , { sessionID : toolPart . sessionID , tool : toolPart . tool , key } )
362+ ctx . log ( "debug" , "otel: tool span started" , {
363+ sessionID : toolPart . sessionID ,
364+ tool : toolPart . tool ,
365+ key,
366+ } )
289367 return
290368 }
291369
@@ -309,30 +387,28 @@ export function handleMessagePartUpdated(e: EventMessagePartUpdated, ctx: Handle
309387 }
310388
311389 if ( isTraceEnabled ( "tool" , ctx ) ) {
312- const toolSpan = pending ?. span ?? ( ( ) => {
313- const sessionSpan = ctx . sessionSpans . get ( toolPart . sessionID )
314- const parentCtx = sessionSpan
315- ? trace . setSpan ( context . active ( ) , sessionSpan )
316- : context . active ( )
317- return ctx . tracer . startSpan (
318- `${ ctx . tracePrefix } tool.${ toolPart . tool } ` ,
319- {
320- startTime : start ,
321- kind : SpanKind . INTERNAL ,
322- attributes : {
323- [ OPENINFERENCE_SPAN_KIND ] : OpenInferenceSpanKind . TOOL ,
324- [ SESSION_ID ] : toolPart . sessionID ,
325- [ TOOL_ID ] : toolPart . callID ,
326- [ TOOL_NAME ] : toolPart . tool ,
327- [ TOOL_PARAMETERS ] : JSON . stringify ( toolPart . state . input ) ,
328- [ INPUT_VALUE ] : JSON . stringify ( toolPart . state . input ) ,
329- [ INPUT_MIME_TYPE ] : MimeType . JSON ,
330- ...ctx . commonAttrs ,
390+ const toolSpan =
391+ pending ?. span ??
392+ ( ( ) => {
393+ return ctx . tracer . startSpan (
394+ `${ ctx . tracePrefix } tool.${ toolPart . tool } ` ,
395+ {
396+ startTime : start ,
397+ kind : SpanKind . INTERNAL ,
398+ attributes : {
399+ [ OPENINFERENCE_SPAN_KIND ] : OpenInferenceSpanKind . TOOL ,
400+ [ SESSION_ID ] : toolPart . sessionID ,
401+ [ TOOL_ID ] : toolPart . callID ,
402+ [ TOOL_NAME ] : toolPart . tool ,
403+ [ TOOL_PARAMETERS ] : JSON . stringify ( toolPart . state . input ) ,
404+ [ INPUT_VALUE ] : JSON . stringify ( toolPart . state . input ) ,
405+ [ INPUT_MIME_TYPE ] : MimeType . JSON ,
406+ ...ctx . commonAttrs ,
407+ } ,
331408 } ,
332- } ,
333- parentCtx ,
334- )
335- } ) ( )
409+ resolveSessionTraceContext ( toolPart . sessionID , ctx ) ?? context . active ( ) ,
410+ )
411+ } ) ( )
336412 toolSpan . setAttribute ( "tool.success" , success )
337413 if ( success ) {
338414 const output = ( toolPart . state as { output : string } ) . output
@@ -355,7 +431,12 @@ export function handleMessagePartUpdated(e: EventMessagePartUpdated, ctx: Handle
355431 }
356432
357433 const sizeAttr = success
358- ? { tool_result_size_bytes : Buffer . byteLength ( ( toolPart . state as { output : string } ) . output , "utf8" ) }
434+ ? {
435+ tool_result_size_bytes : Buffer . byteLength (
436+ ( toolPart . state as { output : string } ) . output ,
437+ "utf8" ,
438+ ) ,
439+ }
359440 : { error : ( toolPart . state as { error : string } ) . error }
360441
361442 ctx . logger . emit ( {
@@ -407,11 +488,6 @@ export function startMessageSpan(
407488 if ( ! isTraceEnabled ( "llm" , ctx ) ) return
408489 const msgKey = `${ sessionID } :${ messageID } `
409490 if ( ctx . messageSpans . has ( msgKey ) ) return
410- const sessionSpan = ctx . sessionSpans . get ( sessionID )
411- const parentCtx = sessionSpan
412- ? trace . setSpan ( context . active ( ) , sessionSpan )
413- : context . active ( )
414-
415491 const msgSpan = ctx . tracer . startSpan (
416492 `${ ctx . tracePrefix } llm` ,
417493 {
@@ -428,13 +504,15 @@ export function startMessageSpan(
428504 ? {
429505 [ INPUT_VALUE ] : ctx . sessionInputs . get ( sessionID ) ! ,
430506 [ INPUT_MIME_TYPE ] : MimeType . TEXT ,
431- [ LLM_INPUT_MESSAGES ] : JSON . stringify ( [ { role : "user" , content : ctx . sessionInputs . get ( sessionID ) ! } ] ) ,
507+ [ LLM_INPUT_MESSAGES ] : JSON . stringify ( [
508+ { role : "user" , content : ctx . sessionInputs . get ( sessionID ) ! } ,
509+ ] ) ,
432510 }
433511 : { } ) ,
434512 ...ctx . commonAttrs ,
435513 } ,
436514 } ,
437- parentCtx ,
515+ resolveSessionTraceContext ( sessionID , ctx ) ?? context . active ( ) ,
438516 )
439517 setBoundedMap ( ctx . messageSpans , msgKey , msgSpan )
440518}
0 commit comments