@@ -230,6 +230,14 @@ def start(self, invocation: LLMInvocation):
230230 )
231231 if parent_state is not None :
232232 parent_state .children .append (invocation .run_id )
233+ span = self ._start_span (
234+ name = f"{ GenAI .GenAiOperationNameValues .CHAT .value } { invocation .request_model } " ,
235+ kind = SpanKind .CLIENT ,
236+ parent_run_id = invocation .parent_run_id ,
237+ )
238+ # Keep span active but do not end it here; it will be ended in finish()/error()
239+ with use_span (span , end_on_exit = False ):
240+ self .spans [invocation .run_id ] = _SpanState (span = span )
233241
234242 @contextmanager
235243 def _start_span_for_invocation (self , invocation : LLMInvocation ):
@@ -255,18 +263,39 @@ def _finalize_invocation(self, invocation: LLMInvocation) -> None:
255263 self ._end_span (invocation .run_id )
256264
257265 def finish (self , invocation : LLMInvocation ):
258- with self ._start_span_for_invocation (invocation ) as span :
259- _apply_common_span_attributes (span , invocation )
260- _maybe_set_span_messages (
261- span , invocation .messages , invocation .chat_generations
262- )
266+ state = self .spans .get (invocation .run_id )
267+ if state is None :
268+ with self ._start_span_for_invocation (invocation ) as span :
269+ _apply_common_span_attributes (span , invocation )
270+ _maybe_set_span_messages (
271+ span , invocation .messages , invocation .chat_generations
272+ )
273+ self ._finalize_invocation (invocation )
274+ return
275+
276+ span = state .span
277+ _apply_common_span_attributes (span , invocation )
278+ _maybe_set_span_messages (
279+ span , invocation .messages , invocation .chat_generations
280+ )
263281 self ._finalize_invocation (invocation )
264282
265283 def error (self , error : Error , invocation : LLMInvocation ):
266- with self ._start_span_for_invocation (invocation ) as span :
267- span .set_status (Status (StatusCode .ERROR , error .message ))
268- if span .is_recording ():
269- span .set_attribute (
270- ErrorAttributes .ERROR_TYPE , error .type .__qualname__
271- )
284+ state = self .spans .get (invocation .run_id )
285+ if state is None :
286+ with self ._start_span_for_invocation (invocation ) as span :
287+ span .set_status (Status (StatusCode .ERROR , error .message ))
288+ if span .is_recording ():
289+ span .set_attribute (
290+ ErrorAttributes .ERROR_TYPE , error .type .__qualname__
291+ )
292+ self ._finalize_invocation (invocation )
293+ return
294+
295+ span = state .span
296+ span .set_status (Status (StatusCode .ERROR , error .message ))
297+ if span .is_recording ():
298+ span .set_attribute (
299+ ErrorAttributes .ERROR_TYPE , error .type .__qualname__
300+ )
272301 self ._finalize_invocation (invocation )
0 commit comments