3535from traceloop .sdk .utils import is_notebook
3636from traceloop .sdk .utils .package_check import is_package_installed
3737from typing import Callable , Dict , List , Optional , Set , Union
38- from opentelemetry .semconv ._incubating .attributes .gen_ai_attributes import GEN_AI_AGENT_NAME
38+ from opentelemetry .semconv ._incubating .attributes .gen_ai_attributes import (
39+ GEN_AI_AGENT_NAME ,
40+ )
3941
4042
4143TRACER_NAME = "traceloop.tracer"
@@ -87,15 +89,19 @@ def __new__(
8789
8890 obj .__image_uploader = image_uploader
8991 obj .__resource = Resource .create (TracerWrapper .resource_attributes )
90- obj .__tracer_provider = init_tracer_provider (resource = obj .__resource , sampler = sampler )
92+ obj .__tracer_provider = init_tracer_provider (
93+ resource = obj .__resource , sampler = sampler
94+ )
9195
9296 # Handle multiple processors case
9397 if processor is not None and isinstance (processor , list ):
9498 obj .__spans_processors = []
9599 for proc in processor :
96100 original_on_start = proc .on_start
97101
98- def chained_on_start (span , parent_context = None , orig = original_on_start ):
102+ def chained_on_start (
103+ span , parent_context = None , orig = original_on_start
104+ ):
99105 if orig :
100106 orig (span , parent_context )
101107 obj ._span_processor_on_start (span , parent_context )
@@ -105,7 +111,9 @@ def chained_on_start(span, parent_context=None, orig=original_on_start):
105111
106112 obj .__tracer_provider .add_span_processor (proc )
107113
108- Telemetry ().capture ("tracer:init" , {"processor" : "multiple" , "count" : len (processor )})
114+ Telemetry ().capture (
115+ "tracer:init" , {"processor" : "multiple" , "count" : len (processor )}
116+ )
109117
110118 # Handle single processor case (backward compatibility)
111119 elif processor is not None :
@@ -142,8 +150,7 @@ def chained_on_start(span, parent_context=None, orig=original_on_start):
142150 )
143151
144152 obj .__spans_processor = get_default_span_processor (
145- disable_batch = disable_batch ,
146- exporter = exporter
153+ disable_batch = disable_batch , exporter = exporter
147154 )
148155
149156 if span_postprocess_callback :
@@ -155,6 +162,7 @@ def wrapped_on_end(span):
155162 span_postprocess_callback (span )
156163 # Then call the original to ensure normal processing
157164 original_on_end (span )
165+
158166 obj .__spans_processor .on_end = wrapped_on_end
159167
160168 obj .__spans_processor .on_start = obj ._span_processor_on_start
@@ -231,9 +239,9 @@ def set_disabled(cls, disabled: bool) -> None:
231239 cls .__disabled = disabled
232240
233241 def flush (self ):
234- if hasattr (self , ' _TracerWrapper__spans_processor' ):
242+ if hasattr (self , " _TracerWrapper__spans_processor" ):
235243 self .__spans_processor .force_flush ()
236- elif hasattr (self , ' _TracerWrapper__spans_processors' ):
244+ elif hasattr (self , " _TracerWrapper__spans_processors" ):
237245 for processor in self .__spans_processors :
238246 processor .force_flush ()
239247
@@ -368,7 +376,9 @@ def default_span_processor_on_start(span: Span, parent_context: Context | None =
368376 )
369377
370378 prompt_template_variables = get_value ("prompt_template_variables" )
371- if prompt_template_variables is not None and isinstance (prompt_template_variables , dict ):
379+ if prompt_template_variables is not None and isinstance (
380+ prompt_template_variables , dict
381+ ):
372382 for key , value in prompt_template_variables .items ():
373383 span .set_attribute (
374384 f"{ SpanAttributes .TRACELOOP_PROMPT_TEMPLATE_VARIABLES } .{ key } " ,
@@ -380,7 +390,7 @@ def get_default_span_processor(
380390 disable_batch : bool = False ,
381391 api_endpoint : Optional [str ] = None ,
382392 headers : Optional [Dict [str , str ]] = None ,
383- exporter : Optional [SpanExporter ] = None
393+ exporter : Optional [SpanExporter ] = None ,
384394) -> SpanProcessor :
385395 """
386396 Creates and returns the default Traceloop span processor.
@@ -409,7 +419,9 @@ def get_default_span_processor(
409419 return processor
410420
411421
412- def init_tracer_provider (resource : Resource , sampler : Optional [Sampler ] = None ) -> TracerProvider :
422+ def init_tracer_provider (
423+ resource : Resource , sampler : Optional [Sampler ] = None
424+ ) -> TracerProvider :
413425 provider : TracerProvider = None
414426 default_provider : TracerProvider = get_tracer_provider ()
415427
@@ -438,16 +450,17 @@ def init_instrumentations(
438450):
439451 block_instruments = block_instruments or set ()
440452 # explictly test for None since empty set is a False value
441- instruments = instruments if instruments is not None else set (
442- Instruments
443- )
453+ instruments = instruments if instruments is not None else set (Instruments )
444454
445455 # Remove any instruments that were explicitly blocked
446456 instruments = instruments - block_instruments
447457
448458 instrument_set = False
449459 for instrument in instruments :
450- if instrument == Instruments .ALEPHALPHA :
460+ if instrument == Instruments .AGNO :
461+ if init_agno_instrumentor ():
462+ instrument_set = True
463+ elif instrument == Instruments .ALEPHALPHA :
451464 if init_alephalpha_instrumentor ():
452465 instrument_set = True
453466 elif instrument == Instruments .ANTHROPIC :
@@ -468,7 +481,9 @@ def init_instrumentations(
468481 if init_crewai_instrumentor ():
469482 instrument_set = True
470483 elif instrument == Instruments .GOOGLE_GENERATIVEAI :
471- if init_google_generativeai_instrumentor (should_enrich_metrics , base64_image_uploader ):
484+ if init_google_generativeai_instrumentor (
485+ should_enrich_metrics , base64_image_uploader
486+ ):
472487 instrument_set = True
473488 elif instrument == Instruments .GROQ :
474489 if init_groq_instrumentor ():
@@ -537,9 +552,7 @@ def init_instrumentations(
537552 if init_urllib3_instrumentor ():
538553 instrument_set = True
539554 elif instrument == Instruments .VERTEXAI :
540- if init_vertexai_instrumentor (
541- should_enrich_metrics , base64_image_uploader
542- ):
555+ if init_vertexai_instrumentor (should_enrich_metrics , base64_image_uploader ):
543556 instrument_set = True
544557 elif instrument == Instruments .WATSONX :
545558 if init_watsonx_instrumentor ():
@@ -572,7 +585,8 @@ def init_instrumentations(
572585
573586
574587def init_openai_instrumentor (
575- should_enrich_metrics : bool , base64_image_uploader : Callable [[str , str , str , str ], str ]
588+ should_enrich_metrics : bool ,
589+ base64_image_uploader : Callable [[str , str , str , str ], str ],
576590):
577591 try :
578592 if is_package_installed ("openai" ):
@@ -596,7 +610,8 @@ def init_openai_instrumentor(
596610
597611
598612def init_anthropic_instrumentor (
599- should_enrich_metrics : bool , base64_image_uploader : Callable [[str , str , str , str ], str ]
613+ should_enrich_metrics : bool ,
614+ base64_image_uploader : Callable [[str , str , str , str ], str ],
600615):
601616 try :
602617 if is_package_installed ("anthropic" ):
@@ -656,7 +671,9 @@ def init_pinecone_instrumentor():
656671
657672def init_qdrant_instrumentor ():
658673 try :
659- if is_package_installed ("qdrant_client" ) or is_package_installed ("qdrant-client" ):
674+ if is_package_installed ("qdrant_client" ) or is_package_installed (
675+ "qdrant-client"
676+ ):
660677 Telemetry ().capture ("instrumentation:qdrant:init" )
661678 from opentelemetry .instrumentation .qdrant import QdrantInstrumentor
662679
@@ -691,10 +708,13 @@ def init_chroma_instrumentor():
691708
692709
693710def init_google_generativeai_instrumentor (
694- should_enrich_metrics : bool , base64_image_uploader : Callable [[str , str , str , str ], str ]
711+ should_enrich_metrics : bool ,
712+ base64_image_uploader : Callable [[str , str , str , str ], str ],
695713):
696714 try :
697- if is_package_installed ("google-generativeai" ) or is_package_installed ("google-genai" ):
715+ if is_package_installed ("google-generativeai" ) or is_package_installed (
716+ "google-genai"
717+ ):
698718 Telemetry ().capture ("instrumentation:gemini:init" )
699719 from opentelemetry .instrumentation .google_generativeai import (
700720 GoogleGenerativeAiInstrumentor ,
@@ -954,7 +974,8 @@ def init_replicate_instrumentor():
954974
955975
956976def init_vertexai_instrumentor (
957- should_enrich_metrics : bool , base64_image_uploader : Callable [[str , str , str , str ], str ]
977+ should_enrich_metrics : bool ,
978+ base64_image_uploader : Callable [[str , str , str , str ], str ],
958979):
959980 try :
960981 if is_package_installed ("google-cloud-aiplatform" ):
@@ -1030,6 +1051,24 @@ def init_writer_instrumentor():
10301051 return False
10311052
10321053
1054+ def init_agno_instrumentor ():
1055+ try :
1056+ if is_package_installed ("agno" ):
1057+ Telemetry ().capture ("instrumentation:agno:init" )
1058+ from opentelemetry .instrumentation .agno import AgnoInstrumentor
1059+
1060+ instrumentor = AgnoInstrumentor (
1061+ exception_logger = lambda e : Telemetry ().log_exception (e ),
1062+ )
1063+ if not instrumentor .is_instrumented_by_opentelemetry :
1064+ instrumentor .instrument ()
1065+ return True
1066+ except Exception as e :
1067+ logging .error (f"Error initializing Agno instrumentor: { e } " )
1068+ Telemetry ().log_exception (e )
1069+ return False
1070+
1071+
10331072def init_alephalpha_instrumentor ():
10341073 try :
10351074 if is_package_installed ("aleph_alpha_client" ):
0 commit comments