3535from agentops .logging import logger
3636from agentops .sdk .core import tracer
3737
38+
39+ # Define the structure for instrumentor configurations
40+ class InstrumentorConfig (TypedDict ):
41+ module_name : str
42+ class_name : str
43+ min_version : str
44+ package_name : NotRequired [str ] # Optional: actual pip package name if different from module
45+
46+
47+ # Configuration for supported LLM providers
48+ PROVIDERS : dict [str , InstrumentorConfig ] = {
49+ "openai" : {
50+ "module_name" : "agentops.instrumentation.openai" ,
51+ "class_name" : "OpenAIInstrumentor" ,
52+ "min_version" : "1.0.0" ,
53+ },
54+ "anthropic" : {
55+ "module_name" : "agentops.instrumentation.anthropic" ,
56+ "class_name" : "AnthropicInstrumentor" ,
57+ "min_version" : "0.32.0" ,
58+ },
59+ "ibm_watsonx_ai" : {
60+ "module_name" : "agentops.instrumentation.ibm_watsonx_ai" ,
61+ "class_name" : "IBMWatsonXInstrumentor" ,
62+ "min_version" : "0.1.0" ,
63+ },
64+ "google.genai" : {
65+ "module_name" : "agentops.instrumentation.google_genai" ,
66+ "class_name" : "GoogleGenAIInstrumentor" ,
67+ "min_version" : "0.1.0" ,
68+ "package_name" : "google-genai" , # Actual pip package name
69+ },
70+ }
71+
72+ # Configuration for utility instrumentors
73+ UTILITY_INSTRUMENTORS : dict [str , InstrumentorConfig ] = {
74+ "concurrent.futures" : {
75+ "module_name" : "agentops.instrumentation.concurrent_futures" ,
76+ "class_name" : "ConcurrentFuturesInstrumentor" ,
77+ "min_version" : "3.7.0" , # Python 3.7+ (concurrent.futures is stdlib)
78+ "package_name" : "python" , # Special case for stdlib modules
79+ },
80+ }
81+
82+ # Configuration for supported agentic libraries
83+ AGENTIC_LIBRARIES : dict [str , InstrumentorConfig ] = {
84+ "crewai" : {
85+ "module_name" : "agentops.instrumentation.crewai" ,
86+ "class_name" : "CrewAIInstrumentor" ,
87+ "min_version" : "0.56.0" ,
88+ },
89+ "autogen" : {"module_name" : "agentops.instrumentation.ag2" , "class_name" : "AG2Instrumentor" , "min_version" : "0.1.0" },
90+ "agents" : {
91+ "module_name" : "agentops.instrumentation.openai_agents" ,
92+ "class_name" : "OpenAIAgentsInstrumentor" ,
93+ "min_version" : "0.0.1" ,
94+ },
95+ "google.adk" : {
96+ "module_name" : "agentops.instrumentation.google_adk" ,
97+ "class_name" : "GoogleADKInstrumentor" ,
98+ "min_version" : "0.1.0" ,
99+ },
100+ }
101+
102+ # Combine all target packages for monitoring
103+ TARGET_PACKAGES = set (PROVIDERS .keys ()) | set (AGENTIC_LIBRARIES .keys ()) | set (UTILITY_INSTRUMENTORS .keys ())
104+
105+ # Create a single instance of the manager
106+ # _manager = InstrumentationManager() # Removed
107+
38108# Module-level state variables
39109_active_instrumentors : list [BaseInstrumentor ] = []
40110_original_builtins_import = builtins .__import__ # Store original import
@@ -49,6 +119,16 @@ def _is_installed_package(module_obj: ModuleType, package_name_key: str) -> bool
49119 rather than a local module, especially when names might collide.
50120 `package_name_key` is the key from TARGET_PACKAGES (e.g., 'agents', 'google.adk').
51121 """
122+ # Special case for stdlib modules (marked with package_name="python" in UTILITY_INSTRUMENTORS)
123+ if (
124+ package_name_key in UTILITY_INSTRUMENTORS
125+ and UTILITY_INSTRUMENTORS [package_name_key ].get ("package_name" ) == "python"
126+ ):
127+ logger .debug (
128+ f"_is_installed_package: Module '{ package_name_key } ' is a Python standard library module. Considering it an installed package."
129+ )
130+ return True
131+
52132 if not hasattr (module_obj , "__file__" ) or not module_obj .__file__ :
53133 logger .debug (
54134 f"_is_installed_package: Module '{ package_name_key } ' has no __file__, assuming it might be an SDK namespace package. Returning True."
@@ -141,7 +221,7 @@ def _uninstrument_providers():
141221def _should_instrument_package (package_name : str ) -> bool :
142222 """
143223 Determine if a package should be instrumented based on current state.
144- Handles special cases for agentic libraries and providers .
224+ Handles special cases for agentic libraries, providers, and utility instrumentors .
145225 """
146226 global _has_agentic_library
147227
@@ -150,6 +230,12 @@ def _should_instrument_package(package_name: str) -> bool:
150230 logger .debug (f"_should_instrument_package: '{ package_name } ' already instrumented by AgentOps. Skipping." )
151231 return False
152232
233+ # Utility instrumentors should always be instrumented regardless of agentic library state
234+ if package_name in UTILITY_INSTRUMENTORS :
235+ logger .debug (f"_should_instrument_package: '{ package_name } ' is a utility instrumentor. Always allowing." )
236+ return True
237+
238+ # Only apply agentic/provider logic if it's NOT a utility instrumentor
153239 is_target_agentic = package_name in AGENTIC_LIBRARIES
154240 is_target_provider = package_name in PROVIDERS
155241
@@ -198,14 +284,18 @@ def _perform_instrumentation(package_name: str):
198284 return
199285
200286 # Get the appropriate configuration for the package
201- # Ensure package_name is a key in either PROVIDERS or AGENTIC_LIBRARIES
202- if package_name not in PROVIDERS and package_name not in AGENTIC_LIBRARIES :
287+ # Ensure package_name is a key in either PROVIDERS, AGENTIC_LIBRARIES, or UTILITY_INSTRUMENTORS
288+ if (
289+ package_name not in PROVIDERS
290+ and package_name not in AGENTIC_LIBRARIES
291+ and package_name not in UTILITY_INSTRUMENTORS
292+ ):
203293 logger .debug (
204- f"_perform_instrumentation: Package '{ package_name } ' not found in PROVIDERS or AGENTIC_LIBRARIES . Skipping."
294+ f"_perform_instrumentation: Package '{ package_name } ' not found in PROVIDERS, AGENTIC_LIBRARIES, or UTILITY_INSTRUMENTORS . Skipping."
205295 )
206296 return
207297
208- config = PROVIDERS .get (package_name ) or AGENTIC_LIBRARIES [package_name ]
298+ config = PROVIDERS .get (package_name ) or AGENTIC_LIBRARIES . get ( package_name ) or UTILITY_INSTRUMENTORS [package_name ]
209299 loader = InstrumentorLoader (** config )
210300
211301 # instrument_one already checks loader.should_activate
@@ -327,74 +417,6 @@ def _import_monitor(name: str, globals_dict=None, locals_dict=None, fromlist=(),
327417 return module
328418
329419
330- # Define the structure for instrumentor configurations
331- class InstrumentorConfig (TypedDict ):
332- module_name : str
333- class_name : str
334- min_version : str
335- package_name : NotRequired [str ] # Optional: actual pip package name if different from module
336-
337-
338- # Configuration for supported LLM providers
339- PROVIDERS : dict [str , InstrumentorConfig ] = {
340- "openai" : {
341- "module_name" : "agentops.instrumentation.openai" ,
342- "class_name" : "OpenAIInstrumentor" ,
343- "min_version" : "1.0.0" ,
344- "package_name" : "openai" , # Actual pip package name
345- },
346- "anthropic" : {
347- "module_name" : "agentops.instrumentation.anthropic" ,
348- "class_name" : "AnthropicInstrumentor" ,
349- "min_version" : "0.32.0" ,
350- "package_name" : "anthropic" , # Actual pip package name
351- },
352- "ibm_watsonx_ai" : {
353- "module_name" : "agentops.instrumentation.ibm_watsonx_ai" ,
354- "class_name" : "IBMWatsonXInstrumentor" ,
355- "min_version" : "0.1.0" ,
356- "package_name" : "ibm-watsonx-ai" , # Actual pip package name
357- },
358- "google.genai" : {
359- "module_name" : "agentops.instrumentation.google_genai" ,
360- "class_name" : "GoogleGenAIInstrumentor" ,
361- "min_version" : "0.1.0" ,
362- "package_name" : "google-genai" , # Actual pip package name
363- },
364- }
365-
366- # Configuration for supported agentic libraries
367- AGENTIC_LIBRARIES : dict [str , InstrumentorConfig ] = {
368- "crewai" : {
369- "module_name" : "agentops.instrumentation.crewai" ,
370- "class_name" : "CrewAIInstrumentor" ,
371- "min_version" : "0.56.0" ,
372- "package_name" : "crewai" , # Actual pip package name
373- },
374- "autogen" : {"module_name" : "agentops.instrumentation.ag2" , "class_name" : "AG2Instrumentor" , "min_version" : "0.1.0" },
375- "agents" : {
376- "module_name" : "agentops.instrumentation.openai_agents" ,
377- "class_name" : "OpenAIAgentsInstrumentor" ,
378- "min_version" : "0.0.1" ,
379- "package_name" : "openai-agents" ,
380- },
381- "google.adk" : {
382- "module_name" : "agentops.instrumentation.google_adk" ,
383- "class_name" : "GoogleADKInstrumentor" ,
384- "min_version" : "0.1.0" ,
385- "package_name" : "google-adk" , # Actual pip package name
386- },
387- "agno" : {
388- "module_name" : "agentops.instrumentation.agno" ,
389- "class_name" : "AgnoInstrumentor" ,
390- "min_version" : "0.1.0" ,
391- },
392- }
393-
394- # Combine all target packages for monitoring
395- TARGET_PACKAGES = set (PROVIDERS .keys ()) | set (AGENTIC_LIBRARIES .keys ())
396-
397-
398420@dataclass
399421class InstrumentorLoader :
400422 """
@@ -416,6 +438,13 @@ def module(self) -> ModuleType:
416438 def should_activate (self ) -> bool :
417439 """Check if the package is available and meets version requirements."""
418440 try :
441+ # Special case for stdlib modules (like concurrent.futures)
442+ if self .package_name == "python" :
443+ import sys
444+
445+ python_version = f"{ sys .version_info .major } .{ sys .version_info .minor } .{ sys .version_info .micro } "
446+ return Version (python_version ) >= parse (self .min_version )
447+
419448 # Use explicit package_name if provided, otherwise derive from module_name
420449 if self .package_name :
421450 provider_name = self .package_name
0 commit comments