openai-agents 0.3.0__py3-none-any.whl → 0.3.2__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.
Potentially problematic release.
This version of openai-agents might be problematic. Click here for more details.
- agents/agent.py +18 -2
- agents/extensions/handoff_filters.py +2 -0
- agents/extensions/memory/__init__.py +42 -15
- agents/extensions/memory/encrypt_session.py +185 -0
- agents/extensions/models/litellm_model.py +38 -5
- agents/function_schema.py +45 -3
- agents/models/chatcmpl_converter.py +85 -15
- agents/models/chatcmpl_helpers.py +6 -0
- agents/models/chatcmpl_stream_handler.py +29 -1
- agents/models/openai_chatcompletions.py +9 -2
- agents/models/openai_responses.py +14 -1
- agents/realtime/__init__.py +2 -0
- agents/realtime/config.py +10 -0
- agents/realtime/model_events.py +2 -0
- agents/realtime/openai_realtime.py +11 -1
- agents/realtime/session.py +2 -0
- agents/result.py +47 -20
- agents/run.py +157 -78
- agents/tool_context.py +14 -1
- agents/tracing/processor_interface.py +84 -11
- agents/tracing/spans.py +88 -0
- agents/tracing/traces.py +99 -16
- agents/util/_transforms.py +12 -2
- agents/voice/models/openai_stt.py +9 -4
- {openai_agents-0.3.0.dist-info → openai_agents-0.3.2.dist-info}/METADATA +3 -1
- {openai_agents-0.3.0.dist-info → openai_agents-0.3.2.dist-info}/RECORD +28 -27
- {openai_agents-0.3.0.dist-info → openai_agents-0.3.2.dist-info}/WHEEL +0 -0
- {openai_agents-0.3.0.dist-info → openai_agents-0.3.2.dist-info}/licenses/LICENSE +0 -0
agents/run.py
CHANGED
|
@@ -45,6 +45,7 @@ from .guardrail import (
|
|
|
45
45
|
)
|
|
46
46
|
from .handoffs import Handoff, HandoffInputFilter, handoff
|
|
47
47
|
from .items import (
|
|
48
|
+
HandoffCallItem,
|
|
48
49
|
ItemHelpers,
|
|
49
50
|
ModelResponse,
|
|
50
51
|
RunItem,
|
|
@@ -52,7 +53,7 @@ from .items import (
|
|
|
52
53
|
ToolCallItemTypes,
|
|
53
54
|
TResponseInputItem,
|
|
54
55
|
)
|
|
55
|
-
from .lifecycle import RunHooks
|
|
56
|
+
from .lifecycle import AgentHooksBase, RunHooks, RunHooksBase
|
|
56
57
|
from .logger import logger
|
|
57
58
|
from .memory import Session, SessionInputCallback
|
|
58
59
|
from .model_settings import ModelSettings
|
|
@@ -60,7 +61,12 @@ from .models.interface import Model, ModelProvider
|
|
|
60
61
|
from .models.multi_provider import MultiProvider
|
|
61
62
|
from .result import RunResult, RunResultStreaming
|
|
62
63
|
from .run_context import RunContextWrapper, TContext
|
|
63
|
-
from .stream_events import
|
|
64
|
+
from .stream_events import (
|
|
65
|
+
AgentUpdatedStreamEvent,
|
|
66
|
+
RawResponsesStreamEvent,
|
|
67
|
+
RunItemStreamEvent,
|
|
68
|
+
StreamEvent,
|
|
69
|
+
)
|
|
64
70
|
from .tool import Tool
|
|
65
71
|
from .tracing import Span, SpanError, agent_span, get_current_trace, trace
|
|
66
72
|
from .tracing.span_data import AgentSpanData
|
|
@@ -237,39 +243,54 @@ class Runner:
|
|
|
237
243
|
conversation_id: str | None = None,
|
|
238
244
|
session: Session | None = None,
|
|
239
245
|
) -> RunResult:
|
|
240
|
-
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
"""
|
|
247
|
+
Run a workflow starting at the given agent.
|
|
248
|
+
|
|
249
|
+
The agent will run in a loop until a final output is generated. The loop runs like so:
|
|
250
|
+
|
|
251
|
+
1. The agent is invoked with the given input.
|
|
252
|
+
2. If there is a final output (i.e. the agent produces something of type
|
|
253
|
+
`agent.output_type`), the loop terminates.
|
|
254
|
+
3. If there's a handoff, we run the loop again, with the new agent.
|
|
255
|
+
4. Else, we run tool calls (if any), and re-run the loop.
|
|
256
|
+
|
|
247
257
|
In two cases, the agent may raise an exception:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
258
|
+
|
|
259
|
+
1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
|
|
260
|
+
2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered
|
|
261
|
+
exception is raised.
|
|
262
|
+
|
|
263
|
+
Note:
|
|
264
|
+
Only the first agent's input guardrails are run.
|
|
265
|
+
|
|
251
266
|
Args:
|
|
252
267
|
starting_agent: The starting agent to run.
|
|
253
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
254
|
-
or a list of input items.
|
|
268
|
+
input: The initial input to the agent. You can pass a single string for a
|
|
269
|
+
user message, or a list of input items.
|
|
255
270
|
context: The context to run the agent with.
|
|
256
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
257
|
-
AI invocation (including any tool calls that might occur).
|
|
271
|
+
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
272
|
+
defined as one AI invocation (including any tool calls that might occur).
|
|
258
273
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
259
274
|
run_config: Global settings for the entire agent run.
|
|
260
|
-
previous_response_id: The ID of the previous response
|
|
261
|
-
Responses API, this allows you to skip passing in input
|
|
262
|
-
|
|
275
|
+
previous_response_id: The ID of the previous response. If using OpenAI
|
|
276
|
+
models via the Responses API, this allows you to skip passing in input
|
|
277
|
+
from the previous turn.
|
|
278
|
+
conversation_id: The conversation ID
|
|
279
|
+
(https://platform.openai.com/docs/guides/conversation-state?api-mode=responses).
|
|
263
280
|
If provided, the conversation will be used to read and write items.
|
|
264
281
|
Every agent will have access to the conversation history so far,
|
|
265
|
-
and
|
|
282
|
+
and its output items will be written to the conversation.
|
|
266
283
|
We recommend only using this if you are exclusively using OpenAI models;
|
|
267
284
|
other model providers don't write to the Conversation object,
|
|
268
285
|
so you'll end up having partial conversations stored.
|
|
286
|
+
session: A session for automatic conversation history management.
|
|
287
|
+
|
|
269
288
|
Returns:
|
|
270
|
-
A run result containing all the inputs, guardrail results and the output of
|
|
271
|
-
agent. Agents may perform handoffs, so we don't know the specific
|
|
289
|
+
A run result containing all the inputs, guardrail results and the output of
|
|
290
|
+
the last agent. Agents may perform handoffs, so we don't know the specific
|
|
291
|
+
type of the output.
|
|
272
292
|
"""
|
|
293
|
+
|
|
273
294
|
runner = DEFAULT_AGENT_RUNNER
|
|
274
295
|
return await runner.run(
|
|
275
296
|
starting_agent,
|
|
@@ -297,36 +318,52 @@ class Runner:
|
|
|
297
318
|
conversation_id: str | None = None,
|
|
298
319
|
session: Session | None = None,
|
|
299
320
|
) -> RunResult:
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
"""
|
|
322
|
+
Run a workflow synchronously, starting at the given agent.
|
|
323
|
+
|
|
324
|
+
Note:
|
|
325
|
+
This just wraps the `run` method, so it will not work if there's already an
|
|
326
|
+
event loop (e.g. inside an async function, or in a Jupyter notebook or async
|
|
327
|
+
context like FastAPI). For those cases, use the `run` method instead.
|
|
328
|
+
|
|
329
|
+
The agent will run in a loop until a final output is generated. The loop runs:
|
|
330
|
+
|
|
331
|
+
1. The agent is invoked with the given input.
|
|
332
|
+
2. If there is a final output (i.e. the agent produces something of type
|
|
333
|
+
`agent.output_type`), the loop terminates.
|
|
334
|
+
3. If there's a handoff, we run the loop again, with the new agent.
|
|
335
|
+
4. Else, we run tool calls (if any), and re-run the loop.
|
|
336
|
+
|
|
310
337
|
In two cases, the agent may raise an exception:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
338
|
+
|
|
339
|
+
1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
|
|
340
|
+
2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered
|
|
341
|
+
exception is raised.
|
|
342
|
+
|
|
343
|
+
Note:
|
|
344
|
+
Only the first agent's input guardrails are run.
|
|
345
|
+
|
|
314
346
|
Args:
|
|
315
347
|
starting_agent: The starting agent to run.
|
|
316
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
317
|
-
or a list of input items.
|
|
348
|
+
input: The initial input to the agent. You can pass a single string for a
|
|
349
|
+
user message, or a list of input items.
|
|
318
350
|
context: The context to run the agent with.
|
|
319
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
320
|
-
AI invocation (including any tool calls that might occur).
|
|
351
|
+
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
352
|
+
defined as one AI invocation (including any tool calls that might occur).
|
|
321
353
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
322
354
|
run_config: Global settings for the entire agent run.
|
|
323
|
-
previous_response_id: The ID of the previous response, if using OpenAI
|
|
324
|
-
Responses API, this allows you to skip passing in input
|
|
355
|
+
previous_response_id: The ID of the previous response, if using OpenAI
|
|
356
|
+
models via the Responses API, this allows you to skip passing in input
|
|
357
|
+
from the previous turn.
|
|
325
358
|
conversation_id: The ID of the stored conversation, if any.
|
|
359
|
+
session: A session for automatic conversation history management.
|
|
360
|
+
|
|
326
361
|
Returns:
|
|
327
|
-
A run result containing all the inputs, guardrail results and the output of
|
|
328
|
-
agent. Agents may perform handoffs, so we don't know the specific
|
|
362
|
+
A run result containing all the inputs, guardrail results and the output of
|
|
363
|
+
the last agent. Agents may perform handoffs, so we don't know the specific
|
|
364
|
+
type of the output.
|
|
329
365
|
"""
|
|
366
|
+
|
|
330
367
|
runner = DEFAULT_AGENT_RUNNER
|
|
331
368
|
return runner.run_sync(
|
|
332
369
|
starting_agent,
|
|
@@ -353,33 +390,49 @@ class Runner:
|
|
|
353
390
|
conversation_id: str | None = None,
|
|
354
391
|
session: Session | None = None,
|
|
355
392
|
) -> RunResultStreaming:
|
|
356
|
-
"""
|
|
357
|
-
|
|
393
|
+
"""
|
|
394
|
+
Run a workflow starting at the given agent in streaming mode.
|
|
395
|
+
|
|
396
|
+
The returned result object contains a method you can use to stream semantic
|
|
397
|
+
events as they are generated.
|
|
398
|
+
|
|
358
399
|
The agent will run in a loop until a final output is generated. The loop runs like so:
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
400
|
+
|
|
401
|
+
1. The agent is invoked with the given input.
|
|
402
|
+
2. If there is a final output (i.e. the agent produces something of type
|
|
403
|
+
`agent.output_type`), the loop terminates.
|
|
404
|
+
3. If there's a handoff, we run the loop again, with the new agent.
|
|
405
|
+
4. Else, we run tool calls (if any), and re-run the loop.
|
|
406
|
+
|
|
364
407
|
In two cases, the agent may raise an exception:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
408
|
+
|
|
409
|
+
1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
|
|
410
|
+
2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered
|
|
411
|
+
exception is raised.
|
|
412
|
+
|
|
413
|
+
Note:
|
|
414
|
+
Only the first agent's input guardrails are run.
|
|
415
|
+
|
|
368
416
|
Args:
|
|
369
417
|
starting_agent: The starting agent to run.
|
|
370
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
371
|
-
or a list of input items.
|
|
418
|
+
input: The initial input to the agent. You can pass a single string for a
|
|
419
|
+
user message, or a list of input items.
|
|
372
420
|
context: The context to run the agent with.
|
|
373
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
374
|
-
AI invocation (including any tool calls that might occur).
|
|
421
|
+
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
422
|
+
defined as one AI invocation (including any tool calls that might occur).
|
|
375
423
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
376
424
|
run_config: Global settings for the entire agent run.
|
|
377
|
-
previous_response_id: The ID of the previous response, if using OpenAI
|
|
378
|
-
Responses API, this allows you to skip passing in input
|
|
425
|
+
previous_response_id: The ID of the previous response, if using OpenAI
|
|
426
|
+
models via the Responses API, this allows you to skip passing in input
|
|
427
|
+
from the previous turn.
|
|
379
428
|
conversation_id: The ID of the stored conversation, if any.
|
|
429
|
+
session: A session for automatic conversation history management.
|
|
430
|
+
|
|
380
431
|
Returns:
|
|
381
|
-
A result object that contains data about the run, as well as a method to
|
|
432
|
+
A result object that contains data about the run, as well as a method to
|
|
433
|
+
stream events.
|
|
382
434
|
"""
|
|
435
|
+
|
|
383
436
|
runner = DEFAULT_AGENT_RUNNER
|
|
384
437
|
return runner.run_streamed(
|
|
385
438
|
starting_agent,
|
|
@@ -408,13 +461,11 @@ class AgentRunner:
|
|
|
408
461
|
) -> RunResult:
|
|
409
462
|
context = kwargs.get("context")
|
|
410
463
|
max_turns = kwargs.get("max_turns", DEFAULT_MAX_TURNS)
|
|
411
|
-
hooks = kwargs.get("hooks")
|
|
464
|
+
hooks = cast(RunHooks[TContext], self._validate_run_hooks(kwargs.get("hooks")))
|
|
412
465
|
run_config = kwargs.get("run_config")
|
|
413
466
|
previous_response_id = kwargs.get("previous_response_id")
|
|
414
467
|
conversation_id = kwargs.get("conversation_id")
|
|
415
468
|
session = kwargs.get("session")
|
|
416
|
-
if hooks is None:
|
|
417
|
-
hooks = RunHooks[Any]()
|
|
418
469
|
if run_config is None:
|
|
419
470
|
run_config = RunConfig()
|
|
420
471
|
|
|
@@ -615,14 +666,12 @@ class AgentRunner:
|
|
|
615
666
|
) -> RunResultStreaming:
|
|
616
667
|
context = kwargs.get("context")
|
|
617
668
|
max_turns = kwargs.get("max_turns", DEFAULT_MAX_TURNS)
|
|
618
|
-
hooks = kwargs.get("hooks")
|
|
669
|
+
hooks = cast(RunHooks[TContext], self._validate_run_hooks(kwargs.get("hooks")))
|
|
619
670
|
run_config = kwargs.get("run_config")
|
|
620
671
|
previous_response_id = kwargs.get("previous_response_id")
|
|
621
672
|
conversation_id = kwargs.get("conversation_id")
|
|
622
673
|
session = kwargs.get("session")
|
|
623
674
|
|
|
624
|
-
if hooks is None:
|
|
625
|
-
hooks = RunHooks[Any]()
|
|
626
675
|
if run_config is None:
|
|
627
676
|
run_config = RunConfig()
|
|
628
677
|
|
|
@@ -679,6 +728,23 @@ class AgentRunner:
|
|
|
679
728
|
)
|
|
680
729
|
return streamed_result
|
|
681
730
|
|
|
731
|
+
@staticmethod
|
|
732
|
+
def _validate_run_hooks(
|
|
733
|
+
hooks: RunHooksBase[Any, Agent[Any]] | AgentHooksBase[Any, Agent[Any]] | Any | None,
|
|
734
|
+
) -> RunHooks[Any]:
|
|
735
|
+
if hooks is None:
|
|
736
|
+
return RunHooks[Any]()
|
|
737
|
+
input_hook_type = type(hooks).__name__
|
|
738
|
+
if isinstance(hooks, AgentHooksBase):
|
|
739
|
+
raise TypeError(
|
|
740
|
+
"Run hooks must be instances of RunHooks. "
|
|
741
|
+
f"Received agent-scoped hooks ({input_hook_type}). "
|
|
742
|
+
"Attach AgentHooks to an Agent via Agent(..., hooks=...)."
|
|
743
|
+
)
|
|
744
|
+
if not isinstance(hooks, RunHooksBase):
|
|
745
|
+
raise TypeError(f"Run hooks must be instances of RunHooks. Received {input_hook_type}.")
|
|
746
|
+
return hooks
|
|
747
|
+
|
|
682
748
|
@classmethod
|
|
683
749
|
async def _maybe_filter_model_input(
|
|
684
750
|
cls,
|
|
@@ -1095,14 +1161,19 @@ class AgentRunner:
|
|
|
1095
1161
|
context_wrapper=context_wrapper,
|
|
1096
1162
|
run_config=run_config,
|
|
1097
1163
|
tool_use_tracker=tool_use_tracker,
|
|
1164
|
+
event_queue=streamed_result._event_queue,
|
|
1098
1165
|
)
|
|
1099
1166
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1167
|
+
import dataclasses as _dc
|
|
1168
|
+
|
|
1169
|
+
# Filter out items that have already been sent to avoid duplicates
|
|
1170
|
+
items_to_filter = single_step_result.new_step_items
|
|
1102
1171
|
|
|
1103
|
-
|
|
1172
|
+
if emitted_tool_call_ids:
|
|
1173
|
+
# Filter out tool call items that were already emitted during streaming
|
|
1174
|
+
items_to_filter = [
|
|
1104
1175
|
item
|
|
1105
|
-
for item in
|
|
1176
|
+
for item in items_to_filter
|
|
1106
1177
|
if not (
|
|
1107
1178
|
isinstance(item, ToolCallItem)
|
|
1108
1179
|
and (
|
|
@@ -1114,15 +1185,14 @@ class AgentRunner:
|
|
|
1114
1185
|
)
|
|
1115
1186
|
]
|
|
1116
1187
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
)
|
|
1188
|
+
# Filter out HandoffCallItem to avoid duplicates (already sent earlier)
|
|
1189
|
+
items_to_filter = [
|
|
1190
|
+
item for item in items_to_filter if not isinstance(item, HandoffCallItem)
|
|
1191
|
+
]
|
|
1120
1192
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
else:
|
|
1125
|
-
RunImpl.stream_step_result_to_queue(single_step_result, streamed_result._event_queue)
|
|
1193
|
+
# Create filtered result and send to queue
|
|
1194
|
+
filtered_result = _dc.replace(single_step_result, new_step_items=items_to_filter)
|
|
1195
|
+
RunImpl.stream_step_result_to_queue(filtered_result, streamed_result._event_queue)
|
|
1126
1196
|
return single_step_result
|
|
1127
1197
|
|
|
1128
1198
|
@classmethod
|
|
@@ -1207,6 +1277,7 @@ class AgentRunner:
|
|
|
1207
1277
|
context_wrapper: RunContextWrapper[TContext],
|
|
1208
1278
|
run_config: RunConfig,
|
|
1209
1279
|
tool_use_tracker: AgentToolUseTracker,
|
|
1280
|
+
event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] | None = None,
|
|
1210
1281
|
) -> SingleStepResult:
|
|
1211
1282
|
processed_response = RunImpl.process_model_response(
|
|
1212
1283
|
agent=agent,
|
|
@@ -1218,6 +1289,14 @@ class AgentRunner:
|
|
|
1218
1289
|
|
|
1219
1290
|
tool_use_tracker.add_tool_use(agent, processed_response.tools_used)
|
|
1220
1291
|
|
|
1292
|
+
# Send handoff items immediately for streaming, but avoid duplicates
|
|
1293
|
+
if event_queue is not None and processed_response.new_items:
|
|
1294
|
+
handoff_items = [
|
|
1295
|
+
item for item in processed_response.new_items if isinstance(item, HandoffCallItem)
|
|
1296
|
+
]
|
|
1297
|
+
if handoff_items:
|
|
1298
|
+
RunImpl.stream_step_items_to_queue(cast(list[RunItem], handoff_items), event_queue)
|
|
1299
|
+
|
|
1221
1300
|
return await RunImpl.execute_tools_and_side_effects(
|
|
1222
1301
|
agent=agent,
|
|
1223
1302
|
original_input=original_input,
|
agents/tool_context.py
CHANGED
|
@@ -14,6 +14,10 @@ def _assert_must_pass_tool_name() -> str:
|
|
|
14
14
|
raise ValueError("tool_name must be passed to ToolContext")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _assert_must_pass_tool_arguments() -> str:
|
|
18
|
+
raise ValueError("tool_arguments must be passed to ToolContext")
|
|
19
|
+
|
|
20
|
+
|
|
17
21
|
@dataclass
|
|
18
22
|
class ToolContext(RunContextWrapper[TContext]):
|
|
19
23
|
"""The context of a tool call."""
|
|
@@ -24,6 +28,9 @@ class ToolContext(RunContextWrapper[TContext]):
|
|
|
24
28
|
tool_call_id: str = field(default_factory=_assert_must_pass_tool_call_id)
|
|
25
29
|
"""The ID of the tool call."""
|
|
26
30
|
|
|
31
|
+
tool_arguments: str = field(default_factory=_assert_must_pass_tool_arguments)
|
|
32
|
+
"""The raw arguments string of the tool call."""
|
|
33
|
+
|
|
27
34
|
@classmethod
|
|
28
35
|
def from_agent_context(
|
|
29
36
|
cls,
|
|
@@ -39,4 +46,10 @@ class ToolContext(RunContextWrapper[TContext]):
|
|
|
39
46
|
f.name: getattr(context, f.name) for f in fields(RunContextWrapper) if f.init
|
|
40
47
|
}
|
|
41
48
|
tool_name = tool_call.name if tool_call is not None else _assert_must_pass_tool_name()
|
|
42
|
-
|
|
49
|
+
tool_args = (
|
|
50
|
+
tool_call.arguments if tool_call is not None else _assert_must_pass_tool_arguments()
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return cls(
|
|
54
|
+
tool_name=tool_name, tool_call_id=tool_call_id, tool_arguments=tool_args, **base_values
|
|
55
|
+
)
|
|
@@ -7,52 +7,125 @@ if TYPE_CHECKING:
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TracingProcessor(abc.ABC):
|
|
10
|
-
"""Interface for processing spans.
|
|
10
|
+
"""Interface for processing and monitoring traces and spans in the OpenAI Agents system.
|
|
11
|
+
|
|
12
|
+
This abstract class defines the interface that all tracing processors must implement.
|
|
13
|
+
Processors receive notifications when traces and spans start and end, allowing them
|
|
14
|
+
to collect, process, and export tracing data.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
```python
|
|
18
|
+
class CustomProcessor(TracingProcessor):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.active_traces = {}
|
|
21
|
+
self.active_spans = {}
|
|
22
|
+
|
|
23
|
+
def on_trace_start(self, trace):
|
|
24
|
+
self.active_traces[trace.trace_id] = trace
|
|
25
|
+
|
|
26
|
+
def on_trace_end(self, trace):
|
|
27
|
+
# Process completed trace
|
|
28
|
+
del self.active_traces[trace.trace_id]
|
|
29
|
+
|
|
30
|
+
def on_span_start(self, span):
|
|
31
|
+
self.active_spans[span.span_id] = span
|
|
32
|
+
|
|
33
|
+
def on_span_end(self, span):
|
|
34
|
+
# Process completed span
|
|
35
|
+
del self.active_spans[span.span_id]
|
|
36
|
+
|
|
37
|
+
def shutdown(self):
|
|
38
|
+
# Clean up resources
|
|
39
|
+
self.active_traces.clear()
|
|
40
|
+
self.active_spans.clear()
|
|
41
|
+
|
|
42
|
+
def force_flush(self):
|
|
43
|
+
# Force processing of any queued items
|
|
44
|
+
pass
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Notes:
|
|
48
|
+
- All methods should be thread-safe
|
|
49
|
+
- Methods should not block for long periods
|
|
50
|
+
- Handle errors gracefully to prevent disrupting agent execution
|
|
51
|
+
"""
|
|
11
52
|
|
|
12
53
|
@abc.abstractmethod
|
|
13
54
|
def on_trace_start(self, trace: "Trace") -> None:
|
|
14
|
-
"""Called when a trace
|
|
55
|
+
"""Called when a new trace begins execution.
|
|
15
56
|
|
|
16
57
|
Args:
|
|
17
|
-
trace: The trace that started.
|
|
58
|
+
trace: The trace that started. Contains workflow name and metadata.
|
|
59
|
+
|
|
60
|
+
Notes:
|
|
61
|
+
- Called synchronously on trace start
|
|
62
|
+
- Should return quickly to avoid blocking execution
|
|
63
|
+
- Any errors should be caught and handled internally
|
|
18
64
|
"""
|
|
19
65
|
pass
|
|
20
66
|
|
|
21
67
|
@abc.abstractmethod
|
|
22
68
|
def on_trace_end(self, trace: "Trace") -> None:
|
|
23
|
-
"""Called when a trace
|
|
69
|
+
"""Called when a trace completes execution.
|
|
24
70
|
|
|
25
71
|
Args:
|
|
26
|
-
trace: The trace
|
|
72
|
+
trace: The completed trace containing all spans and results.
|
|
73
|
+
|
|
74
|
+
Notes:
|
|
75
|
+
- Called synchronously when trace finishes
|
|
76
|
+
- Good time to export/process the complete trace
|
|
77
|
+
- Should handle cleanup of any trace-specific resources
|
|
27
78
|
"""
|
|
28
79
|
pass
|
|
29
80
|
|
|
30
81
|
@abc.abstractmethod
|
|
31
82
|
def on_span_start(self, span: "Span[Any]") -> None:
|
|
32
|
-
"""Called when a span
|
|
83
|
+
"""Called when a new span begins execution.
|
|
33
84
|
|
|
34
85
|
Args:
|
|
35
|
-
span: The span that started.
|
|
86
|
+
span: The span that started. Contains operation details and context.
|
|
87
|
+
|
|
88
|
+
Notes:
|
|
89
|
+
- Called synchronously on span start
|
|
90
|
+
- Should return quickly to avoid blocking execution
|
|
91
|
+
- Spans are automatically nested under current trace/span
|
|
36
92
|
"""
|
|
37
93
|
pass
|
|
38
94
|
|
|
39
95
|
@abc.abstractmethod
|
|
40
96
|
def on_span_end(self, span: "Span[Any]") -> None:
|
|
41
|
-
"""Called when a span
|
|
97
|
+
"""Called when a span completes execution.
|
|
42
98
|
|
|
43
99
|
Args:
|
|
44
|
-
span: The span
|
|
100
|
+
span: The completed span containing execution results.
|
|
101
|
+
|
|
102
|
+
Notes:
|
|
103
|
+
- Called synchronously when span finishes
|
|
104
|
+
- Should not block or raise exceptions
|
|
105
|
+
- Good time to export/process the individual span
|
|
45
106
|
"""
|
|
46
107
|
pass
|
|
47
108
|
|
|
48
109
|
@abc.abstractmethod
|
|
49
110
|
def shutdown(self) -> None:
|
|
50
|
-
"""Called when the application stops.
|
|
111
|
+
"""Called when the application stops to clean up resources.
|
|
112
|
+
|
|
113
|
+
Should perform any necessary cleanup like:
|
|
114
|
+
- Flushing queued traces/spans
|
|
115
|
+
- Closing connections
|
|
116
|
+
- Releasing resources
|
|
117
|
+
"""
|
|
51
118
|
pass
|
|
52
119
|
|
|
53
120
|
@abc.abstractmethod
|
|
54
121
|
def force_flush(self) -> None:
|
|
55
|
-
"""Forces
|
|
122
|
+
"""Forces immediate processing of any queued traces/spans.
|
|
123
|
+
|
|
124
|
+
Notes:
|
|
125
|
+
- Should process all queued items before returning
|
|
126
|
+
- Useful before shutdown or when immediate processing is needed
|
|
127
|
+
- May block while processing completes
|
|
128
|
+
"""
|
|
56
129
|
pass
|
|
57
130
|
|
|
58
131
|
|