openai-agents 0.4.1__py3-none-any.whl → 0.4.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/extensions/models/litellm_model.py +45 -11
- agents/models/chatcmpl_stream_handler.py +7 -1
- agents/realtime/config.py +3 -0
- agents/realtime/session.py +59 -8
- {openai_agents-0.4.1.dist-info → openai_agents-0.4.2.dist-info}/METADATA +2 -2
- {openai_agents-0.4.1.dist-info → openai_agents-0.4.2.dist-info}/RECORD +8 -8
- {openai_agents-0.4.1.dist-info → openai_agents-0.4.2.dist-info}/WHEEL +0 -0
- {openai_agents-0.4.1.dist-info → openai_agents-0.4.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -110,18 +110,26 @@ class LitellmModel(Model):
|
|
|
110
110
|
prompt=prompt,
|
|
111
111
|
)
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
message: litellm.types.utils.Message | None = None
|
|
114
|
+
first_choice: litellm.types.utils.Choices | None = None
|
|
115
|
+
if response.choices and len(response.choices) > 0:
|
|
116
|
+
choice = response.choices[0]
|
|
117
|
+
if isinstance(choice, litellm.types.utils.Choices):
|
|
118
|
+
first_choice = choice
|
|
119
|
+
message = first_choice.message
|
|
114
120
|
|
|
115
121
|
if _debug.DONT_LOG_MODEL_DATA:
|
|
116
122
|
logger.debug("Received model response")
|
|
117
123
|
else:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
if message is not None:
|
|
125
|
+
logger.debug(
|
|
126
|
+
f"""LLM resp:\n{
|
|
127
|
+
json.dumps(message.model_dump(), indent=2, ensure_ascii=False)
|
|
128
|
+
}\n"""
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
finish_reason = first_choice.finish_reason if first_choice else "-"
|
|
132
|
+
logger.debug(f"LLM resp had no message. finish_reason: {finish_reason}")
|
|
125
133
|
|
|
126
134
|
if hasattr(response, "usage"):
|
|
127
135
|
response_usage = response.usage
|
|
@@ -152,14 +160,20 @@ class LitellmModel(Model):
|
|
|
152
160
|
logger.warning("No usage information returned from Litellm")
|
|
153
161
|
|
|
154
162
|
if tracing.include_data():
|
|
155
|
-
span_generation.span_data.output =
|
|
163
|
+
span_generation.span_data.output = (
|
|
164
|
+
[message.model_dump()] if message is not None else []
|
|
165
|
+
)
|
|
156
166
|
span_generation.span_data.usage = {
|
|
157
167
|
"input_tokens": usage.input_tokens,
|
|
158
168
|
"output_tokens": usage.output_tokens,
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
items =
|
|
162
|
-
|
|
171
|
+
items = (
|
|
172
|
+
Converter.message_to_output_items(
|
|
173
|
+
LitellmConverter.convert_message_to_openai(message)
|
|
174
|
+
)
|
|
175
|
+
if message is not None
|
|
176
|
+
else []
|
|
163
177
|
)
|
|
164
178
|
|
|
165
179
|
return ModelResponse(
|
|
@@ -326,6 +340,23 @@ class LitellmModel(Model):
|
|
|
326
340
|
)
|
|
327
341
|
|
|
328
342
|
reasoning_effort = model_settings.reasoning.effort if model_settings.reasoning else None
|
|
343
|
+
# Enable developers to pass non-OpenAI compatible reasoning_effort data like "none"
|
|
344
|
+
# Priority order:
|
|
345
|
+
# 1. model_settings.reasoning.effort
|
|
346
|
+
# 2. model_settings.extra_body["reasoning_effort"]
|
|
347
|
+
# 3. model_settings.extra_args["reasoning_effort"]
|
|
348
|
+
if (
|
|
349
|
+
reasoning_effort is None # Unset in model_settings
|
|
350
|
+
and isinstance(model_settings.extra_body, dict)
|
|
351
|
+
and "reasoning_effort" in model_settings.extra_body
|
|
352
|
+
):
|
|
353
|
+
reasoning_effort = model_settings.extra_body["reasoning_effort"]
|
|
354
|
+
if (
|
|
355
|
+
reasoning_effort is None # Unset in both model_settings and model_settings.extra_body
|
|
356
|
+
and model_settings.extra_args
|
|
357
|
+
and "reasoning_effort" in model_settings.extra_args
|
|
358
|
+
):
|
|
359
|
+
reasoning_effort = model_settings.extra_args["reasoning_effort"]
|
|
329
360
|
|
|
330
361
|
stream_options = None
|
|
331
362
|
if stream and model_settings.include_usage is not None:
|
|
@@ -343,6 +374,9 @@ class LitellmModel(Model):
|
|
|
343
374
|
if model_settings.extra_args:
|
|
344
375
|
extra_kwargs.update(model_settings.extra_args)
|
|
345
376
|
|
|
377
|
+
# Prevent duplicate reasoning_effort kwargs when it was promoted to a top-level argument.
|
|
378
|
+
extra_kwargs.pop("reasoning_effort", None)
|
|
379
|
+
|
|
346
380
|
ret = await litellm.acompletion(
|
|
347
381
|
model=self.model,
|
|
348
382
|
messages=converted_messages,
|
|
@@ -150,6 +150,12 @@ class ChatCmplStreamHandler:
|
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
if reasoning_content and state.reasoning_content_index_and_output:
|
|
153
|
+
# Ensure summary list has at least one element
|
|
154
|
+
if not state.reasoning_content_index_and_output[1].summary:
|
|
155
|
+
state.reasoning_content_index_and_output[1].summary = [
|
|
156
|
+
Summary(text="", type="summary_text")
|
|
157
|
+
]
|
|
158
|
+
|
|
153
159
|
yield ResponseReasoningSummaryTextDeltaEvent(
|
|
154
160
|
delta=reasoning_content,
|
|
155
161
|
item_id=FAKE_RESPONSES_ID,
|
|
@@ -201,7 +207,7 @@ class ChatCmplStreamHandler:
|
|
|
201
207
|
)
|
|
202
208
|
|
|
203
209
|
# Create a new summary with updated text
|
|
204
|
-
if state.reasoning_content_index_and_output[1].content
|
|
210
|
+
if not state.reasoning_content_index_and_output[1].content:
|
|
205
211
|
state.reasoning_content_index_and_output[1].content = [
|
|
206
212
|
Content(text="", type="reasoning_text")
|
|
207
213
|
]
|
agents/realtime/config.py
CHANGED
|
@@ -184,6 +184,9 @@ class RealtimeRunConfig(TypedDict):
|
|
|
184
184
|
tracing_disabled: NotRequired[bool]
|
|
185
185
|
"""Whether tracing is disabled for this run."""
|
|
186
186
|
|
|
187
|
+
async_tool_calls: NotRequired[bool]
|
|
188
|
+
"""Whether function tool calls should run asynchronously. Defaults to True."""
|
|
189
|
+
|
|
187
190
|
# TODO (rm) Add history audio storage config
|
|
188
191
|
|
|
189
192
|
|
agents/realtime/session.py
CHANGED
|
@@ -112,7 +112,7 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
112
112
|
}
|
|
113
113
|
self._event_queue: asyncio.Queue[RealtimeSessionEvent] = asyncio.Queue()
|
|
114
114
|
self._closed = False
|
|
115
|
-
self._stored_exception:
|
|
115
|
+
self._stored_exception: BaseException | None = None
|
|
116
116
|
|
|
117
117
|
# Guardrails state tracking
|
|
118
118
|
self._interrupted_response_ids: set[str] = set()
|
|
@@ -123,6 +123,8 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
self._guardrail_tasks: set[asyncio.Task[Any]] = set()
|
|
126
|
+
self._tool_call_tasks: set[asyncio.Task[Any]] = set()
|
|
127
|
+
self._async_tool_calls: bool = bool(self._run_config.get("async_tool_calls", True))
|
|
126
128
|
|
|
127
129
|
@property
|
|
128
130
|
def model(self) -> RealtimeModel:
|
|
@@ -216,7 +218,11 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
216
218
|
if event.type == "error":
|
|
217
219
|
await self._put_event(RealtimeError(info=self._event_info, error=event.error))
|
|
218
220
|
elif event.type == "function_call":
|
|
219
|
-
|
|
221
|
+
agent_snapshot = self._current_agent
|
|
222
|
+
if self._async_tool_calls:
|
|
223
|
+
self._enqueue_tool_call_task(event, agent_snapshot)
|
|
224
|
+
else:
|
|
225
|
+
await self._handle_tool_call(event, agent_snapshot=agent_snapshot)
|
|
220
226
|
elif event.type == "audio":
|
|
221
227
|
await self._put_event(
|
|
222
228
|
RealtimeAudio(
|
|
@@ -384,11 +390,17 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
384
390
|
"""Put an event into the queue."""
|
|
385
391
|
await self._event_queue.put(event)
|
|
386
392
|
|
|
387
|
-
async def _handle_tool_call(
|
|
393
|
+
async def _handle_tool_call(
|
|
394
|
+
self,
|
|
395
|
+
event: RealtimeModelToolCallEvent,
|
|
396
|
+
*,
|
|
397
|
+
agent_snapshot: RealtimeAgent | None = None,
|
|
398
|
+
) -> None:
|
|
388
399
|
"""Handle a tool call event."""
|
|
400
|
+
agent = agent_snapshot or self._current_agent
|
|
389
401
|
tools, handoffs = await asyncio.gather(
|
|
390
|
-
|
|
391
|
-
self._get_handoffs(
|
|
402
|
+
agent.get_all_tools(self._context_wrapper),
|
|
403
|
+
self._get_handoffs(agent, self._context_wrapper),
|
|
392
404
|
)
|
|
393
405
|
function_map = {tool.name: tool for tool in tools if isinstance(tool, FunctionTool)}
|
|
394
406
|
handoff_map = {handoff.tool_name: handoff for handoff in handoffs}
|
|
@@ -398,7 +410,7 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
398
410
|
RealtimeToolStart(
|
|
399
411
|
info=self._event_info,
|
|
400
412
|
tool=function_map[event.name],
|
|
401
|
-
agent=
|
|
413
|
+
agent=agent,
|
|
402
414
|
)
|
|
403
415
|
)
|
|
404
416
|
|
|
@@ -423,7 +435,7 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
423
435
|
info=self._event_info,
|
|
424
436
|
tool=func_tool,
|
|
425
437
|
output=result,
|
|
426
|
-
agent=
|
|
438
|
+
agent=agent,
|
|
427
439
|
)
|
|
428
440
|
)
|
|
429
441
|
elif event.name in handoff_map:
|
|
@@ -444,7 +456,7 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
444
456
|
)
|
|
445
457
|
|
|
446
458
|
# Store previous agent for event
|
|
447
|
-
previous_agent =
|
|
459
|
+
previous_agent = agent
|
|
448
460
|
|
|
449
461
|
# Update current agent
|
|
450
462
|
self._current_agent = result
|
|
@@ -752,10 +764,49 @@ class RealtimeSession(RealtimeModelListener):
|
|
|
752
764
|
task.cancel()
|
|
753
765
|
self._guardrail_tasks.clear()
|
|
754
766
|
|
|
767
|
+
def _enqueue_tool_call_task(
|
|
768
|
+
self, event: RealtimeModelToolCallEvent, agent_snapshot: RealtimeAgent
|
|
769
|
+
) -> None:
|
|
770
|
+
"""Run tool calls in the background to avoid blocking realtime transport."""
|
|
771
|
+
task = asyncio.create_task(self._handle_tool_call(event, agent_snapshot=agent_snapshot))
|
|
772
|
+
self._tool_call_tasks.add(task)
|
|
773
|
+
task.add_done_callback(self._on_tool_call_task_done)
|
|
774
|
+
|
|
775
|
+
def _on_tool_call_task_done(self, task: asyncio.Task[Any]) -> None:
|
|
776
|
+
self._tool_call_tasks.discard(task)
|
|
777
|
+
|
|
778
|
+
if task.cancelled():
|
|
779
|
+
return
|
|
780
|
+
|
|
781
|
+
exception = task.exception()
|
|
782
|
+
if exception is None:
|
|
783
|
+
return
|
|
784
|
+
|
|
785
|
+
logger.exception("Realtime tool call task failed", exc_info=exception)
|
|
786
|
+
|
|
787
|
+
if self._stored_exception is None:
|
|
788
|
+
self._stored_exception = exception
|
|
789
|
+
|
|
790
|
+
asyncio.create_task(
|
|
791
|
+
self._put_event(
|
|
792
|
+
RealtimeError(
|
|
793
|
+
info=self._event_info,
|
|
794
|
+
error={"message": f"Tool call task failed: {exception}"},
|
|
795
|
+
)
|
|
796
|
+
)
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
def _cleanup_tool_call_tasks(self) -> None:
|
|
800
|
+
for task in self._tool_call_tasks:
|
|
801
|
+
if not task.done():
|
|
802
|
+
task.cancel()
|
|
803
|
+
self._tool_call_tasks.clear()
|
|
804
|
+
|
|
755
805
|
async def _cleanup(self) -> None:
|
|
756
806
|
"""Clean up all resources and mark session as closed."""
|
|
757
807
|
# Cancel and cleanup guardrail tasks
|
|
758
808
|
self._cleanup_guardrail_tasks()
|
|
809
|
+
self._cleanup_tool_call_tasks()
|
|
759
810
|
|
|
760
811
|
# Remove ourselves as a listener
|
|
761
812
|
self._model.remove_listener(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openai-agents
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -44,7 +44,7 @@ Requires-Dist: numpy<3,>=2.2.0; (python_version >= '3.10') and extra == 'voice'
|
|
|
44
44
|
Requires-Dist: websockets<16,>=15.0; extra == 'voice'
|
|
45
45
|
Description-Content-Type: text/markdown
|
|
46
46
|
|
|
47
|
-
# OpenAI Agents SDK
|
|
47
|
+
# OpenAI Agents SDK [](https://pypi.org/project/openai-agents/)
|
|
48
48
|
|
|
49
49
|
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. It is provider-agnostic, supporting the OpenAI Responses and Chat Completions APIs, as well as 100+ other LLMs.
|
|
50
50
|
|
|
@@ -36,7 +36,7 @@ agents/extensions/memory/encrypt_session.py,sha256=PVnZIEj50bjUq16OLnMKrbZiinLkr
|
|
|
36
36
|
agents/extensions/memory/redis_session.py,sha256=JwXY6zUTMgq9bRezlyFZ4Tze7DO7T0hioTc23qjSHjU,9838
|
|
37
37
|
agents/extensions/memory/sqlalchemy_session.py,sha256=fnlZkNF_XZekP44uhiR4rjlCkwG7JJEiFm35TJfiCtc,12325
|
|
38
38
|
agents/extensions/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
agents/extensions/models/litellm_model.py,sha256=
|
|
39
|
+
agents/extensions/models/litellm_model.py,sha256=hbQFhAeEF5eHdyu5Q-7HNYFEhmn0KFK2KAcfYA10vqc,25621
|
|
40
40
|
agents/extensions/models/litellm_provider.py,sha256=ZHgh1nMoEvA7NpawkzLh3JDuDFtwXUV94Rs7UrwWqAk,1083
|
|
41
41
|
agents/mcp/__init__.py,sha256=yHmmYlrmEHzUas1inRLKL2iPqbb_-107G3gKe_tyg4I,750
|
|
42
42
|
agents/mcp/server.py,sha256=cby0KKKKRhuWCydr4llERPL72Z94uV-SV3LLAcgcWTk,28435
|
|
@@ -50,7 +50,7 @@ agents/models/__init__.py,sha256=E0XVqWayVAsFqxucDLBW30siaqfNQsVrAnfidG_C3ok,287
|
|
|
50
50
|
agents/models/_openai_shared.py,sha256=4Ngwo2Fv2RXY61Pqck1cYPkSln2tDnb8Ai-ao4QG-iE,836
|
|
51
51
|
agents/models/chatcmpl_converter.py,sha256=qEobLnIJjrK6WRi_tsVkrDrGq78EGro3MZXlVMpMK2c,26011
|
|
52
52
|
agents/models/chatcmpl_helpers.py,sha256=YC2krp_-uBgRCrCEImLjNvONTWRWfwLlPKHI4kBmNXE,1483
|
|
53
|
-
agents/models/chatcmpl_stream_handler.py,sha256=
|
|
53
|
+
agents/models/chatcmpl_stream_handler.py,sha256=1h0esxmnlBk9NwDjjwSlWYzjzuMgIpMLtRU9kaszfyg,29212
|
|
54
54
|
agents/models/default_models.py,sha256=mlvBePn8H4UkHo7lN-wh7A3k2ciLgBUFKpROQxzdTfs,2098
|
|
55
55
|
agents/models/fake_id.py,sha256=lbXjUUSMeAQ8eFx4V5QLUnBClHE6adJlYYav55RlG5w,268
|
|
56
56
|
agents/models/interface.py,sha256=-AFUHC8iRuGZmtQwguDw4s-M4OPL2y2mct4TAmWvVrU,4057
|
|
@@ -64,7 +64,7 @@ agents/realtime/_default_tracker.py,sha256=4OMxBvD1MnZmMn6JZYKL42uWhVzvK6NdDLDfP
|
|
|
64
64
|
agents/realtime/_util.py,sha256=ehBzUN1RTD2m2TXq73Jm4WohQzJ6y_MfnF5MaK8uu14,341
|
|
65
65
|
agents/realtime/agent.py,sha256=bkegBJ_lc3z3NtnlIyEkVZFxZWBJwVjsQVzpQZAu7PM,4283
|
|
66
66
|
agents/realtime/audio_formats.py,sha256=DBUWVVff4XY5BT6Mol86tF4PFMp5OIS3LmAbqUmQn_k,1019
|
|
67
|
-
agents/realtime/config.py,sha256=
|
|
67
|
+
agents/realtime/config.py,sha256=vnjgkeZXcOSLFopoAiGj4Vki_75pEJIKTagJtQpCWmg,7072
|
|
68
68
|
agents/realtime/events.py,sha256=eANiNNyYlp_1Ybdl-MOwXRVTDtrK9hfgn6iw0xNxnaY,5889
|
|
69
69
|
agents/realtime/handoffs.py,sha256=iJ4lr5RVdDkw5W3_AOGB_Az-hlRt1CoFFFNFDfd3ues,6698
|
|
70
70
|
agents/realtime/items.py,sha256=5EG768FkKpbk-dhe4b_7BfFpdUEFWtxoiVUtNI9KXsc,5517
|
|
@@ -73,7 +73,7 @@ agents/realtime/model_events.py,sha256=2NKofzLszKHwtlcsogsNnH6hdeFfO7S96yWDB4Alx
|
|
|
73
73
|
agents/realtime/model_inputs.py,sha256=-pl8Oj0WVrA5Gt-dqP5Va3ZHqXyIXpsjMsf9UL-suEY,2789
|
|
74
74
|
agents/realtime/openai_realtime.py,sha256=jN3OvcEQt9X-59t6InllkOOEd8Tdw69K5vuKfXBeObg,44763
|
|
75
75
|
agents/realtime/runner.py,sha256=KfU7utmc9QFH2htIKN2IN9H-5EnB0qN9ezmvlRTnOm4,2511
|
|
76
|
-
agents/realtime/session.py,sha256=
|
|
76
|
+
agents/realtime/session.py,sha256=79WqKWwGOsutQRLs7fDsijE-OxEJjGm-aOpjL5F7Fn8,36983
|
|
77
77
|
agents/tracing/__init__.py,sha256=5HO_6na5S6EwICgwl50OMtxiIIosUrqalhvldlYvSVc,2991
|
|
78
78
|
agents/tracing/create.py,sha256=xpJ4ZRnGyUDPKoVVkA_8hmdhtwOKGhSkwRco2AQIhAo,18003
|
|
79
79
|
agents/tracing/logger.py,sha256=J4KUDRSGa7x5UVfUwWe-gbKwoaq8AeETRqkPt3QvtGg,68
|
|
@@ -108,7 +108,7 @@ agents/voice/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
108
108
|
agents/voice/models/openai_model_provider.py,sha256=Khn0uT-VhsEbe7_OhBMGFQzXNwL80gcWZyTHl3CaBII,3587
|
|
109
109
|
agents/voice/models/openai_stt.py,sha256=Lb_F9160VNKDHXZ9zylSzeig7sB8lBjiYhQLDZsp6NQ,17257
|
|
110
110
|
agents/voice/models/openai_tts.py,sha256=4KoLQuFDHKu5a1VTJlu9Nj3MHwMlrn9wfT_liJDJ2dw,1477
|
|
111
|
-
openai_agents-0.4.
|
|
112
|
-
openai_agents-0.4.
|
|
113
|
-
openai_agents-0.4.
|
|
114
|
-
openai_agents-0.4.
|
|
111
|
+
openai_agents-0.4.2.dist-info/METADATA,sha256=UUyVoFXNYwTLrBnkpo7MFwT73-kJH0rQX53xwF3pFXw,13046
|
|
112
|
+
openai_agents-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
113
|
+
openai_agents-0.4.2.dist-info/licenses/LICENSE,sha256=E994EspT7Krhy0qGiES7WYNzBHrh1YDk3r--8d1baRU,1063
|
|
114
|
+
openai_agents-0.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|