openai-agents 0.2.7__py3-none-any.whl → 0.2.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/items.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import abc
4
- import copy
5
4
  from dataclasses import dataclass
6
5
  from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union
7
6
 
@@ -277,7 +276,7 @@ class ItemHelpers:
277
276
  "role": "user",
278
277
  }
279
278
  ]
280
- return copy.deepcopy(input)
279
+ return input.copy()
281
280
 
282
281
  @classmethod
283
282
  def text_message_outputs(cls, items: list[RunItem]) -> str:
@@ -170,7 +170,10 @@ class OpenAIRealtimeWebSocketModel(RealtimeModel):
170
170
  "OpenAI-Beta": "realtime=v1",
171
171
  }
172
172
  self._websocket = await websockets.connect(
173
- url, user_agent_header=_USER_AGENT, additional_headers=headers
173
+ url,
174
+ user_agent_header=_USER_AGENT,
175
+ additional_headers=headers,
176
+ max_size=None, # Allow any size of message
174
177
  )
175
178
  self._websocket_task = asyncio.create_task(self._listen_for_messages())
176
179
  await self._update_session_config(model_settings)
@@ -10,6 +10,7 @@ from typing_extensions import assert_never
10
10
  from ..agent import Agent
11
11
  from ..exceptions import ModelBehaviorError, UserError
12
12
  from ..handoffs import Handoff
13
+ from ..logger import logger
13
14
  from ..run_context import RunContextWrapper, TContext
14
15
  from ..tool import FunctionTool
15
16
  from ..tool_context import ToolContext
@@ -33,7 +34,7 @@ from .events import (
33
34
  RealtimeToolStart,
34
35
  )
35
36
  from .handoffs import realtime_handoff
36
- from .items import InputAudio, InputText, RealtimeItem
37
+ from .items import AssistantAudio, InputAudio, InputText, RealtimeItem
37
38
  from .model import RealtimeModel, RealtimeModelConfig, RealtimeModelListener
38
39
  from .model_events import (
39
40
  RealtimeModelEvent,
@@ -246,7 +247,58 @@ class RealtimeSession(RealtimeModelListener):
246
247
  self._enqueue_guardrail_task(self._item_transcripts[item_id], event.response_id)
247
248
  elif event.type == "item_updated":
248
249
  is_new = not any(item.item_id == event.item.item_id for item in self._history)
249
- self._history = self._get_new_history(self._history, event.item)
250
+
251
+ # Preserve previously known transcripts when updating existing items.
252
+ # This prevents transcripts from disappearing when an item is later
253
+ # retrieved without transcript fields populated.
254
+ incoming_item = event.item
255
+ existing_item = next(
256
+ (i for i in self._history if i.item_id == incoming_item.item_id), None
257
+ )
258
+
259
+ if (
260
+ existing_item is not None
261
+ and existing_item.type == "message"
262
+ and incoming_item.type == "message"
263
+ ):
264
+ try:
265
+ # Merge transcripts for matching content indices
266
+ existing_content = existing_item.content
267
+ new_content = []
268
+ for idx, entry in enumerate(incoming_item.content):
269
+ # Only attempt to preserve for audio-like content
270
+ if entry.type in ("audio", "input_audio"):
271
+ # Use tuple form for Python 3.9 compatibility
272
+ assert isinstance(entry, (InputAudio, AssistantAudio))
273
+ # Determine if transcript is missing/empty on the incoming entry
274
+ entry_transcript = entry.transcript
275
+ if not entry_transcript:
276
+ preserved: str | None = None
277
+ # First prefer any transcript from the existing history item
278
+ if idx < len(existing_content):
279
+ this_content = existing_content[idx]
280
+ if isinstance(this_content, AssistantAudio) or isinstance(
281
+ this_content, InputAudio
282
+ ):
283
+ preserved = this_content.transcript
284
+
285
+ # If still missing and this is an assistant item, fall back to
286
+ # accumulated transcript deltas tracked during the turn.
287
+ if not preserved and incoming_item.role == "assistant":
288
+ preserved = self._item_transcripts.get(incoming_item.item_id)
289
+
290
+ if preserved:
291
+ entry = entry.model_copy(update={"transcript": preserved})
292
+
293
+ new_content.append(entry)
294
+
295
+ if new_content:
296
+ incoming_item = incoming_item.model_copy(update={"content": new_content})
297
+ except Exception:
298
+ logger.error("Error merging transcripts", exc_info=True)
299
+ pass
300
+
301
+ self._history = self._get_new_history(self._history, incoming_item)
250
302
  if is_new:
251
303
  new_item = next(
252
304
  item for item in self._history if item.item_id == event.item.item_id
agents/run.py CHANGED
@@ -1,10 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import copy
5
4
  import inspect
6
5
  from dataclasses import dataclass, field
7
- from typing import Any, Generic, cast
6
+ from typing import Any, Callable, Generic, cast
8
7
 
9
8
  from openai.types.responses import ResponseCompletedEvent
10
9
  from openai.types.responses.response_prompt_param import (
@@ -56,6 +55,7 @@ from .tracing import Span, SpanError, agent_span, get_current_trace, trace
56
55
  from .tracing.span_data import AgentSpanData
57
56
  from .usage import Usage
58
57
  from .util import _coro, _error_tracing
58
+ from .util._types import MaybeAwaitable
59
59
 
60
60
  DEFAULT_MAX_TURNS = 10
61
61
 
@@ -81,6 +81,27 @@ def get_default_agent_runner() -> AgentRunner:
81
81
  return DEFAULT_AGENT_RUNNER
82
82
 
83
83
 
84
+ @dataclass
85
+ class ModelInputData:
86
+ """Container for the data that will be sent to the model."""
87
+
88
+ input: list[TResponseInputItem]
89
+ instructions: str | None
90
+
91
+
92
+ @dataclass
93
+ class CallModelData(Generic[TContext]):
94
+ """Data passed to `RunConfig.call_model_input_filter` prior to model call."""
95
+
96
+ model_data: ModelInputData
97
+ agent: Agent[TContext]
98
+ context: TContext | None
99
+
100
+
101
+ # Type alias for the optional input filter callback
102
+ CallModelInputFilter = Callable[[CallModelData[Any]], MaybeAwaitable[ModelInputData]]
103
+
104
+
84
105
  @dataclass
85
106
  class RunConfig:
86
107
  """Configures settings for the entire agent run."""
@@ -139,6 +160,16 @@ class RunConfig:
139
160
  An optional dictionary of additional metadata to include with the trace.
140
161
  """
141
162
 
163
+ call_model_input_filter: CallModelInputFilter | None = None
164
+ """
165
+ Optional callback that is invoked immediately before calling the model. It receives the current
166
+ agent, context and the model input (instructions and input items), and must return a possibly
167
+ modified `ModelInputData` to use for the model call.
168
+
169
+ This allows you to edit the input sent to the model e.g. to stay within a token limit.
170
+ For example, you can use this to add a system prompt to the input.
171
+ """
172
+
142
173
 
143
174
  class RunOptions(TypedDict, Generic[TContext]):
144
175
  """Arguments for ``AgentRunner`` methods."""
@@ -355,7 +386,7 @@ class AgentRunner:
355
386
  disabled=run_config.tracing_disabled,
356
387
  ):
357
388
  current_turn = 0
358
- original_input: str | list[TResponseInputItem] = copy.deepcopy(prepared_input)
389
+ original_input: str | list[TResponseInputItem] = _copy_str_or_list(prepared_input)
359
390
  generated_items: list[RunItem] = []
360
391
  model_responses: list[ModelResponse] = []
361
392
 
@@ -414,7 +445,7 @@ class AgentRunner:
414
445
  starting_agent,
415
446
  starting_agent.input_guardrails
416
447
  + (run_config.input_guardrails or []),
417
- copy.deepcopy(prepared_input),
448
+ _copy_str_or_list(prepared_input),
418
449
  context_wrapper,
419
450
  ),
420
451
  self._run_single_turn(
@@ -562,7 +593,7 @@ class AgentRunner:
562
593
  )
563
594
 
564
595
  streamed_result = RunResultStreaming(
565
- input=copy.deepcopy(input),
596
+ input=_copy_str_or_list(input),
566
597
  new_items=[],
567
598
  current_agent=starting_agent,
568
599
  raw_responses=[],
@@ -593,6 +624,47 @@ class AgentRunner:
593
624
  )
594
625
  return streamed_result
595
626
 
627
+ @classmethod
628
+ async def _maybe_filter_model_input(
629
+ cls,
630
+ *,
631
+ agent: Agent[TContext],
632
+ run_config: RunConfig,
633
+ context_wrapper: RunContextWrapper[TContext],
634
+ input_items: list[TResponseInputItem],
635
+ system_instructions: str | None,
636
+ ) -> ModelInputData:
637
+ """Apply optional call_model_input_filter to modify model input.
638
+
639
+ Returns a `ModelInputData` that will be sent to the model.
640
+ """
641
+ effective_instructions = system_instructions
642
+ effective_input: list[TResponseInputItem] = input_items
643
+
644
+ if run_config.call_model_input_filter is None:
645
+ return ModelInputData(input=effective_input, instructions=effective_instructions)
646
+
647
+ try:
648
+ model_input = ModelInputData(
649
+ input=effective_input.copy(),
650
+ instructions=effective_instructions,
651
+ )
652
+ filter_payload: CallModelData[TContext] = CallModelData(
653
+ model_data=model_input,
654
+ agent=agent,
655
+ context=context_wrapper.context,
656
+ )
657
+ maybe_updated = run_config.call_model_input_filter(filter_payload)
658
+ updated = await maybe_updated if inspect.isawaitable(maybe_updated) else maybe_updated
659
+ if not isinstance(updated, ModelInputData):
660
+ raise UserError("call_model_input_filter must return a ModelInputData instance")
661
+ return updated
662
+ except Exception as e:
663
+ _error_tracing.attach_error_to_current_span(
664
+ SpanError(message="Error in call_model_input_filter", data={"error": str(e)})
665
+ )
666
+ raise
667
+
596
668
  @classmethod
597
669
  async def _run_input_guardrails_with_queue(
598
670
  cls,
@@ -713,7 +785,7 @@ class AgentRunner:
713
785
  cls._run_input_guardrails_with_queue(
714
786
  starting_agent,
715
787
  starting_agent.input_guardrails + (run_config.input_guardrails or []),
716
- copy.deepcopy(ItemHelpers.input_to_new_input_list(prepared_input)),
788
+ ItemHelpers.input_to_new_input_list(prepared_input),
717
789
  context_wrapper,
718
790
  streamed_result,
719
791
  current_span,
@@ -863,10 +935,18 @@ class AgentRunner:
863
935
  input = ItemHelpers.input_to_new_input_list(streamed_result.input)
864
936
  input.extend([item.to_input_item() for item in streamed_result.new_items])
865
937
 
938
+ filtered = await cls._maybe_filter_model_input(
939
+ agent=agent,
940
+ run_config=run_config,
941
+ context_wrapper=context_wrapper,
942
+ input_items=input,
943
+ system_instructions=system_prompt,
944
+ )
945
+
866
946
  # 1. Stream the output events
867
947
  async for event in model.stream_response(
868
- system_prompt,
869
- input,
948
+ filtered.instructions,
949
+ filtered.input,
870
950
  model_settings,
871
951
  all_tools,
872
952
  output_schema,
@@ -1034,7 +1114,6 @@ class AgentRunner:
1034
1114
  run_config: RunConfig,
1035
1115
  tool_use_tracker: AgentToolUseTracker,
1036
1116
  ) -> SingleStepResult:
1037
-
1038
1117
  original_input = streamed_result.input
1039
1118
  pre_step_items = streamed_result.new_items
1040
1119
  event_queue = streamed_result._event_queue
@@ -1161,13 +1240,22 @@ class AgentRunner:
1161
1240
  previous_response_id: str | None,
1162
1241
  prompt_config: ResponsePromptParam | None,
1163
1242
  ) -> ModelResponse:
1243
+ # Allow user to modify model input right before the call, if configured
1244
+ filtered = await cls._maybe_filter_model_input(
1245
+ agent=agent,
1246
+ run_config=run_config,
1247
+ context_wrapper=context_wrapper,
1248
+ input_items=input,
1249
+ system_instructions=system_prompt,
1250
+ )
1251
+
1164
1252
  model = cls._get_model(agent, run_config)
1165
1253
  model_settings = agent.model_settings.resolve(run_config.model_settings)
1166
1254
  model_settings = RunImpl.maybe_reset_tool_choice(agent, tool_use_tracker, model_settings)
1167
1255
 
1168
1256
  new_response = await model.get_response(
1169
- system_instructions=system_prompt,
1170
- input=input,
1257
+ system_instructions=filtered.instructions,
1258
+ input=filtered.input,
1171
1259
  model_settings=model_settings,
1172
1260
  tools=all_tools,
1173
1261
  output_schema=output_schema,
@@ -1287,3 +1375,9 @@ class AgentRunner:
1287
1375
 
1288
1376
 
1289
1377
  DEFAULT_AGENT_RUNNER = AgentRunner()
1378
+
1379
+
1380
+ def _copy_str_or_list(input: str | list[TResponseInputItem]) -> str | list[TResponseInputItem]:
1381
+ if isinstance(input, str):
1382
+ return input
1383
+ return input.copy()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openai-agents
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: OpenAI Agents SDK
5
5
  Project-URL: Homepage, https://openai.github.io/openai-agents-python/
6
6
  Project-URL: Repository, https://github.com/openai/openai-agents-python
@@ -9,7 +9,7 @@ agents/exceptions.py,sha256=NHMdHE0cZ6AdA6UgUylTzVHAX05Ol1CkO814a0FdZcs,2862
9
9
  agents/function_schema.py,sha256=yZ3PEOmfy836Me_W4QlItMeFq2j4BtpuI2FmQswbIcQ,13590
10
10
  agents/guardrail.py,sha256=7P-kd9rKPhgB8rtI31MCV5ho4ZrEaNCQxHvE8IK3EOk,9582
11
11
  agents/handoffs.py,sha256=31-rQ-iMWlWNd93ivgTTSMGkqlariXrNfWI_udMWt7s,11409
12
- agents/items.py,sha256=ntrJ-HuqSMC8HtIwS9pcqHYXtiQ2TJB6lHR-bcvNn4c,9848
12
+ agents/items.py,sha256=aHo7KTXZLBcHSrKHWDaBB6L7XmBCAIekG5e0xOIhkyM,9828
13
13
  agents/lifecycle.py,sha256=sJwESHBHbml7rSYH360-P6x1bLyENcQWm4bT4rQcbuo,3129
14
14
  agents/logger.py,sha256=p_ef7vWKpBev5FFybPJjhrCCQizK08Yy1A2EDO1SNNg,60
15
15
  agents/model_settings.py,sha256=7zGEGxfXtRHlst9qYngYJc5mkr2l_mi5YuQDGiQ-qXM,6485
@@ -17,7 +17,7 @@ agents/prompts.py,sha256=Ss5y_7s2HFcRAOAKu4WTxQszs5ybI8TfbxgEYdnj9sg,2231
17
17
  agents/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
18
18
  agents/repl.py,sha256=FKZlkGfw6QxItTkjFkCAQwXuV_pn69DIamGd3PiKQFk,2361
19
19
  agents/result.py,sha256=YCGYHoc5X1_vLKu5QiK6F8C1ZXI3tTfLXaZoqbYgUMA,10753
20
- agents/run.py,sha256=Q0UcLVjlmWjpEvXpWm-0obDU5Gu5T9eJ7xW29wW-QEA,52453
20
+ agents/run.py,sha256=lPp-nZWKA0gg0E2ace94zwFo-FmQK7Fj_cXcrvAoFlQ,55978
21
21
  agents/run_context.py,sha256=vuSUQM8O4CLensQY27-22fOqECnw7yvwL9U3WO8b_bk,851
22
22
  agents/stream_events.py,sha256=VFyTu-DT3ZMnHLtMbg-X_lxec0doQxNfx-hVxLB0BpI,1700
23
23
  agents/strict_schema.py,sha256=_KuEJkglmq-Fj3HSeYP4WqTvqrxbSKu6gezfz5Brhh0,5775
@@ -60,9 +60,9 @@ agents/realtime/items.py,sha256=psT6AH65qmngmPsgwk6CXacVo5tEDYq0Za3EitHFpTA,5052
60
60
  agents/realtime/model.py,sha256=RJBA8-Dkd2JTqGzbKacoX4dN_qTWn_p7npL73To3ymw,6143
61
61
  agents/realtime/model_events.py,sha256=X7UrUU_g4u5gWaf2mUesJJ-Ik1Z1QE0Z-ZP7kDmX1t0,4034
62
62
  agents/realtime/model_inputs.py,sha256=OW2bn3wD5_pXLunDUf35jhG2q_bTKbC_D7Qu-83aOEA,2243
63
- agents/realtime/openai_realtime.py,sha256=vgzgklFcRpB9ZfsDda7DtXlBn3NF6bZdysta1DwQhrM,30120
63
+ agents/realtime/openai_realtime.py,sha256=20yHhG-KAGeXY0M0ucty0wpRqXSUVLvoL5cs663NGrI,30201
64
64
  agents/realtime/runner.py,sha256=KfU7utmc9QFH2htIKN2IN9H-5EnB0qN9ezmvlRTnOm4,2511
65
- agents/realtime/session.py,sha256=EmbjWBoIw-1RAPICZbWtQ5OUaZh14xPXPwjHWXDU8c4,23766
65
+ agents/realtime/session.py,sha256=aGifsl_4LYGZBwXneaOo-H_46fdQs-CAtQD6DmJY2Uo,26560
66
66
  agents/tracing/__init__.py,sha256=5HO_6na5S6EwICgwl50OMtxiIIosUrqalhvldlYvSVc,2991
67
67
  agents/tracing/create.py,sha256=xpJ4ZRnGyUDPKoVVkA_8hmdhtwOKGhSkwRco2AQIhAo,18003
68
68
  agents/tracing/logger.py,sha256=J4KUDRSGa7x5UVfUwWe-gbKwoaq8AeETRqkPt3QvtGg,68
@@ -97,7 +97,7 @@ agents/voice/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
97
97
  agents/voice/models/openai_model_provider.py,sha256=Khn0uT-VhsEbe7_OhBMGFQzXNwL80gcWZyTHl3CaBII,3587
98
98
  agents/voice/models/openai_stt.py,sha256=LcVDS7f1pmbm--PWX-IaV9uLg9uv5_L3vSCbVnTJeGs,16864
99
99
  agents/voice/models/openai_tts.py,sha256=4KoLQuFDHKu5a1VTJlu9Nj3MHwMlrn9wfT_liJDJ2dw,1477
100
- openai_agents-0.2.7.dist-info/METADATA,sha256=AusANdnHsmV0VjQRDtmRQ3j5Ql8oT4rUKaqgZiR0Hzg,12104
101
- openai_agents-0.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
102
- openai_agents-0.2.7.dist-info/licenses/LICENSE,sha256=E994EspT7Krhy0qGiES7WYNzBHrh1YDk3r--8d1baRU,1063
103
- openai_agents-0.2.7.dist-info/RECORD,,
100
+ openai_agents-0.2.8.dist-info/METADATA,sha256=MqNemwQZlvUlnyqVHU1Bxor4liYOsBa4VcE_UJllEas,12104
101
+ openai_agents-0.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
102
+ openai_agents-0.2.8.dist-info/licenses/LICENSE,sha256=E994EspT7Krhy0qGiES7WYNzBHrh1YDk3r--8d1baRU,1063
103
+ openai_agents-0.2.8.dist-info/RECORD,,