openai-agents 0.2.11__py3-none-any.whl → 0.3.1__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/_debug.py +15 -4
- agents/_run_impl.py +34 -37
- 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 +62 -10
- agents/function_schema.py +45 -3
- agents/memory/__init__.py +2 -0
- agents/memory/openai_conversations_session.py +0 -3
- agents/memory/util.py +20 -0
- agents/models/chatcmpl_converter.py +74 -15
- agents/models/chatcmpl_helpers.py +6 -0
- agents/models/chatcmpl_stream_handler.py +29 -1
- agents/models/openai_chatcompletions.py +26 -4
- agents/models/openai_responses.py +30 -4
- agents/realtime/__init__.py +2 -0
- agents/realtime/_util.py +1 -1
- agents/realtime/agent.py +7 -0
- agents/realtime/audio_formats.py +29 -0
- agents/realtime/config.py +32 -4
- agents/realtime/items.py +17 -1
- agents/realtime/model_events.py +2 -0
- agents/realtime/model_inputs.py +15 -1
- agents/realtime/openai_realtime.py +421 -130
- agents/realtime/session.py +167 -14
- agents/result.py +47 -20
- agents/run.py +191 -106
- agents/tool.py +1 -1
- agents/tracing/processor_interface.py +84 -11
- agents/tracing/spans.py +88 -0
- agents/tracing/traces.py +99 -16
- agents/util/_json.py +19 -1
- agents/util/_transforms.py +12 -2
- agents/voice/input.py +5 -4
- agents/voice/models/openai_stt.py +15 -8
- {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/METADATA +4 -2
- {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/RECORD +40 -37
- {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/WHEEL +0 -0
- {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.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,
|
|
@@ -54,13 +55,18 @@ from .items import (
|
|
|
54
55
|
)
|
|
55
56
|
from .lifecycle import RunHooks
|
|
56
57
|
from .logger import logger
|
|
57
|
-
from .memory import Session
|
|
58
|
+
from .memory import Session, SessionInputCallback
|
|
58
59
|
from .model_settings import ModelSettings
|
|
59
60
|
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
|
|
@@ -179,6 +185,13 @@ class RunConfig:
|
|
|
179
185
|
An optional dictionary of additional metadata to include with the trace.
|
|
180
186
|
"""
|
|
181
187
|
|
|
188
|
+
session_input_callback: SessionInputCallback | None = None
|
|
189
|
+
"""Defines how to handle session history when new input is provided.
|
|
190
|
+
- `None` (default): The new input is appended to the session history.
|
|
191
|
+
- `SessionInputCallback`: A custom function that receives the history and new input, and
|
|
192
|
+
returns the desired combined list of items.
|
|
193
|
+
"""
|
|
194
|
+
|
|
182
195
|
call_model_input_filter: CallModelInputFilter | None = None
|
|
183
196
|
"""
|
|
184
197
|
Optional callback that is invoked immediately before calling the model. It receives the current
|
|
@@ -230,39 +243,54 @@ class Runner:
|
|
|
230
243
|
conversation_id: str | None = None,
|
|
231
244
|
session: Session | None = None,
|
|
232
245
|
) -> RunResult:
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
|
|
240
257
|
In two cases, the agent may raise an exception:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
+
|
|
244
266
|
Args:
|
|
245
267
|
starting_agent: The starting agent to run.
|
|
246
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
247
|
-
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.
|
|
248
270
|
context: The context to run the agent with.
|
|
249
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
250
|
-
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).
|
|
251
273
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
252
274
|
run_config: Global settings for the entire agent run.
|
|
253
|
-
previous_response_id: The ID of the previous response
|
|
254
|
-
Responses API, this allows you to skip passing in input
|
|
255
|
-
|
|
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).
|
|
256
280
|
If provided, the conversation will be used to read and write items.
|
|
257
281
|
Every agent will have access to the conversation history so far,
|
|
258
|
-
and
|
|
282
|
+
and its output items will be written to the conversation.
|
|
259
283
|
We recommend only using this if you are exclusively using OpenAI models;
|
|
260
284
|
other model providers don't write to the Conversation object,
|
|
261
285
|
so you'll end up having partial conversations stored.
|
|
286
|
+
session: A session for automatic conversation history management.
|
|
287
|
+
|
|
262
288
|
Returns:
|
|
263
|
-
A run result containing all the inputs, guardrail results and the output of
|
|
264
|
-
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.
|
|
265
292
|
"""
|
|
293
|
+
|
|
266
294
|
runner = DEFAULT_AGENT_RUNNER
|
|
267
295
|
return await runner.run(
|
|
268
296
|
starting_agent,
|
|
@@ -290,36 +318,52 @@ class Runner:
|
|
|
290
318
|
conversation_id: str | None = None,
|
|
291
319
|
session: Session | None = None,
|
|
292
320
|
) -> RunResult:
|
|
293
|
-
"""
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
+
|
|
303
337
|
In two cases, the agent may raise an exception:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
+
|
|
307
346
|
Args:
|
|
308
347
|
starting_agent: The starting agent to run.
|
|
309
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
310
|
-
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.
|
|
311
350
|
context: The context to run the agent with.
|
|
312
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
313
|
-
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).
|
|
314
353
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
315
354
|
run_config: Global settings for the entire agent run.
|
|
316
|
-
previous_response_id: The ID of the previous response, if using OpenAI
|
|
317
|
-
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.
|
|
318
358
|
conversation_id: The ID of the stored conversation, if any.
|
|
359
|
+
session: A session for automatic conversation history management.
|
|
360
|
+
|
|
319
361
|
Returns:
|
|
320
|
-
A run result containing all the inputs, guardrail results and the output of
|
|
321
|
-
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.
|
|
322
365
|
"""
|
|
366
|
+
|
|
323
367
|
runner = DEFAULT_AGENT_RUNNER
|
|
324
368
|
return runner.run_sync(
|
|
325
369
|
starting_agent,
|
|
@@ -346,33 +390,49 @@ class Runner:
|
|
|
346
390
|
conversation_id: str | None = None,
|
|
347
391
|
session: Session | None = None,
|
|
348
392
|
) -> RunResultStreaming:
|
|
349
|
-
"""
|
|
350
|
-
|
|
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
|
+
|
|
351
399
|
The agent will run in a loop until a final output is generated. The loop runs like so:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
+
|
|
357
407
|
In two cases, the agent may raise an exception:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
+
|
|
361
416
|
Args:
|
|
362
417
|
starting_agent: The starting agent to run.
|
|
363
|
-
input: The initial input to the agent. You can pass a single string for a
|
|
364
|
-
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.
|
|
365
420
|
context: The context to run the agent with.
|
|
366
|
-
max_turns: The maximum number of turns to run the agent for. A turn is
|
|
367
|
-
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).
|
|
368
423
|
hooks: An object that receives callbacks on various lifecycle events.
|
|
369
424
|
run_config: Global settings for the entire agent run.
|
|
370
|
-
previous_response_id: The ID of the previous response, if using OpenAI
|
|
371
|
-
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.
|
|
372
428
|
conversation_id: The ID of the stored conversation, if any.
|
|
429
|
+
session: A session for automatic conversation history management.
|
|
430
|
+
|
|
373
431
|
Returns:
|
|
374
|
-
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.
|
|
375
434
|
"""
|
|
435
|
+
|
|
376
436
|
runner = DEFAULT_AGENT_RUNNER
|
|
377
437
|
return runner.run_streamed(
|
|
378
438
|
starting_agent,
|
|
@@ -411,8 +471,11 @@ class AgentRunner:
|
|
|
411
471
|
if run_config is None:
|
|
412
472
|
run_config = RunConfig()
|
|
413
473
|
|
|
414
|
-
#
|
|
415
|
-
|
|
474
|
+
# Keep original user input separate from session-prepared input
|
|
475
|
+
original_user_input = input
|
|
476
|
+
prepared_input = await self._prepare_input_with_session(
|
|
477
|
+
input, session, run_config.session_input_callback
|
|
478
|
+
)
|
|
416
479
|
|
|
417
480
|
tool_use_tracker = AgentToolUseTracker()
|
|
418
481
|
|
|
@@ -438,6 +501,9 @@ class AgentRunner:
|
|
|
438
501
|
current_agent = starting_agent
|
|
439
502
|
should_run_agent_start_hooks = True
|
|
440
503
|
|
|
504
|
+
# save only the new user input to the session, not the combined history
|
|
505
|
+
await self._save_result_to_session(session, original_user_input, [])
|
|
506
|
+
|
|
441
507
|
try:
|
|
442
508
|
while True:
|
|
443
509
|
all_tools = await AgentRunner._get_all_tools(current_agent, context_wrapper)
|
|
@@ -537,9 +603,7 @@ class AgentRunner:
|
|
|
537
603
|
output_guardrail_results=output_guardrail_results,
|
|
538
604
|
context_wrapper=context_wrapper,
|
|
539
605
|
)
|
|
540
|
-
|
|
541
|
-
# Save the conversation to session if enabled
|
|
542
|
-
await self._save_result_to_session(session, input, result)
|
|
606
|
+
await self._save_result_to_session(session, [], turn_result.new_step_items)
|
|
543
607
|
|
|
544
608
|
return result
|
|
545
609
|
elif isinstance(turn_result.next_step, NextStepHandoff):
|
|
@@ -548,7 +612,7 @@ class AgentRunner:
|
|
|
548
612
|
current_span = None
|
|
549
613
|
should_run_agent_start_hooks = True
|
|
550
614
|
elif isinstance(turn_result.next_step, NextStepRunAgain):
|
|
551
|
-
|
|
615
|
+
await self._save_result_to_session(session, [], turn_result.new_step_items)
|
|
552
616
|
else:
|
|
553
617
|
raise AgentsException(
|
|
554
618
|
f"Unknown next step type: {type(turn_result.next_step)}"
|
|
@@ -779,11 +843,15 @@ class AgentRunner:
|
|
|
779
843
|
|
|
780
844
|
try:
|
|
781
845
|
# Prepare input with session if enabled
|
|
782
|
-
prepared_input = await AgentRunner._prepare_input_with_session(
|
|
846
|
+
prepared_input = await AgentRunner._prepare_input_with_session(
|
|
847
|
+
starting_input, session, run_config.session_input_callback
|
|
848
|
+
)
|
|
783
849
|
|
|
784
850
|
# Update the streamed result with the prepared input
|
|
785
851
|
streamed_result.input = prepared_input
|
|
786
852
|
|
|
853
|
+
await AgentRunner._save_result_to_session(session, starting_input, [])
|
|
854
|
+
|
|
787
855
|
while True:
|
|
788
856
|
if streamed_result.is_complete:
|
|
789
857
|
break
|
|
@@ -887,24 +955,15 @@ class AgentRunner:
|
|
|
887
955
|
streamed_result.is_complete = True
|
|
888
956
|
|
|
889
957
|
# Save the conversation to session if enabled
|
|
890
|
-
# Create a temporary RunResult for session saving
|
|
891
|
-
temp_result = RunResult(
|
|
892
|
-
input=streamed_result.input,
|
|
893
|
-
new_items=streamed_result.new_items,
|
|
894
|
-
raw_responses=streamed_result.raw_responses,
|
|
895
|
-
final_output=streamed_result.final_output,
|
|
896
|
-
_last_agent=current_agent,
|
|
897
|
-
input_guardrail_results=streamed_result.input_guardrail_results,
|
|
898
|
-
output_guardrail_results=streamed_result.output_guardrail_results,
|
|
899
|
-
context_wrapper=context_wrapper,
|
|
900
|
-
)
|
|
901
958
|
await AgentRunner._save_result_to_session(
|
|
902
|
-
session,
|
|
959
|
+
session, [], turn_result.new_step_items
|
|
903
960
|
)
|
|
904
961
|
|
|
905
962
|
streamed_result._event_queue.put_nowait(QueueCompleteSentinel())
|
|
906
963
|
elif isinstance(turn_result.next_step, NextStepRunAgain):
|
|
907
|
-
|
|
964
|
+
await AgentRunner._save_result_to_session(
|
|
965
|
+
session, [], turn_result.new_step_items
|
|
966
|
+
)
|
|
908
967
|
except AgentsException as exc:
|
|
909
968
|
streamed_result.is_complete = True
|
|
910
969
|
streamed_result._event_queue.put_nowait(QueueCompleteSentinel())
|
|
@@ -1089,14 +1148,19 @@ class AgentRunner:
|
|
|
1089
1148
|
context_wrapper=context_wrapper,
|
|
1090
1149
|
run_config=run_config,
|
|
1091
1150
|
tool_use_tracker=tool_use_tracker,
|
|
1151
|
+
event_queue=streamed_result._event_queue,
|
|
1092
1152
|
)
|
|
1093
1153
|
|
|
1094
|
-
|
|
1095
|
-
import dataclasses as _dc
|
|
1154
|
+
import dataclasses as _dc
|
|
1096
1155
|
|
|
1097
|
-
|
|
1156
|
+
# Filter out items that have already been sent to avoid duplicates
|
|
1157
|
+
items_to_filter = single_step_result.new_step_items
|
|
1158
|
+
|
|
1159
|
+
if emitted_tool_call_ids:
|
|
1160
|
+
# Filter out tool call items that were already emitted during streaming
|
|
1161
|
+
items_to_filter = [
|
|
1098
1162
|
item
|
|
1099
|
-
for item in
|
|
1163
|
+
for item in items_to_filter
|
|
1100
1164
|
if not (
|
|
1101
1165
|
isinstance(item, ToolCallItem)
|
|
1102
1166
|
and (
|
|
@@ -1108,15 +1172,14 @@ class AgentRunner:
|
|
|
1108
1172
|
)
|
|
1109
1173
|
]
|
|
1110
1174
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
)
|
|
1175
|
+
# Filter out HandoffCallItem to avoid duplicates (already sent earlier)
|
|
1176
|
+
items_to_filter = [
|
|
1177
|
+
item for item in items_to_filter if not isinstance(item, HandoffCallItem)
|
|
1178
|
+
]
|
|
1114
1179
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
else:
|
|
1119
|
-
RunImpl.stream_step_result_to_queue(single_step_result, streamed_result._event_queue)
|
|
1180
|
+
# Create filtered result and send to queue
|
|
1181
|
+
filtered_result = _dc.replace(single_step_result, new_step_items=items_to_filter)
|
|
1182
|
+
RunImpl.stream_step_result_to_queue(filtered_result, streamed_result._event_queue)
|
|
1120
1183
|
return single_step_result
|
|
1121
1184
|
|
|
1122
1185
|
@classmethod
|
|
@@ -1201,6 +1264,7 @@ class AgentRunner:
|
|
|
1201
1264
|
context_wrapper: RunContextWrapper[TContext],
|
|
1202
1265
|
run_config: RunConfig,
|
|
1203
1266
|
tool_use_tracker: AgentToolUseTracker,
|
|
1267
|
+
event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] | None = None,
|
|
1204
1268
|
) -> SingleStepResult:
|
|
1205
1269
|
processed_response = RunImpl.process_model_response(
|
|
1206
1270
|
agent=agent,
|
|
@@ -1212,6 +1276,14 @@ class AgentRunner:
|
|
|
1212
1276
|
|
|
1213
1277
|
tool_use_tracker.add_tool_use(agent, processed_response.tools_used)
|
|
1214
1278
|
|
|
1279
|
+
# Send handoff items immediately for streaming, but avoid duplicates
|
|
1280
|
+
if event_queue is not None and processed_response.new_items:
|
|
1281
|
+
handoff_items = [
|
|
1282
|
+
item for item in processed_response.new_items if isinstance(item, HandoffCallItem)
|
|
1283
|
+
]
|
|
1284
|
+
if handoff_items:
|
|
1285
|
+
RunImpl.stream_step_items_to_queue(cast(list[RunItem], handoff_items), event_queue)
|
|
1286
|
+
|
|
1215
1287
|
return await RunImpl.execute_tools_and_side_effects(
|
|
1216
1288
|
agent=agent,
|
|
1217
1289
|
original_input=original_input,
|
|
@@ -1479,19 +1551,20 @@ class AgentRunner:
|
|
|
1479
1551
|
cls,
|
|
1480
1552
|
input: str | list[TResponseInputItem],
|
|
1481
1553
|
session: Session | None,
|
|
1554
|
+
session_input_callback: SessionInputCallback | None,
|
|
1482
1555
|
) -> str | list[TResponseInputItem]:
|
|
1483
1556
|
"""Prepare input by combining it with session history if enabled."""
|
|
1484
1557
|
if session is None:
|
|
1485
1558
|
return input
|
|
1486
1559
|
|
|
1487
|
-
#
|
|
1488
|
-
|
|
1489
|
-
if isinstance(input, list):
|
|
1560
|
+
# If the user doesn't specify an input callback and pass a list as input
|
|
1561
|
+
if isinstance(input, list) and not session_input_callback:
|
|
1490
1562
|
raise UserError(
|
|
1491
|
-
"
|
|
1492
|
-
"
|
|
1493
|
-
"
|
|
1494
|
-
"
|
|
1563
|
+
"When using session memory, list inputs require a "
|
|
1564
|
+
"`RunConfig.session_input_callback` to define how they should be merged "
|
|
1565
|
+
"with the conversation history. If you don't want to use a callback, "
|
|
1566
|
+
"provide your input as a string instead, or disable session memory "
|
|
1567
|
+
"(session=None) and pass a list to manage the history manually."
|
|
1495
1568
|
)
|
|
1496
1569
|
|
|
1497
1570
|
# Get previous conversation history
|
|
@@ -1500,19 +1573,31 @@ class AgentRunner:
|
|
|
1500
1573
|
# Convert input to list format
|
|
1501
1574
|
new_input_list = ItemHelpers.input_to_new_input_list(input)
|
|
1502
1575
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1576
|
+
if session_input_callback is None:
|
|
1577
|
+
return history + new_input_list
|
|
1578
|
+
elif callable(session_input_callback):
|
|
1579
|
+
res = session_input_callback(history, new_input_list)
|
|
1580
|
+
if inspect.isawaitable(res):
|
|
1581
|
+
return await res
|
|
1582
|
+
return res
|
|
1583
|
+
else:
|
|
1584
|
+
raise UserError(
|
|
1585
|
+
f"Invalid `session_input_callback` value: {session_input_callback}. "
|
|
1586
|
+
"Choose between `None` or a custom callable function."
|
|
1587
|
+
)
|
|
1507
1588
|
|
|
1508
1589
|
@classmethod
|
|
1509
1590
|
async def _save_result_to_session(
|
|
1510
1591
|
cls,
|
|
1511
1592
|
session: Session | None,
|
|
1512
1593
|
original_input: str | list[TResponseInputItem],
|
|
1513
|
-
|
|
1594
|
+
new_items: list[RunItem],
|
|
1514
1595
|
) -> None:
|
|
1515
|
-
"""
|
|
1596
|
+
"""
|
|
1597
|
+
Save the conversation turn to session.
|
|
1598
|
+
It does not account for any filtering or modification performed by
|
|
1599
|
+
`RunConfig.session_input_callback`.
|
|
1600
|
+
"""
|
|
1516
1601
|
if session is None:
|
|
1517
1602
|
return
|
|
1518
1603
|
|
|
@@ -1520,7 +1605,7 @@ class AgentRunner:
|
|
|
1520
1605
|
input_list = ItemHelpers.input_to_new_input_list(original_input)
|
|
1521
1606
|
|
|
1522
1607
|
# Convert new items to input format
|
|
1523
|
-
new_items_as_input = [item.to_input_item() for item in
|
|
1608
|
+
new_items_as_input = [item.to_input_item() for item in new_items]
|
|
1524
1609
|
|
|
1525
1610
|
# Save all items from this turn
|
|
1526
1611
|
items_to_save = input_list + new_items_as_input
|
agents/tool.py
CHANGED
|
@@ -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
|
|