1
- import datetime
2
1
import logging
3
- import uuid
4
- from typing import Dict , Optional
2
+ from datetime import datetime , timezone
3
+ from typing import Dict , Optional , TypedDict
4
+ from uuid import uuid4
5
5
6
6
from langsmith import run_trees as rt
7
7
@@ -80,6 +80,13 @@ def __init__(self, *args, **kwargs):
80
80
81
81
if HAVE_AGENTS :
82
82
83
+ class RunData (TypedDict ):
84
+ id : str
85
+ trace_id : str
86
+ start_time : datetime
87
+ dotted_order : str
88
+ parent_run_id : Optional [str ]
89
+
83
90
class OpenAIAgentsTracingProcessor (tracing .TracingProcessor ): # type: ignore[no-redef]
84
91
"""Tracing processor for the `OpenAI Agents SDK <https://openai.github.io/openai-agents-python/>`_.
85
92
@@ -154,7 +161,7 @@ def __init__(
154
161
self ._first_response_inputs : dict = {}
155
162
self ._last_response_outputs : dict = {}
156
163
157
- self ._runs : Dict [str , str ] = {}
164
+ self ._runs : Dict [str , RunData ] = {}
158
165
159
166
def on_trace_start (self , trace : tracing .Trace ) -> None :
160
167
if self ._name :
@@ -163,8 +170,22 @@ def on_trace_start(self, trace: tracing.Trace) -> None:
163
170
run_name = trace .name
164
171
else :
165
172
run_name = "Agent workflow"
166
- trace_run_id = str (uuid .uuid4 ())
167
- self ._runs [trace .trace_id ] = trace_run_id
173
+ trace_run_id = str (uuid4 ())
174
+
175
+ start_time = datetime .now (timezone .utc )
176
+
177
+ dotted_order = agent_utils .ensure_dotted_order (
178
+ start_time = start_time ,
179
+ run_id = trace_run_id ,
180
+ )
181
+ self ._runs [trace .trace_id ] = RunData (
182
+ id = trace_run_id ,
183
+ trace_id = trace_run_id ,
184
+ start_time = start_time ,
185
+ dotted_order = dotted_order ,
186
+ parent_run_id = None ,
187
+ )
188
+
168
189
run_extra = {"metadata" : self ._metadata or {}}
169
190
170
191
trace_dict = trace .export () or {}
@@ -177,23 +198,30 @@ def on_trace_start(self, trace: tracing.Trace) -> None:
177
198
inputs = {},
178
199
run_type = "chain" ,
179
200
id = trace_run_id ,
201
+ trace_id = trace_run_id ,
202
+ dotted_order = dotted_order ,
203
+ start_time = start_time ,
180
204
revision_id = None ,
181
205
extra = run_extra ,
182
206
tags = self ._tags ,
183
207
project_name = self ._project_name ,
184
208
)
209
+
185
210
self .client .create_run (** run_data )
186
211
except Exception as e :
187
212
logger .exception (f"Error creating trace run: { e } " )
188
213
189
214
def on_trace_end (self , trace : tracing .Trace ) -> None :
190
- run_id = self ._runs .pop (trace .trace_id , None )
215
+ run = self ._runs .pop (trace .trace_id , None )
191
216
trace_dict = trace .export () or {}
192
217
metadata = {** (trace_dict .get ("metadata" ) or {}), ** (self ._metadata or {})}
193
- if run_id :
218
+
219
+ if run :
194
220
try :
195
221
self .client .update_run (
196
- run_id = run_id ,
222
+ run_id = run ["id" ],
223
+ trace_id = run ["trace_id" ],
224
+ dotted_order = run ["dotted_order" ],
197
225
inputs = self ._first_response_inputs .pop (trace .trace_id , {}),
198
226
outputs = self ._last_response_outputs .pop (trace .trace_id , {}),
199
227
extra = {"metadata" : metadata },
@@ -202,9 +230,38 @@ def on_trace_end(self, trace: tracing.Trace) -> None:
202
230
logger .exception (f"Error updating trace run: { e } " )
203
231
204
232
def on_span_start (self , span : tracing .Span ) -> None :
205
- parent_run_id = self ._runs .get (span .parent_id or span .trace_id )
206
- span_run_id = str (uuid .uuid4 ())
207
- self ._runs [span .span_id ] = span_run_id
233
+ parent_run = (
234
+ self ._runs .get (span .parent_id )
235
+ if span .parent_id
236
+ else self ._runs .get (span .trace_id )
237
+ )
238
+
239
+ if parent_run is None :
240
+ logger .warning (
241
+ f"No trace info found for span, skipping: { span .span_id } "
242
+ )
243
+ return
244
+
245
+ trace_id = parent_run ["trace_id" ]
246
+
247
+ span_run_id = str (uuid4 ())
248
+ span_start_time = (
249
+ datetime .fromisoformat (span .started_at )
250
+ if span .started_at
251
+ else datetime .now (timezone .utc )
252
+ )
253
+ dotted_order = agent_utils .ensure_dotted_order (
254
+ start_time = span_start_time ,
255
+ run_id = span_run_id ,
256
+ parent_dotted_order = parent_run ["dotted_order" ] if parent_run else None ,
257
+ )
258
+ self ._runs [span .span_id ] = RunData (
259
+ id = span_run_id ,
260
+ trace_id = trace_id ,
261
+ start_time = span_start_time ,
262
+ dotted_order = dotted_order ,
263
+ parent_run_id = parent_run ["id" ],
264
+ )
208
265
209
266
run_name = agent_utils .get_run_name (span )
210
267
run_type = agent_utils .get_run_type (span )
@@ -215,20 +272,20 @@ def on_span_start(self, span: tracing.Span) -> None:
215
272
name = run_name ,
216
273
run_type = run_type ,
217
274
id = span_run_id ,
218
- parent_run_id = parent_run_id ,
275
+ trace_id = trace_id ,
276
+ parent_run_id = parent_run ["id" ],
277
+ dotted_order = dotted_order ,
219
278
inputs = extracted .get ("inputs" , {}),
220
279
)
221
280
if span .started_at :
222
- run_data ["start_time" ] = datetime .datetime .fromisoformat (
223
- span .started_at
224
- )
281
+ run_data ["start_time" ] = datetime .fromisoformat (span .started_at )
225
282
self .client .create_run (** run_data )
226
283
except Exception as e :
227
284
logger .exception (f"Error creating span run: { e } " )
228
285
229
286
def on_span_end (self , span : tracing .Span ) -> None :
230
- run_id = self ._runs .pop (span .span_id , None )
231
- if run_id :
287
+ run = self ._runs .pop (span .span_id , None )
288
+ if run :
232
289
extracted = agent_utils .extract_span_data (span )
233
290
metadata = extracted .get ("metadata" , {})
234
291
metadata ["openai_parent_id" ] = span .parent_id
@@ -238,16 +295,17 @@ def on_span_end(self, span: tracing.Span) -> None:
238
295
outputs = extracted .pop ("outputs" , {})
239
296
inputs = extracted .pop ("inputs" , {})
240
297
run_data : dict = dict (
241
- run_id = run_id ,
298
+ run_id = run ["id" ],
299
+ trace_id = run ["trace_id" ],
300
+ dotted_order = run ["dotted_order" ],
301
+ parent_run_id = run ["parent_run_id" ],
242
302
error = str (span .error ) if span .error else None ,
243
303
outputs = outputs ,
244
304
inputs = inputs ,
245
305
extra = extracted ,
246
306
)
247
307
if span .ended_at :
248
- run_data ["end_time" ] = datetime .datetime .fromisoformat (
249
- span .ended_at
250
- )
308
+ run_data ["end_time" ] = datetime .fromisoformat (span .ended_at )
251
309
252
310
if isinstance (span .span_data , tracing .ResponseSpanData ):
253
311
self ._first_response_inputs [span .trace_id ] = (
0 commit comments