openai-agents 0.2.8__py3-none-any.whl → 0.6.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/__init__.py +105 -4
- agents/_debug.py +15 -4
- agents/_run_impl.py +1203 -96
- agents/agent.py +164 -19
- agents/apply_diff.py +329 -0
- agents/editor.py +47 -0
- agents/exceptions.py +35 -0
- agents/extensions/experimental/__init__.py +6 -0
- agents/extensions/experimental/codex/__init__.py +92 -0
- agents/extensions/experimental/codex/codex.py +89 -0
- agents/extensions/experimental/codex/codex_options.py +35 -0
- agents/extensions/experimental/codex/codex_tool.py +1142 -0
- agents/extensions/experimental/codex/events.py +162 -0
- agents/extensions/experimental/codex/exec.py +263 -0
- agents/extensions/experimental/codex/items.py +245 -0
- agents/extensions/experimental/codex/output_schema_file.py +50 -0
- agents/extensions/experimental/codex/payloads.py +31 -0
- agents/extensions/experimental/codex/thread.py +214 -0
- agents/extensions/experimental/codex/thread_options.py +54 -0
- agents/extensions/experimental/codex/turn_options.py +36 -0
- agents/extensions/handoff_filters.py +13 -1
- agents/extensions/memory/__init__.py +120 -0
- agents/extensions/memory/advanced_sqlite_session.py +1285 -0
- agents/extensions/memory/async_sqlite_session.py +239 -0
- agents/extensions/memory/dapr_session.py +423 -0
- agents/extensions/memory/encrypt_session.py +185 -0
- agents/extensions/memory/redis_session.py +261 -0
- agents/extensions/memory/sqlalchemy_session.py +334 -0
- agents/extensions/models/litellm_model.py +449 -36
- agents/extensions/models/litellm_provider.py +3 -1
- agents/function_schema.py +47 -5
- agents/guardrail.py +16 -2
- agents/{handoffs.py → handoffs/__init__.py} +89 -47
- agents/handoffs/history.py +268 -0
- agents/items.py +237 -11
- agents/lifecycle.py +75 -14
- agents/mcp/server.py +280 -37
- agents/mcp/util.py +24 -3
- agents/memory/__init__.py +22 -2
- agents/memory/openai_conversations_session.py +91 -0
- agents/memory/openai_responses_compaction_session.py +249 -0
- agents/memory/session.py +19 -261
- agents/memory/sqlite_session.py +275 -0
- agents/memory/util.py +20 -0
- agents/model_settings.py +14 -3
- agents/models/__init__.py +13 -0
- agents/models/chatcmpl_converter.py +303 -50
- agents/models/chatcmpl_helpers.py +63 -0
- agents/models/chatcmpl_stream_handler.py +290 -68
- agents/models/default_models.py +58 -0
- agents/models/interface.py +4 -0
- agents/models/openai_chatcompletions.py +103 -49
- agents/models/openai_provider.py +10 -4
- agents/models/openai_responses.py +162 -46
- agents/realtime/__init__.py +4 -0
- agents/realtime/_util.py +14 -3
- agents/realtime/agent.py +7 -0
- agents/realtime/audio_formats.py +53 -0
- agents/realtime/config.py +78 -10
- agents/realtime/events.py +18 -0
- agents/realtime/handoffs.py +2 -2
- agents/realtime/items.py +17 -1
- agents/realtime/model.py +13 -0
- agents/realtime/model_events.py +12 -0
- agents/realtime/model_inputs.py +18 -1
- agents/realtime/openai_realtime.py +696 -150
- agents/realtime/session.py +243 -23
- agents/repl.py +7 -3
- agents/result.py +197 -38
- agents/run.py +949 -168
- agents/run_context.py +13 -2
- agents/stream_events.py +1 -0
- agents/strict_schema.py +14 -0
- agents/tool.py +413 -15
- agents/tool_context.py +22 -1
- agents/tool_guardrails.py +279 -0
- agents/tracing/__init__.py +2 -0
- agents/tracing/config.py +9 -0
- agents/tracing/create.py +4 -0
- agents/tracing/processor_interface.py +84 -11
- agents/tracing/processors.py +65 -54
- agents/tracing/provider.py +64 -7
- agents/tracing/spans.py +105 -0
- agents/tracing/traces.py +116 -16
- agents/usage.py +134 -12
- agents/util/_json.py +19 -1
- agents/util/_transforms.py +12 -2
- agents/voice/input.py +5 -4
- agents/voice/models/openai_stt.py +17 -9
- agents/voice/pipeline.py +2 -0
- agents/voice/pipeline_config.py +4 -0
- {openai_agents-0.2.8.dist-info → openai_agents-0.6.8.dist-info}/METADATA +44 -19
- openai_agents-0.6.8.dist-info/RECORD +134 -0
- {openai_agents-0.2.8.dist-info → openai_agents-0.6.8.dist-info}/WHEEL +1 -1
- openai_agents-0.2.8.dist-info/RECORD +0 -103
- {openai_agents-0.2.8.dist-info → openai_agents-0.6.8.dist-info}/licenses/LICENSE +0 -0
agents/tracing/provider.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import threading
|
|
5
6
|
import uuid
|
|
@@ -8,12 +9,38 @@ from datetime import datetime, timezone
|
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
10
11
|
from ..logger import logger
|
|
12
|
+
from .config import TracingConfig
|
|
11
13
|
from .processor_interface import TracingProcessor
|
|
12
14
|
from .scope import Scope
|
|
13
15
|
from .spans import NoOpSpan, Span, SpanImpl, TSpanData
|
|
14
16
|
from .traces import NoOpTrace, Trace, TraceImpl
|
|
15
17
|
|
|
16
18
|
|
|
19
|
+
def _safe_debug(message: str) -> None:
|
|
20
|
+
"""Best-effort debug logging that tolerates closed streams during shutdown."""
|
|
21
|
+
|
|
22
|
+
def _has_closed_stream_handler(log: logging.Logger) -> bool:
|
|
23
|
+
current: logging.Logger | None = log
|
|
24
|
+
while current is not None:
|
|
25
|
+
for handler in current.handlers:
|
|
26
|
+
stream = getattr(handler, "stream", None)
|
|
27
|
+
if stream is not None and getattr(stream, "closed", False):
|
|
28
|
+
return True
|
|
29
|
+
if not current.propagate:
|
|
30
|
+
break
|
|
31
|
+
current = current.parent
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Avoid emitting debug logs when any handler already owns a closed stream.
|
|
36
|
+
if _has_closed_stream_handler(logger):
|
|
37
|
+
return
|
|
38
|
+
logger.debug(message)
|
|
39
|
+
except Exception:
|
|
40
|
+
# Avoid noisy shutdown errors when the underlying stream is already closed.
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
|
|
17
44
|
class SynchronousMultiTracingProcessor(TracingProcessor):
|
|
18
45
|
"""
|
|
19
46
|
Forwards all calls to a list of TracingProcessors, in order of registration.
|
|
@@ -83,7 +110,7 @@ class SynchronousMultiTracingProcessor(TracingProcessor):
|
|
|
83
110
|
Called when the application stops.
|
|
84
111
|
"""
|
|
85
112
|
for processor in self._processors:
|
|
86
|
-
|
|
113
|
+
_safe_debug(f"Shutting down trace processor {processor}")
|
|
87
114
|
try:
|
|
88
115
|
processor.shutdown()
|
|
89
116
|
except Exception as e:
|
|
@@ -147,6 +174,7 @@ class TraceProvider(ABC):
|
|
|
147
174
|
group_id: str | None = None,
|
|
148
175
|
metadata: dict[str, Any] | None = None,
|
|
149
176
|
disabled: bool = False,
|
|
177
|
+
tracing: TracingConfig | None = None,
|
|
150
178
|
) -> Trace:
|
|
151
179
|
"""Create a new trace."""
|
|
152
180
|
|
|
@@ -168,10 +196,10 @@ class TraceProvider(ABC):
|
|
|
168
196
|
class DefaultTraceProvider(TraceProvider):
|
|
169
197
|
def __init__(self) -> None:
|
|
170
198
|
self._multi_processor = SynchronousMultiTracingProcessor()
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
199
|
+
# Lazily read env flag on first use to honor env set after import but before first trace.
|
|
200
|
+
self._env_disabled: bool | None = None
|
|
201
|
+
self._manual_disabled: bool | None = None
|
|
202
|
+
self._disabled = False
|
|
175
203
|
|
|
176
204
|
def register_processor(self, processor: TracingProcessor):
|
|
177
205
|
"""
|
|
@@ -201,7 +229,27 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
201
229
|
"""
|
|
202
230
|
Set whether tracing is disabled.
|
|
203
231
|
"""
|
|
204
|
-
self.
|
|
232
|
+
self._manual_disabled = disabled
|
|
233
|
+
self._refresh_disabled_flag()
|
|
234
|
+
|
|
235
|
+
def _refresh_disabled_flag(self) -> None:
|
|
236
|
+
"""Refresh disabled flag from cached env value and manual override.
|
|
237
|
+
|
|
238
|
+
The env flag is read once on first use to avoid surprises mid-run; further env
|
|
239
|
+
changes are ignored after the manual flag is set via set_disabled, which always
|
|
240
|
+
takes precedence over the env value.
|
|
241
|
+
"""
|
|
242
|
+
if self._env_disabled is None:
|
|
243
|
+
self._env_disabled = os.environ.get(
|
|
244
|
+
"OPENAI_AGENTS_DISABLE_TRACING", "false"
|
|
245
|
+
).lower() in (
|
|
246
|
+
"true",
|
|
247
|
+
"1",
|
|
248
|
+
)
|
|
249
|
+
if self._manual_disabled is None:
|
|
250
|
+
self._disabled = bool(self._env_disabled)
|
|
251
|
+
else:
|
|
252
|
+
self._disabled = self._manual_disabled
|
|
205
253
|
|
|
206
254
|
def time_iso(self) -> str:
|
|
207
255
|
"""Return the current time in ISO 8601 format."""
|
|
@@ -226,10 +274,12 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
226
274
|
group_id: str | None = None,
|
|
227
275
|
metadata: dict[str, Any] | None = None,
|
|
228
276
|
disabled: bool = False,
|
|
277
|
+
tracing: TracingConfig | None = None,
|
|
229
278
|
) -> Trace:
|
|
230
279
|
"""
|
|
231
280
|
Create a new trace.
|
|
232
281
|
"""
|
|
282
|
+
self._refresh_disabled_flag()
|
|
233
283
|
if self._disabled or disabled:
|
|
234
284
|
logger.debug(f"Tracing is disabled. Not creating trace {name}")
|
|
235
285
|
return NoOpTrace()
|
|
@@ -244,6 +294,7 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
244
294
|
group_id=group_id,
|
|
245
295
|
metadata=metadata,
|
|
246
296
|
processor=self._multi_processor,
|
|
297
|
+
tracing_api_key=tracing.get("api_key") if tracing else None,
|
|
247
298
|
)
|
|
248
299
|
|
|
249
300
|
def create_span(
|
|
@@ -256,6 +307,8 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
256
307
|
"""
|
|
257
308
|
Create a new span.
|
|
258
309
|
"""
|
|
310
|
+
self._refresh_disabled_flag()
|
|
311
|
+
tracing_api_key: str | None = None
|
|
259
312
|
if self._disabled or disabled:
|
|
260
313
|
logger.debug(f"Tracing is disabled. Not creating span {span_data}")
|
|
261
314
|
return NoOpSpan(span_data)
|
|
@@ -277,6 +330,7 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
277
330
|
|
|
278
331
|
parent_id = current_span.span_id if current_span else None
|
|
279
332
|
trace_id = current_trace.trace_id
|
|
333
|
+
tracing_api_key = current_trace.tracing_api_key
|
|
280
334
|
|
|
281
335
|
elif isinstance(parent, Trace):
|
|
282
336
|
if isinstance(parent, NoOpTrace):
|
|
@@ -284,12 +338,14 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
284
338
|
return NoOpSpan(span_data)
|
|
285
339
|
trace_id = parent.trace_id
|
|
286
340
|
parent_id = None
|
|
341
|
+
tracing_api_key = parent.tracing_api_key
|
|
287
342
|
elif isinstance(parent, Span):
|
|
288
343
|
if isinstance(parent, NoOpSpan):
|
|
289
344
|
logger.debug(f"Parent {parent} is no-op, returning NoOpSpan")
|
|
290
345
|
return NoOpSpan(span_data)
|
|
291
346
|
parent_id = parent.span_id
|
|
292
347
|
trace_id = parent.trace_id
|
|
348
|
+
tracing_api_key = parent.tracing_api_key
|
|
293
349
|
|
|
294
350
|
logger.debug(f"Creating span {span_data} with id {span_id}")
|
|
295
351
|
|
|
@@ -299,6 +355,7 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
299
355
|
parent_id=parent_id,
|
|
300
356
|
processor=self._multi_processor,
|
|
301
357
|
span_data=span_data,
|
|
358
|
+
tracing_api_key=tracing_api_key,
|
|
302
359
|
)
|
|
303
360
|
|
|
304
361
|
def shutdown(self) -> None:
|
|
@@ -306,7 +363,7 @@ class DefaultTraceProvider(TraceProvider):
|
|
|
306
363
|
return
|
|
307
364
|
|
|
308
365
|
try:
|
|
309
|
-
|
|
366
|
+
_safe_debug("Shutting down trace provider")
|
|
310
367
|
self._multi_processor.shutdown()
|
|
311
368
|
except Exception as e:
|
|
312
369
|
logger.error(f"Error shutting down trace provider: {e}")
|
agents/tracing/spans.py
CHANGED
|
@@ -16,24 +16,84 @@ TSpanData = TypeVar("TSpanData", bound=SpanData)
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class SpanError(TypedDict):
|
|
19
|
+
"""Represents an error that occurred during span execution.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
message: A human-readable error description
|
|
23
|
+
data: Optional dictionary containing additional error context
|
|
24
|
+
"""
|
|
25
|
+
|
|
19
26
|
message: str
|
|
20
27
|
data: dict[str, Any] | None
|
|
21
28
|
|
|
22
29
|
|
|
23
30
|
class Span(abc.ABC, Generic[TSpanData]):
|
|
31
|
+
"""Base class for representing traceable operations with timing and context.
|
|
32
|
+
|
|
33
|
+
A span represents a single operation within a trace (e.g., an LLM call, tool execution,
|
|
34
|
+
or agent run). Spans track timing, relationships between operations, and operation-specific
|
|
35
|
+
data.
|
|
36
|
+
|
|
37
|
+
Type Args:
|
|
38
|
+
TSpanData: The type of span-specific data this span contains.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
```python
|
|
42
|
+
# Creating a custom span
|
|
43
|
+
with custom_span("database_query", {
|
|
44
|
+
"operation": "SELECT",
|
|
45
|
+
"table": "users"
|
|
46
|
+
}) as span:
|
|
47
|
+
results = await db.query("SELECT * FROM users")
|
|
48
|
+
span.set_output({"count": len(results)})
|
|
49
|
+
|
|
50
|
+
# Handling errors in spans
|
|
51
|
+
with custom_span("risky_operation") as span:
|
|
52
|
+
try:
|
|
53
|
+
result = perform_risky_operation()
|
|
54
|
+
except Exception as e:
|
|
55
|
+
span.set_error({
|
|
56
|
+
"message": str(e),
|
|
57
|
+
"data": {"operation": "risky_operation"}
|
|
58
|
+
})
|
|
59
|
+
raise
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Notes:
|
|
63
|
+
- Spans automatically nest under the current trace
|
|
64
|
+
- Use context managers for reliable start/finish
|
|
65
|
+
- Include relevant data but avoid sensitive information
|
|
66
|
+
- Handle errors properly using set_error()
|
|
67
|
+
"""
|
|
68
|
+
|
|
24
69
|
@property
|
|
25
70
|
@abc.abstractmethod
|
|
26
71
|
def trace_id(self) -> str:
|
|
72
|
+
"""The ID of the trace this span belongs to.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
str: Unique identifier of the parent trace.
|
|
76
|
+
"""
|
|
27
77
|
pass
|
|
28
78
|
|
|
29
79
|
@property
|
|
30
80
|
@abc.abstractmethod
|
|
31
81
|
def span_id(self) -> str:
|
|
82
|
+
"""Unique identifier for this span.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
str: The span's unique ID within its trace.
|
|
86
|
+
"""
|
|
32
87
|
pass
|
|
33
88
|
|
|
34
89
|
@property
|
|
35
90
|
@abc.abstractmethod
|
|
36
91
|
def span_data(self) -> TSpanData:
|
|
92
|
+
"""Operation-specific data for this span.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
TSpanData: Data specific to this type of span (e.g., LLM generation data).
|
|
96
|
+
"""
|
|
37
97
|
pass
|
|
38
98
|
|
|
39
99
|
@abc.abstractmethod
|
|
@@ -67,6 +127,11 @@ class Span(abc.ABC, Generic[TSpanData]):
|
|
|
67
127
|
@property
|
|
68
128
|
@abc.abstractmethod
|
|
69
129
|
def parent_id(self) -> str | None:
|
|
130
|
+
"""ID of the parent span, if any.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
str | None: The parent span's ID, or None if this is a root span.
|
|
134
|
+
"""
|
|
70
135
|
pass
|
|
71
136
|
|
|
72
137
|
@abc.abstractmethod
|
|
@@ -76,6 +141,11 @@ class Span(abc.ABC, Generic[TSpanData]):
|
|
|
76
141
|
@property
|
|
77
142
|
@abc.abstractmethod
|
|
78
143
|
def error(self) -> SpanError | None:
|
|
144
|
+
"""Any error that occurred during span execution.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
SpanError | None: Error details if an error occurred, None otherwise.
|
|
148
|
+
"""
|
|
79
149
|
pass
|
|
80
150
|
|
|
81
151
|
@abc.abstractmethod
|
|
@@ -85,15 +155,39 @@ class Span(abc.ABC, Generic[TSpanData]):
|
|
|
85
155
|
@property
|
|
86
156
|
@abc.abstractmethod
|
|
87
157
|
def started_at(self) -> str | None:
|
|
158
|
+
"""When the span started execution.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
str | None: ISO format timestamp of span start, None if not started.
|
|
162
|
+
"""
|
|
88
163
|
pass
|
|
89
164
|
|
|
90
165
|
@property
|
|
91
166
|
@abc.abstractmethod
|
|
92
167
|
def ended_at(self) -> str | None:
|
|
168
|
+
"""When the span finished execution.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
str | None: ISO format timestamp of span end, None if not finished.
|
|
172
|
+
"""
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
@abc.abstractmethod
|
|
177
|
+
def tracing_api_key(self) -> str | None:
|
|
178
|
+
"""The API key to use when exporting this span."""
|
|
93
179
|
pass
|
|
94
180
|
|
|
95
181
|
|
|
96
182
|
class NoOpSpan(Span[TSpanData]):
|
|
183
|
+
"""A no-op implementation of Span that doesn't record any data.
|
|
184
|
+
|
|
185
|
+
Used when tracing is disabled but span operations still need to work.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
span_data: The operation-specific data for this span.
|
|
189
|
+
"""
|
|
190
|
+
|
|
97
191
|
__slots__ = ("_span_data", "_prev_span_token")
|
|
98
192
|
|
|
99
193
|
def __init__(self, span_data: TSpanData):
|
|
@@ -155,6 +249,10 @@ class NoOpSpan(Span[TSpanData]):
|
|
|
155
249
|
def ended_at(self) -> str | None:
|
|
156
250
|
return None
|
|
157
251
|
|
|
252
|
+
@property
|
|
253
|
+
def tracing_api_key(self) -> str | None:
|
|
254
|
+
return None
|
|
255
|
+
|
|
158
256
|
|
|
159
257
|
class SpanImpl(Span[TSpanData]):
|
|
160
258
|
__slots__ = (
|
|
@@ -167,6 +265,7 @@ class SpanImpl(Span[TSpanData]):
|
|
|
167
265
|
"_prev_span_token",
|
|
168
266
|
"_processor",
|
|
169
267
|
"_span_data",
|
|
268
|
+
"_tracing_api_key",
|
|
170
269
|
)
|
|
171
270
|
|
|
172
271
|
def __init__(
|
|
@@ -176,6 +275,7 @@ class SpanImpl(Span[TSpanData]):
|
|
|
176
275
|
parent_id: str | None,
|
|
177
276
|
processor: TracingProcessor,
|
|
178
277
|
span_data: TSpanData,
|
|
278
|
+
tracing_api_key: str | None,
|
|
179
279
|
):
|
|
180
280
|
self._trace_id = trace_id
|
|
181
281
|
self._span_id = span_id or util.gen_span_id()
|
|
@@ -186,6 +286,7 @@ class SpanImpl(Span[TSpanData]):
|
|
|
186
286
|
self._error: SpanError | None = None
|
|
187
287
|
self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None
|
|
188
288
|
self._span_data = span_data
|
|
289
|
+
self._tracing_api_key = tracing_api_key
|
|
189
290
|
|
|
190
291
|
@property
|
|
191
292
|
def trace_id(self) -> str:
|
|
@@ -251,6 +352,10 @@ class SpanImpl(Span[TSpanData]):
|
|
|
251
352
|
def ended_at(self) -> str | None:
|
|
252
353
|
return self._ended_at
|
|
253
354
|
|
|
355
|
+
@property
|
|
356
|
+
def tracing_api_key(self) -> str | None:
|
|
357
|
+
return self._tracing_api_key
|
|
358
|
+
|
|
254
359
|
def export(self) -> dict[str, Any] | None:
|
|
255
360
|
return {
|
|
256
361
|
"object": "trace.span",
|
agents/tracing/traces.py
CHANGED
|
@@ -11,8 +11,35 @@ from .scope import Scope
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Trace(abc.ABC):
|
|
14
|
-
"""
|
|
15
|
-
|
|
14
|
+
"""A complete end-to-end workflow containing related spans and metadata.
|
|
15
|
+
|
|
16
|
+
A trace represents a logical workflow or operation (e.g., "Customer Service Query"
|
|
17
|
+
or "Code Generation") and contains all the spans (individual operations) that occur
|
|
18
|
+
during that workflow.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
# Basic trace usage
|
|
23
|
+
with trace("Order Processing") as t:
|
|
24
|
+
validation_result = await Runner.run(validator, order_data)
|
|
25
|
+
if validation_result.approved:
|
|
26
|
+
await Runner.run(processor, order_data)
|
|
27
|
+
|
|
28
|
+
# Trace with metadata and grouping
|
|
29
|
+
with trace(
|
|
30
|
+
"Customer Service",
|
|
31
|
+
group_id="chat_123",
|
|
32
|
+
metadata={"customer": "user_456"}
|
|
33
|
+
) as t:
|
|
34
|
+
result = await Runner.run(support_agent, query)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
- Use descriptive workflow names
|
|
39
|
+
- Group related traces with consistent group_ids
|
|
40
|
+
- Add relevant metadata for filtering/analysis
|
|
41
|
+
- Use context managers for reliable cleanup
|
|
42
|
+
- Consider privacy when adding trace data
|
|
16
43
|
"""
|
|
17
44
|
|
|
18
45
|
@abc.abstractmethod
|
|
@@ -25,51 +52,98 @@ class Trace(abc.ABC):
|
|
|
25
52
|
|
|
26
53
|
@abc.abstractmethod
|
|
27
54
|
def start(self, mark_as_current: bool = False):
|
|
28
|
-
"""
|
|
29
|
-
Start the trace.
|
|
55
|
+
"""Start the trace and optionally mark it as the current trace.
|
|
30
56
|
|
|
31
57
|
Args:
|
|
32
|
-
mark_as_current: If true,
|
|
58
|
+
mark_as_current: If true, marks this trace as the current trace
|
|
59
|
+
in the execution context.
|
|
60
|
+
|
|
61
|
+
Notes:
|
|
62
|
+
- Must be called before any spans can be added
|
|
63
|
+
- Only one trace can be current at a time
|
|
64
|
+
- Thread-safe when using mark_as_current
|
|
33
65
|
"""
|
|
34
66
|
pass
|
|
35
67
|
|
|
36
68
|
@abc.abstractmethod
|
|
37
69
|
def finish(self, reset_current: bool = False):
|
|
38
|
-
"""
|
|
39
|
-
Finish the trace.
|
|
70
|
+
"""Finish the trace and optionally reset the current trace.
|
|
40
71
|
|
|
41
72
|
Args:
|
|
42
|
-
reset_current: If true, the trace
|
|
73
|
+
reset_current: If true, resets the current trace to the previous
|
|
74
|
+
trace in the execution context.
|
|
75
|
+
|
|
76
|
+
Notes:
|
|
77
|
+
- Must be called to complete the trace
|
|
78
|
+
- Finalizes all open spans
|
|
79
|
+
- Thread-safe when using reset_current
|
|
43
80
|
"""
|
|
44
81
|
pass
|
|
45
82
|
|
|
46
83
|
@property
|
|
47
84
|
@abc.abstractmethod
|
|
48
85
|
def trace_id(self) -> str:
|
|
49
|
-
"""
|
|
50
|
-
|
|
86
|
+
"""Get the unique identifier for this trace.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
str: The trace's unique ID in the format 'trace_<32_alphanumeric>'
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
- IDs are globally unique
|
|
93
|
+
- Used to link spans to their parent trace
|
|
94
|
+
- Can be used to look up traces in the dashboard
|
|
51
95
|
"""
|
|
52
96
|
pass
|
|
53
97
|
|
|
54
98
|
@property
|
|
55
99
|
@abc.abstractmethod
|
|
56
100
|
def name(self) -> str:
|
|
57
|
-
"""
|
|
58
|
-
|
|
101
|
+
"""Get the human-readable name of this workflow trace.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
str: The workflow name (e.g., "Customer Service", "Data Processing")
|
|
105
|
+
|
|
106
|
+
Notes:
|
|
107
|
+
- Should be descriptive and meaningful
|
|
108
|
+
- Used for grouping and filtering in the dashboard
|
|
109
|
+
- Helps identify the purpose of the trace
|
|
59
110
|
"""
|
|
60
111
|
pass
|
|
61
112
|
|
|
62
113
|
@abc.abstractmethod
|
|
63
114
|
def export(self) -> dict[str, Any] | None:
|
|
115
|
+
"""Export the trace data as a serializable dictionary.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
dict | None: Dictionary containing trace data, or None if tracing is disabled.
|
|
119
|
+
|
|
120
|
+
Notes:
|
|
121
|
+
- Includes all spans and their data
|
|
122
|
+
- Used for sending traces to backends
|
|
123
|
+
- May include metadata and group ID
|
|
64
124
|
"""
|
|
65
|
-
|
|
66
|
-
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
@abc.abstractmethod
|
|
129
|
+
def tracing_api_key(self) -> str | None:
|
|
130
|
+
"""The API key to use when exporting this trace and its spans."""
|
|
67
131
|
pass
|
|
68
132
|
|
|
69
133
|
|
|
70
134
|
class NoOpTrace(Trace):
|
|
71
|
-
"""
|
|
72
|
-
|
|
135
|
+
"""A no-op implementation of Trace that doesn't record any data.
|
|
136
|
+
|
|
137
|
+
Used when tracing is disabled but trace operations still need to work.
|
|
138
|
+
Maintains proper context management but doesn't store or export any data.
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
```python
|
|
142
|
+
# When tracing is disabled, traces become NoOpTrace
|
|
143
|
+
with trace("Disabled Workflow") as t:
|
|
144
|
+
# Operations still work but nothing is recorded
|
|
145
|
+
await Runner.run(agent, "query")
|
|
146
|
+
```
|
|
73
147
|
"""
|
|
74
148
|
|
|
75
149
|
def __init__(self):
|
|
@@ -101,13 +175,32 @@ class NoOpTrace(Trace):
|
|
|
101
175
|
|
|
102
176
|
@property
|
|
103
177
|
def trace_id(self) -> str:
|
|
178
|
+
"""The trace's unique identifier.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
str: A unique ID for this trace.
|
|
182
|
+
"""
|
|
104
183
|
return "no-op"
|
|
105
184
|
|
|
106
185
|
@property
|
|
107
186
|
def name(self) -> str:
|
|
187
|
+
"""The workflow name for this trace.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
str: Human-readable name describing this workflow.
|
|
191
|
+
"""
|
|
108
192
|
return "no-op"
|
|
109
193
|
|
|
110
194
|
def export(self) -> dict[str, Any] | None:
|
|
195
|
+
"""Export the trace data as a dictionary.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
dict | None: Trace data in exportable format, or None if no data.
|
|
199
|
+
"""
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def tracing_api_key(self) -> str | None:
|
|
111
204
|
return None
|
|
112
205
|
|
|
113
206
|
|
|
@@ -122,6 +215,7 @@ class TraceImpl(Trace):
|
|
|
122
215
|
__slots__ = (
|
|
123
216
|
"_name",
|
|
124
217
|
"_trace_id",
|
|
218
|
+
"_tracing_api_key",
|
|
125
219
|
"group_id",
|
|
126
220
|
"metadata",
|
|
127
221
|
"_prev_context_token",
|
|
@@ -136,9 +230,11 @@ class TraceImpl(Trace):
|
|
|
136
230
|
group_id: str | None,
|
|
137
231
|
metadata: dict[str, Any] | None,
|
|
138
232
|
processor: TracingProcessor,
|
|
233
|
+
tracing_api_key: str | None,
|
|
139
234
|
):
|
|
140
235
|
self._name = name
|
|
141
236
|
self._trace_id = trace_id or util.gen_trace_id()
|
|
237
|
+
self._tracing_api_key = tracing_api_key
|
|
142
238
|
self.group_id = group_id
|
|
143
239
|
self.metadata = metadata
|
|
144
240
|
self._prev_context_token: contextvars.Token[Trace | None] | None = None
|
|
@@ -153,6 +249,10 @@ class TraceImpl(Trace):
|
|
|
153
249
|
def name(self) -> str:
|
|
154
250
|
return self._name
|
|
155
251
|
|
|
252
|
+
@property
|
|
253
|
+
def tracing_api_key(self) -> str | None:
|
|
254
|
+
return self._tracing_api_key
|
|
255
|
+
|
|
156
256
|
def start(self, mark_as_current: bool = False):
|
|
157
257
|
if self._started:
|
|
158
258
|
return
|