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.

Files changed (40) hide show
  1. agents/_debug.py +15 -4
  2. agents/_run_impl.py +34 -37
  3. agents/agent.py +18 -2
  4. agents/extensions/handoff_filters.py +2 -0
  5. agents/extensions/memory/__init__.py +42 -15
  6. agents/extensions/memory/encrypt_session.py +185 -0
  7. agents/extensions/models/litellm_model.py +62 -10
  8. agents/function_schema.py +45 -3
  9. agents/memory/__init__.py +2 -0
  10. agents/memory/openai_conversations_session.py +0 -3
  11. agents/memory/util.py +20 -0
  12. agents/models/chatcmpl_converter.py +74 -15
  13. agents/models/chatcmpl_helpers.py +6 -0
  14. agents/models/chatcmpl_stream_handler.py +29 -1
  15. agents/models/openai_chatcompletions.py +26 -4
  16. agents/models/openai_responses.py +30 -4
  17. agents/realtime/__init__.py +2 -0
  18. agents/realtime/_util.py +1 -1
  19. agents/realtime/agent.py +7 -0
  20. agents/realtime/audio_formats.py +29 -0
  21. agents/realtime/config.py +32 -4
  22. agents/realtime/items.py +17 -1
  23. agents/realtime/model_events.py +2 -0
  24. agents/realtime/model_inputs.py +15 -1
  25. agents/realtime/openai_realtime.py +421 -130
  26. agents/realtime/session.py +167 -14
  27. agents/result.py +47 -20
  28. agents/run.py +191 -106
  29. agents/tool.py +1 -1
  30. agents/tracing/processor_interface.py +84 -11
  31. agents/tracing/spans.py +88 -0
  32. agents/tracing/traces.py +99 -16
  33. agents/util/_json.py +19 -1
  34. agents/util/_transforms.py +12 -2
  35. agents/voice/input.py +5 -4
  36. agents/voice/models/openai_stt.py +15 -8
  37. {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/METADATA +4 -2
  38. {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/RECORD +40 -37
  39. {openai_agents-0.2.11.dist-info → openai_agents-0.3.1.dist-info}/WHEEL +0 -0
  40. {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 AgentUpdatedStreamEvent, RawResponsesStreamEvent, RunItemStreamEvent
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
- """Run a workflow starting at the given agent. The agent will run in a loop until a final
234
- output is generated. The loop runs like so:
235
- 1. The agent is invoked with the given input.
236
- 2. If there is a final output (i.e. the agent produces something of type
237
- `agent.output_type`, the loop terminates.
238
- 3. If there's a handoff, we run the loop again, with the new agent.
239
- 4. Else, we run tool calls (if any), and re-run the loop.
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
- 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
242
- 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised.
243
- Note that only the first agent's input guardrails are run.
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 user message,
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 defined as one
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, if using OpenAI models via the
254
- Responses API, this allows you to skip passing in input from the previous turn.
255
- conversation_id: The conversation ID (https://platform.openai.com/docs/guides/conversation-state?api-mode=responses).
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 it's output items will be written to the conversation.
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 the last
264
- agent. Agents may perform handoffs, so we don't know the specific type of the output.
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
- """Run a workflow synchronously, starting at the given agent. Note that this just wraps the
294
- `run` method, so it will not work if there's already an event loop (e.g. inside an async
295
- function, or in a Jupyter notebook or async context like FastAPI). For those cases, use
296
- the `run` method instead.
297
- The agent will run in a loop until a final output is generated. The loop runs like so:
298
- 1. The agent is invoked with the given input.
299
- 2. If there is a final output (i.e. the agent produces something of type
300
- `agent.output_type`, the loop terminates.
301
- 3. If there's a handoff, we run the loop again, with the new agent.
302
- 4. Else, we run tool calls (if any), and re-run the loop.
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
- 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
305
- 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised.
306
- Note that only the first agent's input guardrails are run.
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 user message,
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 defined as one
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 models via the
317
- Responses API, this allows you to skip passing in input from the previous turn.
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 the last
321
- agent. Agents may perform handoffs, so we don't know the specific type of the output.
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
- """Run a workflow starting at the given agent in streaming mode. The returned result object
350
- contains a method you can use to stream semantic events as they are generated.
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
- 1. The agent is invoked with the given input.
353
- 2. If there is a final output (i.e. the agent produces something of type
354
- `agent.output_type`, the loop terminates.
355
- 3. If there's a handoff, we run the loop again, with the new agent.
356
- 4. Else, we run tool calls (if any), and re-run the loop.
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
- 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised.
359
- 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised.
360
- Note that only the first agent's input guardrails are run.
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 user message,
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 defined as one
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 models via the
371
- Responses API, this allows you to skip passing in input from the previous turn.
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 stream events.
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
- # Prepare input with session if enabled
415
- prepared_input = await self._prepare_input_with_session(input, session)
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
- pass
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(starting_input, 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, starting_input, temp_result
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
- pass
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
- if emitted_tool_call_ids:
1095
- import dataclasses as _dc
1154
+ import dataclasses as _dc
1096
1155
 
1097
- filtered_items = [
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 single_step_result.new_step_items
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
- single_step_result_filtered = _dc.replace(
1112
- single_step_result, new_step_items=filtered_items
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
- RunImpl.stream_step_result_to_queue(
1116
- single_step_result_filtered, streamed_result._event_queue
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
- # Validate that we don't have both a session and a list input, as this creates
1488
- # ambiguity about whether the list should append to or replace existing session history
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
- "Cannot provide both a session and a list of input items. "
1492
- "When using session memory, provide only a string input to append to the "
1493
- "conversation, or use session=None and provide a list to manually manage "
1494
- "conversation history."
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
- # Combine history with new input
1504
- combined_input = history + new_input_list
1505
-
1506
- return combined_input
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
- result: RunResult,
1594
+ new_items: list[RunItem],
1514
1595
  ) -> None:
1515
- """Save the conversation turn to session."""
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 result.new_items]
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
@@ -142,7 +142,7 @@ class WebSearchTool:
142
142
 
143
143
  @property
144
144
  def name(self):
145
- return "web_search_preview"
145
+ return "web_search"
146
146
 
147
147
 
148
148
  @dataclass
@@ -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 is started.
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 is finished.
69
+ """Called when a trace completes execution.
24
70
 
25
71
  Args:
26
- trace: The trace that finished.
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 is started.
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 is finished. Should not block or raise exceptions.
97
+ """Called when a span completes execution.
42
98
 
43
99
  Args:
44
- span: The span that finished.
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 an immediate flush of all queued spans/traces."""
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