1414
1515from typing import List , Optional , Union , Dict , Any
1616from agentops .client import Client
17- from agentops .sdk .core import TracingCore , TraceContext
18- from agentops .sdk .decorators import trace , session , agent , task , workflow , operation
17+ from agentops .sdk .core import TraceContext , tracer
18+ from agentops .sdk .decorators import trace , session , agent , task , workflow , operation , tool
19+ from agentops .enums import TraceState , SUCCESS , ERROR , UNSET
20+ from opentelemetry .trace .status import StatusCode
1921
2022from agentops .logging .config import logger
23+ import threading
2124
22- # Client global instance; one per process runtime
23- _client = Client ()
25+ # Thread-safe client management
26+ _client_lock = threading .Lock ()
27+ _client = None
2428
2529
2630def get_client () -> Client :
27- """Get the singleton client instance"""
31+ """Get the singleton client instance in a thread-safe manner """
2832 global _client
2933
34+ # Double-checked locking pattern for thread safety
35+ if _client is None :
36+ with _client_lock :
37+ if _client is None :
38+ _client = Client ()
39+
3040 return _client
3141
3242
@@ -106,24 +116,31 @@ def init(
106116 elif default_tags :
107117 merged_tags = default_tags
108118
109- return _client .init (
110- api_key = api_key ,
111- endpoint = endpoint ,
112- app_url = app_url ,
113- max_wait_time = max_wait_time ,
114- max_queue_size = max_queue_size ,
115- default_tags = merged_tags ,
116- trace_name = trace_name ,
117- instrument_llm_calls = instrument_llm_calls ,
118- auto_start_session = auto_start_session ,
119- auto_init = auto_init ,
120- skip_auto_end_session = skip_auto_end_session ,
121- env_data_opt_out = env_data_opt_out ,
122- log_level = log_level ,
123- fail_safe = fail_safe ,
124- exporter_endpoint = exporter_endpoint ,
119+ # Prepare initialization arguments
120+ init_kwargs = {
121+ "api_key" : api_key ,
122+ "endpoint" : endpoint ,
123+ "app_url" : app_url ,
124+ "max_wait_time" : max_wait_time ,
125+ "max_queue_size" : max_queue_size ,
126+ "default_tags" : merged_tags ,
127+ "trace_name" : trace_name ,
128+ "instrument_llm_calls" : instrument_llm_calls ,
129+ "auto_start_session" : auto_start_session ,
130+ "auto_init" : auto_init ,
131+ "skip_auto_end_session" : skip_auto_end_session ,
132+ "env_data_opt_out" : env_data_opt_out ,
133+ "log_level" : log_level ,
134+ "fail_safe" : fail_safe ,
135+ "exporter_endpoint" : exporter_endpoint ,
125136 ** kwargs ,
126- )
137+ }
138+
139+ # Get the current client instance (creates new one if needed)
140+ client = get_client ()
141+
142+ # Initialize the client directly
143+ return client .init (** init_kwargs )
127144
128145
129146def configure (** kwargs ):
@@ -173,7 +190,8 @@ def configure(**kwargs):
173190 if invalid_params :
174191 logger .warning (f"Invalid configuration parameters: { invalid_params } " )
175192
176- _client .configure (** kwargs )
193+ client = get_client ()
194+ client .configure (** kwargs )
177195
178196
179197def start_trace (
@@ -190,25 +208,26 @@ def start_trace(
190208 Returns:
191209 A TraceContext object containing the span and context token, or None if SDK not initialized.
192210 """
193- tracing_core = TracingCore .get_instance ()
194- if not tracing_core .initialized :
211+ if not tracer .initialized :
195212 # Optionally, attempt to initialize the client if not already, or log a more severe warning.
196213 # For now, align with legacy start_session that would try to init.
197214 # However, explicit init is preferred before starting traces.
198215 logger .warning ("AgentOps SDK not initialized. Attempting to initialize with defaults before starting trace." )
199216 try :
200217 init () # Attempt to initialize with environment variables / defaults
201- if not tracing_core .initialized :
218+ if not tracer .initialized :
202219 logger .error ("SDK initialization failed. Cannot start trace." )
203220 return None
204221 except Exception as e :
205222 logger .error (f"SDK auto-initialization failed during start_trace: { e } . Cannot start trace." )
206223 return None
207224
208- return tracing_core .start_trace (trace_name = trace_name , tags = tags )
225+ return tracer .start_trace (trace_name = trace_name , tags = tags )
209226
210227
211- def end_trace (trace_context : Optional [TraceContext ] = None , end_state : str = "Success" ) -> None :
228+ def end_trace (
229+ trace_context : Optional [TraceContext ] = None , end_state : Union [TraceState , StatusCode , str ] = TraceState .SUCCESS
230+ ) -> None :
212231 """
213232 Ends a trace (its root span) and finalizes it.
214233 If no trace_context is provided, ends all active session spans.
@@ -217,11 +236,10 @@ def end_trace(trace_context: Optional[TraceContext] = None, end_state: str = "Su
217236 trace_context: The TraceContext object returned by start_trace. If None, ends all active traces.
218237 end_state: The final state of the trace (e.g., "Success", "Failure", "Error").
219238 """
220- tracing_core = TracingCore .get_instance ()
221- if not tracing_core .initialized :
239+ if not tracer .initialized :
222240 logger .warning ("AgentOps SDK not initialized. Cannot end trace." )
223241 return
224- tracing_core .end_trace (trace_context = trace_context , end_state = end_state )
242+ tracer .end_trace (trace_context = trace_context , end_state = end_state )
225243
226244
227245__all__ = [
@@ -247,4 +265,13 @@ def end_trace(trace_context: Optional[TraceContext] = None, end_state: str = "Su
247265 "task" ,
248266 "workflow" ,
249267 "operation" ,
268+ "tracer" ,
269+ "tool" ,
270+ # Trace state enums
271+ "TraceState" ,
272+ "SUCCESS" ,
273+ "ERROR" ,
274+ "UNSET" ,
275+ # OpenTelemetry status codes (for advanced users)
276+ "StatusCode" ,
250277]
0 commit comments