1414
1515from __future__ import annotations
1616
17- from typing import Any , Optional , Sequence
17+ from typing import Any , Optional , cast
1818from uuid import UUID
1919
2020from langchain_core .callbacks import BaseCallbackHandler
2424from opentelemetry .instrumentation .langchain .invocation_manager import (
2525 _InvocationManager ,
2626)
27- from opentelemetry .semconv ._incubating .attributes import (
28- gen_ai_attributes as GenAI ,
29- )
3027from opentelemetry .util .genai .handler import TelemetryHandler
3128from opentelemetry .util .genai .types import (
32- ContentCapturingMode ,
3329 Error ,
3430 InputMessage ,
3531 LLMInvocation ,
32+ MessagePart ,
3633 OutputMessage ,
3734 Text ,
3835)
39- from opentelemetry .util .genai .utils import (
40- get_content_capturing_mode ,
41- is_experimental_mode ,
42- )
43-
44- GEN_AI_MEMORY_STORE_ID = getattr (
45- GenAI , "GEN_AI_MEMORY_STORE_ID" , "gen_ai.memory.store.id"
46- )
47- GEN_AI_MEMORY_STORE_NAME = getattr (
48- GenAI , "GEN_AI_MEMORY_STORE_NAME" , "gen_ai.memory.store.name"
49- )
50- GEN_AI_MEMORY_QUERY = getattr (
51- GenAI , "GEN_AI_MEMORY_QUERY" , "gen_ai.memory.query"
52- )
53- GEN_AI_MEMORY_SEARCH_RESULT_COUNT = getattr (
54- GenAI ,
55- "GEN_AI_MEMORY_SEARCH_RESULT_COUNT" ,
56- "gen_ai.memory.search.result.count" ,
57- )
58- GEN_AI_MEMORY_NAMESPACE = getattr (
59- GenAI , "GEN_AI_MEMORY_NAMESPACE" , "gen_ai.memory.namespace"
60- )
61-
62- _SEARCH_MEMORY_MEMBER = getattr (
63- getattr (GenAI , "GenAiOperationNameValues" , object ()),
64- "SEARCH_MEMORY" ,
65- None ,
66- )
67- SEARCH_MEMORY_OPERATION = (
68- _SEARCH_MEMORY_MEMBER .value
69- if _SEARCH_MEMORY_MEMBER is not None
70- else "search_memory"
71- )
72-
73- _RETRIEVAL_MEMBER = getattr (
74- getattr (GenAI , "GenAiOperationNameValues" , object ()),
75- "RETRIEVAL" ,
76- None ,
77- )
78- RETRIEVAL_OPERATION = (
79- _RETRIEVAL_MEMBER .value if _RETRIEVAL_MEMBER is not None else "retrieval"
80- )
8136
8237
8338class OpenTelemetryLangChainCallbackHandler (BaseCallbackHandler ):
@@ -90,62 +45,6 @@ def __init__(self, telemetry_handler: TelemetryHandler) -> None:
9045 self ._telemetry_handler = telemetry_handler
9146 self ._invocation_manager = _InvocationManager ()
9247
93- @staticmethod
94- def _resolve_retriever_store_name (
95- serialized : dict [str , Any ],
96- metadata : Optional [dict [str , Any ]],
97- ) -> Optional [str ]:
98- if metadata and metadata .get ("memory_store_name" ):
99- return str (metadata ["memory_store_name" ])
100- if metadata and metadata .get ("ls_retriever_name" ):
101- return str (metadata ["ls_retriever_name" ])
102- name = serialized .get ("name" )
103- return str (name ) if isinstance (name , str ) and name else None
104-
105- @staticmethod
106- def _resolve_retriever_store_id (
107- serialized : dict [str , Any ],
108- metadata : Optional [dict [str , Any ]],
109- ) -> Optional [str ]:
110- if metadata and metadata .get ("memory_store_id" ):
111- return str (metadata ["memory_store_id" ])
112-
113- serialized_id = serialized .get ("id" )
114- if isinstance (serialized_id , str ) and serialized_id :
115- return serialized_id
116- if isinstance (serialized_id , list ) and serialized_id :
117- try :
118- return "." .join (str (part ) for part in serialized_id )
119- except TypeError :
120- return None
121- return None
122-
123- @staticmethod
124- def _should_capture_memory_query () -> bool :
125- if not is_experimental_mode ():
126- return False
127- try :
128- mode = get_content_capturing_mode ()
129- except ValueError :
130- return False
131- return mode in (
132- ContentCapturingMode .SPAN_ONLY ,
133- ContentCapturingMode .SPAN_AND_EVENT ,
134- )
135-
136- @staticmethod
137- def _is_memory_retriever (
138- metadata : Optional [dict [str , Any ]],
139- ) -> bool :
140- """Detect if a retriever is a memory retriever based on metadata hints."""
141- if not metadata :
142- return False
143- return bool (
144- metadata .get ("memory_store_name" )
145- or metadata .get ("memory_store_id" )
146- or metadata .get ("is_memory_retriever" )
147- )
148-
14948 def on_chat_model_start (
15049 self ,
15150 serialized : dict [str , Any ],
@@ -235,7 +134,11 @@ def on_chat_model_start(
235134 Text (content = text_value , type = "text" )
236135 )
237136
238- input_messages .append (InputMessage (parts = parts , role = role ))
137+ input_messages .append (
138+ InputMessage (
139+ parts = cast (list [MessagePart ], parts ), role = role
140+ )
141+ )
239142
240143 llm_invocation = LLMInvocation (
241144 request_model = request_model ,
@@ -308,7 +211,7 @@ def on_llm_end(
308211 role = chat_generation .message .type
309212 output_message = OutputMessage (
310213 role = role ,
311- parts = parts ,
214+ parts = cast ( list [ MessagePart ], parts ) ,
312215 finish_reason = finish_reason ,
313216 )
314217 output_messages .append (output_message )
@@ -370,100 +273,3 @@ def on_llm_error(
370273 )
371274 if llm_invocation .span and not llm_invocation .span .is_recording ():
372275 self ._invocation_manager .delete_invocation_state (run_id = run_id )
373-
374- def on_retriever_start (
375- self ,
376- serialized : dict [str , Any ],
377- query : str ,
378- * ,
379- run_id : UUID ,
380- parent_run_id : Optional [UUID ] = None ,
381- tags : Optional [list [str ]] = None ,
382- metadata : Optional [dict [str , Any ]] = None ,
383- ** kwargs : Any ,
384- ) -> None :
385- provider = "unknown"
386- if metadata is not None :
387- provider = metadata .get ("ls_provider" , "unknown" )
388-
389- attributes : dict [str , Any ] = {}
390- is_memory = self ._is_memory_retriever (metadata )
391- operation = (
392- SEARCH_MEMORY_OPERATION if is_memory else RETRIEVAL_OPERATION
393- )
394-
395- if store_name := self ._resolve_retriever_store_name (
396- serialized , metadata
397- ):
398- attributes [GEN_AI_MEMORY_STORE_NAME ] = store_name
399- if store_id := self ._resolve_retriever_store_id (serialized , metadata ):
400- attributes [GEN_AI_MEMORY_STORE_ID ] = store_id
401- if (
402- query
403- and self ._should_capture_memory_query ()
404- and isinstance (query , str )
405- ):
406- attributes [GEN_AI_MEMORY_QUERY ] = query
407- if metadata and metadata .get ("memory_namespace" ):
408- attributes [GEN_AI_MEMORY_NAMESPACE ] = metadata ["memory_namespace" ]
409-
410- llm_invocation = LLMInvocation (
411- request_model = "" ,
412- provider = provider ,
413- operation_name = operation ,
414- attributes = attributes ,
415- )
416- llm_invocation = self ._telemetry_handler .start_llm (
417- invocation = llm_invocation
418- )
419- if llm_invocation .span and store_name :
420- llm_invocation .span .update_name (f"{ operation } { store_name } " )
421- self ._invocation_manager .add_invocation_state (
422- run_id = run_id ,
423- parent_run_id = parent_run_id ,
424- invocation = llm_invocation ,
425- )
426-
427- def on_retriever_end (
428- self ,
429- documents : Sequence [Any ],
430- * ,
431- run_id : UUID ,
432- parent_run_id : Optional [UUID ] = None ,
433- ** kwargs : Any ,
434- ) -> None :
435- llm_invocation = self ._invocation_manager .get_invocation (run_id = run_id )
436- if llm_invocation is None or not isinstance (
437- llm_invocation , LLMInvocation
438- ):
439- return
440-
441- llm_invocation .attributes [GEN_AI_MEMORY_SEARCH_RESULT_COUNT ] = len (
442- documents
443- )
444- llm_invocation = self ._telemetry_handler .stop_llm (
445- invocation = llm_invocation
446- )
447- if llm_invocation .span and not llm_invocation .span .is_recording ():
448- self ._invocation_manager .delete_invocation_state (run_id = run_id )
449-
450- def on_retriever_error (
451- self ,
452- error : BaseException ,
453- * ,
454- run_id : UUID ,
455- parent_run_id : Optional [UUID ] = None ,
456- ** kwargs : Any ,
457- ) -> None :
458- llm_invocation = self ._invocation_manager .get_invocation (run_id = run_id )
459- if llm_invocation is None or not isinstance (
460- llm_invocation , LLMInvocation
461- ):
462- return
463-
464- error_otel = Error (message = str (error ), type = type (error ))
465- llm_invocation = self ._telemetry_handler .fail_llm (
466- invocation = llm_invocation , error = error_otel
467- )
468- if llm_invocation .span and not llm_invocation .span .is_recording ():
469- self ._invocation_manager .delete_invocation_state (run_id = run_id )
0 commit comments