openai-agents 0.0.16__py3-none-any.whl → 0.0.17__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/__init__.py CHANGED
@@ -14,6 +14,7 @@ from .exceptions import (
14
14
  MaxTurnsExceeded,
15
15
  ModelBehaviorError,
16
16
  OutputGuardrailTripwireTriggered,
17
+ RunErrorDetails,
17
18
  UserError,
18
19
  )
19
20
  from .guardrail import (
@@ -204,6 +205,7 @@ __all__ = [
204
205
  "AgentHooks",
205
206
  "RunContextWrapper",
206
207
  "TContext",
208
+ "RunErrorDetails",
207
209
  "RunResult",
208
210
  "RunResultStreaming",
209
211
  "RunConfig",
agents/_run_impl.py CHANGED
@@ -33,6 +33,7 @@ from openai.types.responses.response_output_item import (
33
33
  ImageGenerationCall,
34
34
  LocalShellCall,
35
35
  McpApprovalRequest,
36
+ McpCall,
36
37
  McpListTools,
37
38
  )
38
39
  from openai.types.responses.response_reasoning_item import ResponseReasoningItem
@@ -456,6 +457,9 @@ class RunImpl:
456
457
  )
457
458
  elif isinstance(output, McpListTools):
458
459
  items.append(MCPListToolsItem(raw_item=output, agent=agent))
460
+ elif isinstance(output, McpCall):
461
+ items.append(ToolCallItem(raw_item=output, agent=agent))
462
+ tools_used.append("mcp")
459
463
  elif isinstance(output, ImageGenerationCall):
460
464
  items.append(ToolCallItem(raw_item=output, agent=agent))
461
465
  tools_used.append("image_generation")
agents/agent.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import dataclasses
4
5
  import inspect
5
6
  from collections.abc import Awaitable
@@ -17,7 +18,7 @@ from .mcp import MCPUtil
17
18
  from .model_settings import ModelSettings
18
19
  from .models.interface import Model
19
20
  from .run_context import RunContextWrapper, TContext
20
- from .tool import FunctionToolResult, Tool, function_tool
21
+ from .tool import FunctionTool, FunctionToolResult, Tool, function_tool
21
22
  from .util import _transforms
22
23
  from .util._types import MaybeAwaitable
23
24
 
@@ -246,7 +247,22 @@ class Agent(Generic[TContext]):
246
247
  convert_schemas_to_strict = self.mcp_config.get("convert_schemas_to_strict", False)
247
248
  return await MCPUtil.get_all_function_tools(self.mcp_servers, convert_schemas_to_strict)
248
249
 
249
- async def get_all_tools(self) -> list[Tool]:
250
+ async def get_all_tools(self, run_context: RunContextWrapper[Any]) -> list[Tool]:
250
251
  """All agent tools, including MCP tools and function tools."""
251
252
  mcp_tools = await self.get_mcp_tools()
252
- return mcp_tools + self.tools
253
+
254
+ async def _check_tool_enabled(tool: Tool) -> bool:
255
+ if not isinstance(tool, FunctionTool):
256
+ return True
257
+
258
+ attr = tool.is_enabled
259
+ if isinstance(attr, bool):
260
+ return attr
261
+ res = attr(run_context, self)
262
+ if inspect.isawaitable(res):
263
+ return bool(await res)
264
+ return bool(res)
265
+
266
+ results = await asyncio.gather(*(_check_tool_enabled(t) for t in self.tools))
267
+ enabled: list[Tool] = [t for t, ok in zip(self.tools, results) if ok]
268
+ return [*mcp_tools, *enabled]
agents/agent_output.py CHANGED
@@ -38,7 +38,7 @@ class AgentOutputSchemaBase(abc.ABC):
38
38
  @abc.abstractmethod
39
39
  def is_strict_json_schema(self) -> bool:
40
40
  """Whether the JSON schema is in strict mode. Strict mode constrains the JSON schema
41
- features, but guarantees valis JSON. See here for details:
41
+ features, but guarantees valid JSON. See here for details:
42
42
  https://platform.openai.com/docs/guides/structured-outputs#supported-schemas
43
43
  """
44
44
  pass
agents/exceptions.py CHANGED
@@ -1,12 +1,42 @@
1
- from typing import TYPE_CHECKING
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Any
2
5
 
3
6
  if TYPE_CHECKING:
7
+ from .agent import Agent
4
8
  from .guardrail import InputGuardrailResult, OutputGuardrailResult
9
+ from .items import ModelResponse, RunItem, TResponseInputItem
10
+ from .run_context import RunContextWrapper
11
+
12
+ from .util._pretty_print import pretty_print_run_error_details
13
+
14
+
15
+ @dataclass
16
+ class RunErrorDetails:
17
+ """Data collected from an agent run when an exception occurs."""
18
+
19
+ input: str | list[TResponseInputItem]
20
+ new_items: list[RunItem]
21
+ raw_responses: list[ModelResponse]
22
+ last_agent: Agent[Any]
23
+ context_wrapper: RunContextWrapper[Any]
24
+ input_guardrail_results: list[InputGuardrailResult]
25
+ output_guardrail_results: list[OutputGuardrailResult]
26
+
27
+ def __str__(self) -> str:
28
+ return pretty_print_run_error_details(self)
5
29
 
6
30
 
7
31
  class AgentsException(Exception):
8
32
  """Base class for all exceptions in the Agents SDK."""
9
33
 
34
+ run_data: RunErrorDetails | None
35
+
36
+ def __init__(self, *args: object) -> None:
37
+ super().__init__(*args)
38
+ self.run_data = None
39
+
10
40
 
11
41
  class MaxTurnsExceeded(AgentsException):
12
42
  """Exception raised when the maximum number of turns is exceeded."""
@@ -15,6 +45,7 @@ class MaxTurnsExceeded(AgentsException):
15
45
 
16
46
  def __init__(self, message: str):
17
47
  self.message = message
48
+ super().__init__(message)
18
49
 
19
50
 
20
51
  class ModelBehaviorError(AgentsException):
@@ -26,6 +57,7 @@ class ModelBehaviorError(AgentsException):
26
57
 
27
58
  def __init__(self, message: str):
28
59
  self.message = message
60
+ super().__init__(message)
29
61
 
30
62
 
31
63
  class UserError(AgentsException):
@@ -35,15 +67,16 @@ class UserError(AgentsException):
35
67
 
36
68
  def __init__(self, message: str):
37
69
  self.message = message
70
+ super().__init__(message)
38
71
 
39
72
 
40
73
  class InputGuardrailTripwireTriggered(AgentsException):
41
74
  """Exception raised when a guardrail tripwire is triggered."""
42
75
 
43
- guardrail_result: "InputGuardrailResult"
76
+ guardrail_result: InputGuardrailResult
44
77
  """The result data of the guardrail that was triggered."""
45
78
 
46
- def __init__(self, guardrail_result: "InputGuardrailResult"):
79
+ def __init__(self, guardrail_result: InputGuardrailResult):
47
80
  self.guardrail_result = guardrail_result
48
81
  super().__init__(
49
82
  f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire"
@@ -53,10 +86,10 @@ class InputGuardrailTripwireTriggered(AgentsException):
53
86
  class OutputGuardrailTripwireTriggered(AgentsException):
54
87
  """Exception raised when a guardrail tripwire is triggered."""
55
88
 
56
- guardrail_result: "OutputGuardrailResult"
89
+ guardrail_result: OutputGuardrailResult
57
90
  """The result data of the guardrail that was triggered."""
58
91
 
59
- def __init__(self, guardrail_result: "OutputGuardrailResult"):
92
+ def __init__(self, guardrail_result: OutputGuardrailResult):
60
93
  self.guardrail_result = guardrail_result
61
94
  super().__init__(
62
95
  f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire"
@@ -5,7 +5,6 @@ import time
5
5
  from collections.abc import AsyncIterator
6
6
  from typing import Any, Literal, cast, overload
7
7
 
8
- import litellm.types
9
8
  from openai.types.responses.response_usage import InputTokensDetails, OutputTokensDetails
10
9
 
11
10
  from agents.exceptions import ModelBehaviorError
@@ -112,11 +111,13 @@ class LitellmModel(Model):
112
111
  cached_tokens=getattr(
113
112
  response_usage.prompt_tokens_details, "cached_tokens", 0
114
113
  )
114
+ or 0
115
115
  ),
116
116
  output_tokens_details=OutputTokensDetails(
117
117
  reasoning_tokens=getattr(
118
118
  response_usage.completion_tokens_details, "reasoning_tokens", 0
119
119
  )
120
+ or 0
120
121
  ),
121
122
  )
122
123
  if response.usage
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
2
 
3
3
  import graphviz # type: ignore
4
4
 
@@ -31,7 +31,9 @@ def get_main_graph(agent: Agent) -> str:
31
31
  return "".join(parts)
32
32
 
33
33
 
34
- def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str:
34
+ def get_all_nodes(
35
+ agent: Agent, parent: Agent | None = None, visited: set[str] | None = None
36
+ ) -> str:
35
37
  """
36
38
  Recursively generates the nodes for the given agent and its handoffs in DOT format.
37
39
 
@@ -41,17 +43,23 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str:
41
43
  Returns:
42
44
  str: The DOT format string representing the nodes.
43
45
  """
46
+ if visited is None:
47
+ visited = set()
48
+ if agent.name in visited:
49
+ return ""
50
+ visited.add(agent.name)
51
+
44
52
  parts = []
45
53
 
46
54
  # Start and end the graph
47
- parts.append(
48
- '"__start__" [label="__start__", shape=ellipse, style=filled, '
49
- "fillcolor=lightblue, width=0.5, height=0.3];"
50
- '"__end__" [label="__end__", shape=ellipse, style=filled, '
51
- "fillcolor=lightblue, width=0.5, height=0.3];"
52
- )
53
- # Ensure parent agent node is colored
54
55
  if not parent:
56
+ parts.append(
57
+ '"__start__" [label="__start__", shape=ellipse, style=filled, '
58
+ "fillcolor=lightblue, width=0.5, height=0.3];"
59
+ '"__end__" [label="__end__", shape=ellipse, style=filled, '
60
+ "fillcolor=lightblue, width=0.5, height=0.3];"
61
+ )
62
+ # Ensure parent agent node is colored
55
63
  parts.append(
56
64
  f'"{agent.name}" [label="{agent.name}", shape=box, style=filled, '
57
65
  "fillcolor=lightyellow, width=1.5, height=0.8];"
@@ -71,17 +79,20 @@ def get_all_nodes(agent: Agent, parent: Optional[Agent] = None) -> str:
71
79
  f"fillcolor=lightyellow, width=1.5, height=0.8];"
72
80
  )
73
81
  if isinstance(handoff, Agent):
74
- parts.append(
75
- f'"{handoff.name}" [label="{handoff.name}", '
76
- f"shape=box, style=filled, style=rounded, "
77
- f"fillcolor=lightyellow, width=1.5, height=0.8];"
78
- )
79
- parts.append(get_all_nodes(handoff))
82
+ if handoff.name not in visited:
83
+ parts.append(
84
+ f'"{handoff.name}" [label="{handoff.name}", '
85
+ f"shape=box, style=filled, style=rounded, "
86
+ f"fillcolor=lightyellow, width=1.5, height=0.8];"
87
+ )
88
+ parts.append(get_all_nodes(handoff, agent, visited))
80
89
 
81
90
  return "".join(parts)
82
91
 
83
92
 
84
- def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str:
93
+ def get_all_edges(
94
+ agent: Agent, parent: Agent | None = None, visited: set[str] | None = None
95
+ ) -> str:
85
96
  """
86
97
  Recursively generates the edges for the given agent and its handoffs in DOT format.
87
98
 
@@ -92,6 +103,12 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str:
92
103
  Returns:
93
104
  str: The DOT format string representing the edges.
94
105
  """
106
+ if visited is None:
107
+ visited = set()
108
+ if agent.name in visited:
109
+ return ""
110
+ visited.add(agent.name)
111
+
95
112
  parts = []
96
113
 
97
114
  if not parent:
@@ -109,7 +126,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str:
109
126
  if isinstance(handoff, Agent):
110
127
  parts.append(f"""
111
128
  "{agent.name}" -> "{handoff.name}";""")
112
- parts.append(get_all_edges(handoff, agent))
129
+ parts.append(get_all_edges(handoff, agent, visited))
113
130
 
114
131
  if not agent.handoffs and not isinstance(agent, Tool): # type: ignore
115
132
  parts.append(f'"{agent.name}" -> "__end__";')
@@ -117,7 +134,7 @@ def get_all_edges(agent: Agent, parent: Optional[Agent] = None) -> str:
117
134
  return "".join(parts)
118
135
 
119
136
 
120
- def draw_graph(agent: Agent, filename: Optional[str] = None) -> graphviz.Source:
137
+ def draw_graph(agent: Agent, filename: str | None = None) -> graphviz.Source:
121
138
  """
122
139
  Draws the graph for the given agent and optionally saves it as a PNG file.
123
140
 
agents/handoffs.py CHANGED
@@ -168,7 +168,7 @@ def handoff(
168
168
  input_filter: a function that filters the inputs that are passed to the next agent.
169
169
  """
170
170
  assert (on_handoff and input_type) or not (on_handoff and input_type), (
171
- "You must provide either both on_input and input_type, or neither"
171
+ "You must provide either both on_handoff and input_type, or neither"
172
172
  )
173
173
  type_adapter: TypeAdapter[Any] | None
174
174
  if input_type is not None:
agents/mcp/server.py CHANGED
@@ -88,7 +88,7 @@ class _MCPServerWithClientSession(MCPServer, abc.ABC):
88
88
  tuple[
89
89
  MemoryObjectReceiveStream[SessionMessage | Exception],
90
90
  MemoryObjectSendStream[SessionMessage],
91
- GetSessionIdCallback | None
91
+ GetSessionIdCallback | None,
92
92
  ]
93
93
  ]:
94
94
  """Create the streams for the server."""
@@ -243,7 +243,7 @@ class MCPServerStdio(_MCPServerWithClientSession):
243
243
  tuple[
244
244
  MemoryObjectReceiveStream[SessionMessage | Exception],
245
245
  MemoryObjectSendStream[SessionMessage],
246
- GetSessionIdCallback | None
246
+ GetSessionIdCallback | None,
247
247
  ]
248
248
  ]:
249
249
  """Create the streams for the server."""
@@ -314,7 +314,7 @@ class MCPServerSse(_MCPServerWithClientSession):
314
314
  tuple[
315
315
  MemoryObjectReceiveStream[SessionMessage | Exception],
316
316
  MemoryObjectSendStream[SessionMessage],
317
- GetSessionIdCallback | None
317
+ GetSessionIdCallback | None,
318
318
  ]
319
319
  ]:
320
320
  """Create the streams for the server."""
@@ -394,7 +394,7 @@ class MCPServerStreamableHttp(_MCPServerWithClientSession):
394
394
  tuple[
395
395
  MemoryObjectReceiveStream[SessionMessage | Exception],
396
396
  MemoryObjectSendStream[SessionMessage],
397
- GetSessionIdCallback | None
397
+ GetSessionIdCallback | None,
398
398
  ]
399
399
  ]:
400
400
  """Create the streams for the server."""
@@ -403,7 +403,7 @@ class MCPServerStreamableHttp(_MCPServerWithClientSession):
403
403
  headers=self.params.get("headers", None),
404
404
  timeout=self.params.get("timeout", timedelta(seconds=30)),
405
405
  sse_read_timeout=self.params.get("sse_read_timeout", timedelta(seconds=60 * 5)),
406
- terminate_on_close=self.params.get("terminate_on_close", True)
406
+ terminate_on_close=self.params.get("terminate_on_close", True),
407
407
  )
408
408
 
409
409
  @property
agents/mcp/util.py CHANGED
@@ -116,7 +116,7 @@ class MCPUtil:
116
116
  if len(result.content) == 1:
117
117
  tool_output = result.content[0].model_dump_json()
118
118
  elif len(result.content) > 1:
119
- tool_output = json.dumps([item.model_dump() for item in result.content])
119
+ tool_output = json.dumps([item.model_dump(mode="json") for item in result.content])
120
120
  else:
121
121
  logger.error(f"Errored MCP tool result: {result}")
122
122
  tool_output = "Error running tool."
@@ -71,12 +71,22 @@ class OpenAIChatCompletionsModel(Model):
71
71
  stream=False,
72
72
  )
73
73
 
74
+ first_choice = response.choices[0]
75
+ message = first_choice.message
76
+
74
77
  if _debug.DONT_LOG_MODEL_DATA:
75
78
  logger.debug("Received model response")
76
79
  else:
77
- logger.debug(
78
- f"LLM resp:\n{json.dumps(response.choices[0].message.model_dump(), indent=2)}\n"
79
- )
80
+ if message is not None:
81
+ logger.debug(
82
+ "LLM resp:\n%s\n",
83
+ json.dumps(message.model_dump(), indent=2),
84
+ )
85
+ else:
86
+ logger.debug(
87
+ "LLM resp had no message. finish_reason: %s",
88
+ first_choice.finish_reason,
89
+ )
80
90
 
81
91
  usage = (
82
92
  Usage(
@@ -101,13 +111,15 @@ class OpenAIChatCompletionsModel(Model):
101
111
  else Usage()
102
112
  )
103
113
  if tracing.include_data():
104
- span_generation.span_data.output = [response.choices[0].message.model_dump()]
114
+ span_generation.span_data.output = (
115
+ [message.model_dump()] if message is not None else []
116
+ )
105
117
  span_generation.span_data.usage = {
106
118
  "input_tokens": usage.input_tokens,
107
119
  "output_tokens": usage.output_tokens,
108
120
  }
109
121
 
110
- items = Converter.message_to_output_items(response.choices[0].message)
122
+ items = Converter.message_to_output_items(message) if message is not None else []
111
123
 
112
124
  return ModelResponse(
113
125
  output=items,
agents/result.py CHANGED
@@ -11,14 +11,22 @@ from typing_extensions import TypeVar
11
11
  from ._run_impl import QueueCompleteSentinel
12
12
  from .agent import Agent
13
13
  from .agent_output import AgentOutputSchemaBase
14
- from .exceptions import InputGuardrailTripwireTriggered, MaxTurnsExceeded
14
+ from .exceptions import (
15
+ AgentsException,
16
+ InputGuardrailTripwireTriggered,
17
+ MaxTurnsExceeded,
18
+ RunErrorDetails,
19
+ )
15
20
  from .guardrail import InputGuardrailResult, OutputGuardrailResult
16
21
  from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem
17
22
  from .logger import logger
18
23
  from .run_context import RunContextWrapper
19
24
  from .stream_events import StreamEvent
20
25
  from .tracing import Trace
21
- from .util._pretty_print import pretty_print_result, pretty_print_run_result_streaming
26
+ from .util._pretty_print import (
27
+ pretty_print_result,
28
+ pretty_print_run_result_streaming,
29
+ )
22
30
 
23
31
  if TYPE_CHECKING:
24
32
  from ._run_impl import QueueCompleteSentinel
@@ -206,31 +214,53 @@ class RunResultStreaming(RunResultBase):
206
214
  if self._stored_exception:
207
215
  raise self._stored_exception
208
216
 
217
+ def _create_error_details(self) -> RunErrorDetails:
218
+ """Return a `RunErrorDetails` object considering the current attributes of the class."""
219
+ return RunErrorDetails(
220
+ input=self.input,
221
+ new_items=self.new_items,
222
+ raw_responses=self.raw_responses,
223
+ last_agent=self.current_agent,
224
+ context_wrapper=self.context_wrapper,
225
+ input_guardrail_results=self.input_guardrail_results,
226
+ output_guardrail_results=self.output_guardrail_results,
227
+ )
228
+
209
229
  def _check_errors(self):
210
230
  if self.current_turn > self.max_turns:
211
- self._stored_exception = MaxTurnsExceeded(f"Max turns ({self.max_turns}) exceeded")
231
+ max_turns_exc = MaxTurnsExceeded(f"Max turns ({self.max_turns}) exceeded")
232
+ max_turns_exc.run_data = self._create_error_details()
233
+ self._stored_exception = max_turns_exc
212
234
 
213
235
  # Fetch all the completed guardrail results from the queue and raise if needed
214
236
  while not self._input_guardrail_queue.empty():
215
237
  guardrail_result = self._input_guardrail_queue.get_nowait()
216
238
  if guardrail_result.output.tripwire_triggered:
217
- self._stored_exception = InputGuardrailTripwireTriggered(guardrail_result)
239
+ tripwire_exc = InputGuardrailTripwireTriggered(guardrail_result)
240
+ tripwire_exc.run_data = self._create_error_details()
241
+ self._stored_exception = tripwire_exc
218
242
 
219
243
  # Check the tasks for any exceptions
220
244
  if self._run_impl_task and self._run_impl_task.done():
221
- exc = self._run_impl_task.exception()
222
- if exc and isinstance(exc, Exception):
223
- self._stored_exception = exc
245
+ run_impl_exc = self._run_impl_task.exception()
246
+ if run_impl_exc and isinstance(run_impl_exc, Exception):
247
+ if isinstance(run_impl_exc, AgentsException) and run_impl_exc.run_data is None:
248
+ run_impl_exc.run_data = self._create_error_details()
249
+ self._stored_exception = run_impl_exc
224
250
 
225
251
  if self._input_guardrails_task and self._input_guardrails_task.done():
226
- exc = self._input_guardrails_task.exception()
227
- if exc and isinstance(exc, Exception):
228
- self._stored_exception = exc
252
+ in_guard_exc = self._input_guardrails_task.exception()
253
+ if in_guard_exc and isinstance(in_guard_exc, Exception):
254
+ if isinstance(in_guard_exc, AgentsException) and in_guard_exc.run_data is None:
255
+ in_guard_exc.run_data = self._create_error_details()
256
+ self._stored_exception = in_guard_exc
229
257
 
230
258
  if self._output_guardrails_task and self._output_guardrails_task.done():
231
- exc = self._output_guardrails_task.exception()
232
- if exc and isinstance(exc, Exception):
233
- self._stored_exception = exc
259
+ out_guard_exc = self._output_guardrails_task.exception()
260
+ if out_guard_exc and isinstance(out_guard_exc, Exception):
261
+ if isinstance(out_guard_exc, AgentsException) and out_guard_exc.run_data is None:
262
+ out_guard_exc.run_data = self._create_error_details()
263
+ self._stored_exception = out_guard_exc
234
264
 
235
265
  def _cleanup_tasks(self):
236
266
  if self._run_impl_task and not self._run_impl_task.done():
agents/run.py CHANGED
@@ -26,6 +26,7 @@ from .exceptions import (
26
26
  MaxTurnsExceeded,
27
27
  ModelBehaviorError,
28
28
  OutputGuardrailTripwireTriggered,
29
+ RunErrorDetails,
29
30
  )
30
31
  from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult
31
32
  from .handoffs import Handoff, HandoffInputFilter, handoff
@@ -180,6 +181,8 @@ class Runner:
180
181
 
181
182
  try:
182
183
  while True:
184
+ all_tools = await cls._get_all_tools(current_agent, context_wrapper)
185
+
183
186
  # Start an agent span if we don't have one. This span is ended if the current
184
187
  # agent changes, or if the agent loop ends.
185
188
  if current_span is None:
@@ -195,8 +198,6 @@ class Runner:
195
198
  output_type=output_type_name,
196
199
  )
197
200
  current_span.start(mark_as_current=True)
198
-
199
- all_tools = await cls._get_all_tools(current_agent)
200
201
  current_span.span_data.tools = [t.name for t in all_tools]
201
202
 
202
203
  current_turn += 1
@@ -283,6 +284,17 @@ class Runner:
283
284
  raise AgentsException(
284
285
  f"Unknown next step type: {type(turn_result.next_step)}"
285
286
  )
287
+ except AgentsException as exc:
288
+ exc.run_data = RunErrorDetails(
289
+ input=original_input,
290
+ new_items=generated_items,
291
+ raw_responses=model_responses,
292
+ last_agent=current_agent,
293
+ context_wrapper=context_wrapper,
294
+ input_guardrail_results=input_guardrail_results,
295
+ output_guardrail_results=[],
296
+ )
297
+ raise
286
298
  finally:
287
299
  if current_span:
288
300
  current_span.finish(reset_current=True)
@@ -513,6 +525,8 @@ class Runner:
513
525
  if streamed_result.is_complete:
514
526
  break
515
527
 
528
+ all_tools = await cls._get_all_tools(current_agent, context_wrapper)
529
+
516
530
  # Start an agent span if we don't have one. This span is ended if the current
517
531
  # agent changes, or if the agent loop ends.
518
532
  if current_span is None:
@@ -528,8 +542,6 @@ class Runner:
528
542
  output_type=output_type_name,
529
543
  )
530
544
  current_span.start(mark_as_current=True)
531
-
532
- all_tools = await cls._get_all_tools(current_agent)
533
545
  tool_names = [t.name for t in all_tools]
534
546
  current_span.span_data.tools = tool_names
535
547
  current_turn += 1
@@ -609,6 +621,19 @@ class Runner:
609
621
  streamed_result._event_queue.put_nowait(QueueCompleteSentinel())
610
622
  elif isinstance(turn_result.next_step, NextStepRunAgain):
611
623
  pass
624
+ except AgentsException as exc:
625
+ streamed_result.is_complete = True
626
+ streamed_result._event_queue.put_nowait(QueueCompleteSentinel())
627
+ exc.run_data = RunErrorDetails(
628
+ input=streamed_result.input,
629
+ new_items=streamed_result.new_items,
630
+ raw_responses=streamed_result.raw_responses,
631
+ last_agent=current_agent,
632
+ context_wrapper=context_wrapper,
633
+ input_guardrail_results=streamed_result.input_guardrail_results,
634
+ output_guardrail_results=streamed_result.output_guardrail_results,
635
+ )
636
+ raise
612
637
  except Exception as e:
613
638
  if current_span:
614
639
  _error_tracing.attach_error_to_span(
@@ -955,8 +980,10 @@ class Runner:
955
980
  return handoffs
956
981
 
957
982
  @classmethod
958
- async def _get_all_tools(cls, agent: Agent[Any]) -> list[Tool]:
959
- return await agent.get_all_tools()
983
+ async def _get_all_tools(
984
+ cls, agent: Agent[Any], context_wrapper: RunContextWrapper[Any]
985
+ ) -> list[Tool]:
986
+ return await agent.get_all_tools(context_wrapper)
960
987
 
961
988
  @classmethod
962
989
  def _get_model(cls, agent: Agent[Any], run_config: RunConfig) -> Model:
agents/stream_events.py CHANGED
@@ -31,6 +31,7 @@ class RunItemStreamEvent:
31
31
  name: Literal[
32
32
  "message_output_created",
33
33
  "handoff_requested",
34
+ # This is misspelled, but we can't change it because that would be a breaking change
34
35
  "handoff_occured",
35
36
  "tool_called",
36
37
  "tool_output",
agents/tool.py CHANGED
@@ -4,7 +4,7 @@ import inspect
4
4
  import json
5
5
  from collections.abc import Awaitable
6
6
  from dataclasses import dataclass
7
- from typing import Any, Callable, Literal, Union, overload
7
+ from typing import TYPE_CHECKING, Any, Callable, Literal, Union, overload
8
8
 
9
9
  from openai.types.responses.file_search_tool_param import Filters, RankingOptions
10
10
  from openai.types.responses.response_output_item import LocalShellCall, McpApprovalRequest
@@ -24,6 +24,9 @@ from .tracing import SpanError
24
24
  from .util import _error_tracing
25
25
  from .util._types import MaybeAwaitable
26
26
 
27
+ if TYPE_CHECKING:
28
+ from .agent import Agent
29
+
27
30
  ToolParams = ParamSpec("ToolParams")
28
31
 
29
32
  ToolFunctionWithoutContext = Callable[ToolParams, Any]
@@ -74,6 +77,11 @@ class FunctionTool:
74
77
  """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True,
75
78
  as it increases the likelihood of correct JSON input."""
76
79
 
80
+ is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True
81
+ """Whether the tool is enabled. Either a bool or a Callable that takes the run context and agent
82
+ and returns whether the tool is enabled. You can use this to dynamically enable/disable a tool
83
+ based on your context/state."""
84
+
77
85
 
78
86
  @dataclass
79
87
  class FileSearchTool:
@@ -262,6 +270,7 @@ def function_tool(
262
270
  use_docstring_info: bool = True,
263
271
  failure_error_function: ToolErrorFunction | None = None,
264
272
  strict_mode: bool = True,
273
+ is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True,
265
274
  ) -> FunctionTool:
266
275
  """Overload for usage as @function_tool (no parentheses)."""
267
276
  ...
@@ -276,6 +285,7 @@ def function_tool(
276
285
  use_docstring_info: bool = True,
277
286
  failure_error_function: ToolErrorFunction | None = None,
278
287
  strict_mode: bool = True,
288
+ is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True,
279
289
  ) -> Callable[[ToolFunction[...]], FunctionTool]:
280
290
  """Overload for usage as @function_tool(...)."""
281
291
  ...
@@ -290,6 +300,7 @@ def function_tool(
290
300
  use_docstring_info: bool = True,
291
301
  failure_error_function: ToolErrorFunction | None = default_tool_error_function,
292
302
  strict_mode: bool = True,
303
+ is_enabled: bool | Callable[[RunContextWrapper[Any], Agent[Any]], MaybeAwaitable[bool]] = True,
293
304
  ) -> FunctionTool | Callable[[ToolFunction[...]], FunctionTool]:
294
305
  """
295
306
  Decorator to create a FunctionTool from a function. By default, we will:
@@ -318,6 +329,9 @@ def function_tool(
318
329
  If False, it allows non-strict JSON schemas. For example, if a parameter has a default
319
330
  value, it will be optional, additional properties are allowed, etc. See here for more:
320
331
  https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas
332
+ is_enabled: Whether the tool is enabled. Can be a bool or a callable that takes the run
333
+ context and agent and returns whether the tool is enabled. Disabled tools are hidden
334
+ from the LLM at runtime.
321
335
  """
322
336
 
323
337
  def _create_function_tool(the_func: ToolFunction[...]) -> FunctionTool:
@@ -407,6 +421,7 @@ def function_tool(
407
421
  params_json_schema=schema.params_json_schema,
408
422
  on_invoke_tool=_on_invoke_tool,
409
423
  strict_json_schema=strict_mode,
424
+ is_enabled=is_enabled,
410
425
  )
411
426
 
412
427
  # If func is actually a callable, we were used as @function_tool with no parentheses
@@ -188,10 +188,27 @@ class BatchTraceProcessor(TracingProcessor):
188
188
  # Track when we next *must* perform a scheduled export
189
189
  self._next_export_time = time.time() + self._schedule_delay
190
190
 
191
- self._worker_thread = threading.Thread(target=self._run, daemon=True)
192
- self._worker_thread.start()
191
+ # We lazily start the background worker thread the first time a span/trace is queued.
192
+ self._worker_thread: threading.Thread | None = None
193
+ self._thread_start_lock = threading.Lock()
194
+
195
+ def _ensure_thread_started(self) -> None:
196
+ # Fast path without holding the lock
197
+ if self._worker_thread and self._worker_thread.is_alive():
198
+ return
199
+
200
+ # Double-checked locking to avoid starting multiple threads
201
+ with self._thread_start_lock:
202
+ if self._worker_thread and self._worker_thread.is_alive():
203
+ return
204
+
205
+ self._worker_thread = threading.Thread(target=self._run, daemon=True)
206
+ self._worker_thread.start()
193
207
 
194
208
  def on_trace_start(self, trace: Trace) -> None:
209
+ # Ensure the background worker is running before we enqueue anything.
210
+ self._ensure_thread_started()
211
+
195
212
  try:
196
213
  self._queue.put_nowait(trace)
197
214
  except queue.Full:
@@ -206,6 +223,9 @@ class BatchTraceProcessor(TracingProcessor):
206
223
  pass
207
224
 
208
225
  def on_span_end(self, span: Span[Any]) -> None:
226
+ # Ensure the background worker is running before we enqueue anything.
227
+ self._ensure_thread_started()
228
+
209
229
  try:
210
230
  self._queue.put_nowait(span)
211
231
  except queue.Full:
@@ -216,7 +236,13 @@ class BatchTraceProcessor(TracingProcessor):
216
236
  Called when the application stops. We signal our thread to stop, then join it.
217
237
  """
218
238
  self._shutdown_event.set()
219
- self._worker_thread.join(timeout=timeout)
239
+
240
+ # Only join if we ever started the background thread; otherwise flush synchronously.
241
+ if self._worker_thread and self._worker_thread.is_alive():
242
+ self._worker_thread.join(timeout=timeout)
243
+ else:
244
+ # No background thread: process any remaining items synchronously.
245
+ self._export_batches(force=True)
220
246
 
221
247
  def force_flush(self):
222
248
  """
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
3
3
  from pydantic import BaseModel
4
4
 
5
5
  if TYPE_CHECKING:
6
+ from ..exceptions import RunErrorDetails
6
7
  from ..result import RunResult, RunResultBase, RunResultStreaming
7
8
 
8
9
 
@@ -38,6 +39,17 @@ def pretty_print_result(result: "RunResult") -> str:
38
39
  return output
39
40
 
40
41
 
42
+ def pretty_print_run_error_details(result: "RunErrorDetails") -> str:
43
+ output = "RunErrorDetails:"
44
+ output += f'\n- Last agent: Agent(name="{result.last_agent.name}", ...)'
45
+ output += f"\n- {len(result.new_items)} new item(s)"
46
+ output += f"\n- {len(result.raw_responses)} raw response(s)"
47
+ output += f"\n- {len(result.input_guardrail_results)} input guardrail result(s)"
48
+ output += "\n(See `RunErrorDetails` for more details)"
49
+
50
+ return output
51
+
52
+
41
53
  def pretty_print_run_result_streaming(result: "RunResultStreaming") -> str:
42
54
  output = "RunResultStreaming:"
43
55
  output += f'\n- Current agent: Agent(name="{result.current_agent.name}", ...)'
agents/voice/model.py CHANGED
@@ -17,9 +17,11 @@ DEFAULT_TTS_BUFFER_SIZE = 120
17
17
  TTSVoice = Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]
18
18
  """Exportable type for the TTSModelSettings voice enum"""
19
19
 
20
+
20
21
  @dataclass
21
22
  class TTSModelSettings:
22
23
  """Settings for a TTS model."""
24
+
23
25
  voice: TTSVoice | None = None
24
26
  """
25
27
  The voice to use for the TTS model. If not provided, the default voice for the respective model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openai-agents
3
- Version: 0.0.16
3
+ Version: 0.0.17
4
4
  Summary: OpenAI Agents SDK
5
5
  Project-URL: Homepage, https://github.com/openai/openai-agents-python
6
6
  Project-URL: Repository, https://github.com/openai/openai-agents-python
@@ -1,37 +1,37 @@
1
- agents/__init__.py,sha256=VXDkkC21o3n0JbKx8YIDwHBzTqwsTJ2JsDmL2p7THuU,7394
1
+ agents/__init__.py,sha256=ZnZazUPdfh19uNOgNyu2OBQr5zz2DdUrKgam3Y9BAk4,7438
2
2
  agents/_config.py,sha256=ANrM7GP2VSQehDkMc9qocxkUlPwqU-i5sieMJyEwxpM,796
3
3
  agents/_debug.py,sha256=7OKys2lDjeCtGggTkM53m_8vw0WIr3yt-_JPBDAnsw0,608
4
- agents/_run_impl.py,sha256=1aowNDgxwqPeFsxEQ6P2a7hrlniM_U3lRuKujUoyhuo,42569
5
- agents/agent.py,sha256=aTC49v9sQJm0gv5a3hW8xCgtMhk2TfjycBP8JyeOJ84,10571
6
- agents/agent_output.py,sha256=fEK1Yn0XfMrLXZRsBcSig-YDZZ0kZpCgATdwZ-eHYqQ,7127
4
+ agents/_run_impl.py,sha256=DyIodrzaWNdydZWDKJT6wGg3v445jwBUOwxb5mM-c58,42742
5
+ agents/agent.py,sha256=eeOWjR-a0xOB4Ctt9OTl93rEr_VRAkynN2M0vfx2nTs,11195
6
+ agents/agent_output.py,sha256=cVIVwpsgOfloCHL0BD9DSCBCzW_s3T4LesDhvJRu2Uc,7127
7
7
  agents/computer.py,sha256=XD44UgiUWSfniv-xKwwDP6wFKVwBiZkpaL1hO-0-7ZA,2516
8
- agents/exceptions.py,sha256=F3AltRt27PGdhbFqKBhRJL9eHqoN4SQx7oxBn0GWmhs,1856
8
+ agents/exceptions.py,sha256=NHMdHE0cZ6AdA6UgUylTzVHAX05Ol1CkO814a0FdZcs,2862
9
9
  agents/function_schema.py,sha256=k4GTdxf5bvcisVr9b4xSdTGzkB5oP3XZnJMdouABCsw,12909
10
10
  agents/guardrail.py,sha256=vWWcApo9s_6aHapQ5AMko08MqC8Jrlk-J5iqIRctCDQ,9291
11
- agents/handoffs.py,sha256=wRg-HBGKBZev88mOg_mfv6CR8T2kewZM8eX3tb71l1g,9043
11
+ agents/handoffs.py,sha256=mWvtgWMJjSIlhUR9xf-pXOJbWVCKxNBXytP9tsPGWII,9045
12
12
  agents/items.py,sha256=lXFc_gKLEqwXIcyMKk4Q-6Rjry0MWD93xlvk4Y1W970,9695
13
13
  agents/lifecycle.py,sha256=wYFG6PLSKQ7bICKVbB8oGtdoJNINGq9obh2RSKlAkDE,2938
14
14
  agents/logger.py,sha256=p_ef7vWKpBev5FFybPJjhrCCQizK08Yy1A2EDO1SNNg,60
15
15
  agents/model_settings.py,sha256=7s9YjfHBVz1f1a-V3dd-8eMe-IAgfDXhQgChI27Kz00,3326
16
16
  agents/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
17
- agents/result.py,sha256=dhtOaLIoOp5PYC4qcsgoU5qg2yvdI_VKdCy6i2qth7k,9305
18
- agents/run.py,sha256=5E3hx0mc3Gs3QcCCuNIddlRt4TJ9e_3629X5MSeOGX0,40454
17
+ agents/result.py,sha256=YCGYHoc5X1_vLKu5QiK6F8C1ZXI3tTfLXaZoqbYgUMA,10753
18
+ agents/run.py,sha256=cGvRtw9Ck7gEthmdnUBtb82lD7y0JgIZFsjMXbkCJZY,41816
19
19
  agents/run_context.py,sha256=vuSUQM8O4CLensQY27-22fOqECnw7yvwL9U3WO8b_bk,851
20
- agents/stream_events.py,sha256=2zDCbJvUKnDfaogGltL6Yh9WvHxtTJf_z_IS0nvKjf0,1607
20
+ agents/stream_events.py,sha256=VFyTu-DT3ZMnHLtMbg-X_lxec0doQxNfx-hVxLB0BpI,1700
21
21
  agents/strict_schema.py,sha256=_KuEJkglmq-Fj3HSeYP4WqTvqrxbSKu6gezfz5Brhh0,5775
22
- agents/tool.py,sha256=CnGYyvDDYjIqGDSpDqUtbQtzeQrXdWJONMsFFmCpWHk,14711
22
+ agents/tool.py,sha256=yDUuR6oAO2NufHoJqKtqLExGx6ClHPTYYPsdraf39P0,15675
23
23
  agents/usage.py,sha256=GB83eElU-DVkdutGObGDSX5vJNy8ssu3Xbpp5LlHfwU,1643
24
24
  agents/version.py,sha256=_1knUwzSK-HUeZTpRUkk6Z-CIcurqXuEplbV5TLJ08E,230
25
25
  agents/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  agents/extensions/handoff_filters.py,sha256=2cXxu1JROez96CpTiGuT9PIuaIrIE8ksP01fX83krKM,1977
27
27
  agents/extensions/handoff_prompt.py,sha256=oGWN0uNh3Z1L7E-Ev2up8W084fFrDNOsLDy7P6bcmic,1006
28
- agents/extensions/visualization.py,sha256=AQFC7kQlZqTI6QVkyDHrF_DodCytrrhcLg35nfRd_JA,4256
28
+ agents/extensions/visualization.py,sha256=g2eEwW22qe3A4WtH37LwaHhK3QZE9FYHVw9IcOVpwbk,4699
29
29
  agents/extensions/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- agents/extensions/models/litellm_model.py,sha256=lLWLXmq5pUXAElyPSUKSmRbHPAiKjbG9aWvUSgTcS9s,14402
30
+ agents/extensions/models/litellm_model.py,sha256=zcjdGI2EyhKqiXnobl_WPuPL8_zl2sGDOz7bul3Kjzs,14447
31
31
  agents/extensions/models/litellm_provider.py,sha256=wTm00Anq8YoNb9AnyT0JOunDG-HCDm_98ORNy7aNJdw,928
32
32
  agents/mcp/__init__.py,sha256=_aDpMTvYCe1IezOEasZ0vmombBM8r7BD8lpXiKi-UlM,499
33
- agents/mcp/server.py,sha256=kIpQktrsNQUF0pNkERyfEcu-oYDvseBbsye_PzRmHnM,15910
34
- agents/mcp/util.py,sha256=dIEdYDMc7Sjp-DFQnvoc4VWU-B7Heyx0I41bcW7RlEg,5232
33
+ agents/mcp/server.py,sha256=mP_JxJzz00prX_0SzTZO38bjvhj4A61icypUjvxdG4k,15915
34
+ agents/mcp/util.py,sha256=qXbAo9O-yv0JfmZBxDJIQ8ieHMTNWTEX5lnSVBv637k,5243
35
35
  agents/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  agents/models/_openai_shared.py,sha256=4Ngwo2Fv2RXY61Pqck1cYPkSln2tDnb8Ai-ao4QG-iE,836
37
37
  agents/models/chatcmpl_converter.py,sha256=Sae-ITlhQz8_SiFiSat7Z-lavqIuczduOXR_PF_f6cs,18126
@@ -40,14 +40,14 @@ agents/models/chatcmpl_stream_handler.py,sha256=sDl8O7AKxpWxAq7-bgCUClD5JySUnbQ8
40
40
  agents/models/fake_id.py,sha256=lbXjUUSMeAQ8eFx4V5QLUnBClHE6adJlYYav55RlG5w,268
41
41
  agents/models/interface.py,sha256=eEpiIBn9MxsmXUK1HPpn3c7TYPduBYC7tsWnDHSYJHo,3553
42
42
  agents/models/multi_provider.py,sha256=aiDbls5G4YomPfN6qH1pGlj41WS5jlDp2T82zm6qcnM,5578
43
- agents/models/openai_chatcompletions.py,sha256=obO209PFcf-qXDXmpoIagRmiLMVqsCoG1Amtvu9JfX0,11034
43
+ agents/models/openai_chatcompletions.py,sha256=aSE1cww-C-6p5PXpslo70X-V0MHqbN6msLhnawFbhJU,11445
44
44
  agents/models/openai_provider.py,sha256=NMxTNaoTa329GrA7jj51LC02pb_e2eFh-PCvWADJrkY,3478
45
45
  agents/models/openai_responses.py,sha256=JFajISS-sYYxKhb66tZ5cYPEqIYOj6ap762Z-87c7fE,15368
46
46
  agents/tracing/__init__.py,sha256=-hJeEiNvgyQdEXpFTrr_qu_XYREvIrF5KyePDtovSak,2804
47
47
  agents/tracing/create.py,sha256=kkMf2pp5Te20YkiSvf3Xj3J9qMibQCjEAxZs1Lr_kTE,18124
48
48
  agents/tracing/logger.py,sha256=J4KUDRSGa7x5UVfUwWe-gbKwoaq8AeETRqkPt3QvtGg,68
49
49
  agents/tracing/processor_interface.py,sha256=wNyZCwNJko5CrUIWD_lMou5ppQ67CFYwvWRsJRM3up8,1659
50
- agents/tracing/processors.py,sha256=UVrP1UjhPoJKV-CDDVzxmw19miYK-W8T_-0sx40erZM,10259
50
+ agents/tracing/processors.py,sha256=lOdZHwo0rQAflVkKWOZinnWyLtS0stALyydiFOC0gss,11389
51
51
  agents/tracing/scope.py,sha256=u17_m8RPpGvbHrTkaO_kDi5ROBWhfOAIgBe7suiaRD4,1445
52
52
  agents/tracing/setup.py,sha256=YnEDTaRG_b510vtsXbOaCUZ0nf7MOr1ULvOpQOHtdBs,6776
53
53
  agents/tracing/span_data.py,sha256=nI2Fbu1ORE8ybE6m6RuddTJF5E5xFmEj8Mq5bSFv4bE,9017
@@ -58,7 +58,7 @@ agents/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  agents/util/_coro.py,sha256=S38XUYFC7bqTELSgMUBsAX1GoRlIrV7coupcUAWH__4,45
59
59
  agents/util/_error_tracing.py,sha256=hdkYNx180b18lP0PSB1toE5atNHsMg_Bm9Osw812vLo,421
60
60
  agents/util/_json.py,sha256=eKeQeMlQkBXRFeL3ilNZFmszGyfhtzZdW_GW_As6dcg,972
61
- agents/util/_pretty_print.py,sha256=rRVp24UmTgzCm-W4ritWBOxxnPRinzFdrZlOhTi1KVQ,2227
61
+ agents/util/_pretty_print.py,sha256=pnrM81KRG4G21jZnYrYBCkPgtUeP8qcnJm-9tpAV1WA,2738
62
62
  agents/util/_transforms.py,sha256=CZe74NOHkHneyo4fHYfFWksCSTn-kXtEyejL9P0_xlA,270
63
63
  agents/util/_types.py,sha256=8KxYfCw0gYSMWcQmacJoc3Q7Lc46LmT-AWvhF10KJ-E,160
64
64
  agents/voice/__init__.py,sha256=4VWBUjyoXC6dGFuk-oZQGg8T32bFxVwy371c-zDK-EU,1537
@@ -66,7 +66,7 @@ agents/voice/events.py,sha256=4aPAZC0__ocgmg_mcX4c1zv9Go-YdKIVItQ2kYgtye0,1216
66
66
  agents/voice/exceptions.py,sha256=QcyfvaUTBe4gxbFP82oDSa_puzZ4Z4O4k01B8pAHnK0,233
67
67
  agents/voice/imports.py,sha256=VaE5I8aJTP9Zl_0-y9dx1UcAP7KPRDMaikFK2jFnn8s,348
68
68
  agents/voice/input.py,sha256=FSbdHMIdLVKX4vYcmf3WBJ5dAlh5zMDjCAuGfXOZTQs,2910
69
- agents/voice/model.py,sha256=haVPgL3xlzkUyMeVaDphB74TlcZpruagMsx6CQxpx5g,5954
69
+ agents/voice/model.py,sha256=LWnIWEwU0-aFkff3kbTKkxejnYqzS2XHG5Qm2YcrzFI,5956
70
70
  agents/voice/pipeline.py,sha256=5LKTTDytQt4QlZzVKgbB9x3X2zA-TeR94FTi15vIUc0,6259
71
71
  agents/voice/pipeline_config.py,sha256=_cynbnzxvQijxkGrMYHJzIV54F9bRvDsPV24qexVO8c,1759
72
72
  agents/voice/result.py,sha256=Yx9JCMGCE9OfXacaBFfFLQJRwkNo5-h4Nqm9OPnemU4,11107
@@ -76,7 +76,7 @@ agents/voice/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
76
76
  agents/voice/models/openai_model_provider.py,sha256=Khn0uT-VhsEbe7_OhBMGFQzXNwL80gcWZyTHl3CaBII,3587
77
77
  agents/voice/models/openai_stt.py,sha256=rRsldkvkPhH4T0waX1dhccEqIwmPYh-teK_LRvBgiNI,16882
78
78
  agents/voice/models/openai_tts.py,sha256=4KoLQuFDHKu5a1VTJlu9Nj3MHwMlrn9wfT_liJDJ2dw,1477
79
- openai_agents-0.0.16.dist-info/METADATA,sha256=EVGuugo_KTS-mZ0Luqa-wXEsgu8loIFS2lXwfzdJyR8,8163
80
- openai_agents-0.0.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
81
- openai_agents-0.0.16.dist-info/licenses/LICENSE,sha256=E994EspT7Krhy0qGiES7WYNzBHrh1YDk3r--8d1baRU,1063
82
- openai_agents-0.0.16.dist-info/RECORD,,
79
+ openai_agents-0.0.17.dist-info/METADATA,sha256=2SF0ZEolF_69dW0eTSWGuJc_RLuOAnYkqvBqj4IgqCw,8163
80
+ openai_agents-0.0.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
81
+ openai_agents-0.0.17.dist-info/licenses/LICENSE,sha256=E994EspT7Krhy0qGiES7WYNzBHrh1YDk3r--8d1baRU,1063
82
+ openai_agents-0.0.17.dist-info/RECORD,,