openai-agents 0.1.0__py3-none-any.whl → 0.2.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 (39) hide show
  1. agents/__init__.py +5 -1
  2. agents/_run_impl.py +5 -1
  3. agents/agent.py +62 -30
  4. agents/agent_output.py +2 -2
  5. agents/function_schema.py +11 -1
  6. agents/guardrail.py +5 -1
  7. agents/handoffs.py +32 -14
  8. agents/lifecycle.py +26 -17
  9. agents/mcp/server.py +82 -11
  10. agents/mcp/util.py +16 -9
  11. agents/memory/__init__.py +3 -0
  12. agents/memory/session.py +369 -0
  13. agents/model_settings.py +15 -7
  14. agents/models/chatcmpl_converter.py +20 -3
  15. agents/models/chatcmpl_stream_handler.py +134 -43
  16. agents/models/openai_responses.py +12 -5
  17. agents/realtime/README.md +3 -0
  18. agents/realtime/__init__.py +177 -0
  19. agents/realtime/agent.py +89 -0
  20. agents/realtime/config.py +188 -0
  21. agents/realtime/events.py +216 -0
  22. agents/realtime/handoffs.py +165 -0
  23. agents/realtime/items.py +184 -0
  24. agents/realtime/model.py +69 -0
  25. agents/realtime/model_events.py +159 -0
  26. agents/realtime/model_inputs.py +100 -0
  27. agents/realtime/openai_realtime.py +670 -0
  28. agents/realtime/runner.py +118 -0
  29. agents/realtime/session.py +535 -0
  30. agents/run.py +106 -4
  31. agents/tool.py +6 -7
  32. agents/tool_context.py +16 -3
  33. agents/voice/models/openai_stt.py +1 -1
  34. agents/voice/pipeline.py +6 -0
  35. agents/voice/workflow.py +8 -0
  36. {openai_agents-0.1.0.dist-info → openai_agents-0.2.1.dist-info}/METADATA +121 -4
  37. {openai_agents-0.1.0.dist-info → openai_agents-0.2.1.dist-info}/RECORD +39 -24
  38. {openai_agents-0.1.0.dist-info → openai_agents-0.2.1.dist-info}/WHEEL +0 -0
  39. {openai_agents-0.1.0.dist-info → openai_agents-0.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,188 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import (
4
+ Any,
5
+ Literal,
6
+ Union,
7
+ )
8
+
9
+ from typing_extensions import NotRequired, TypeAlias, TypedDict
10
+
11
+ from ..guardrail import OutputGuardrail
12
+ from ..handoffs import Handoff
13
+ from ..model_settings import ToolChoice
14
+ from ..tool import Tool
15
+
16
+ RealtimeModelName: TypeAlias = Union[
17
+ Literal[
18
+ "gpt-4o-realtime-preview",
19
+ "gpt-4o-mini-realtime-preview",
20
+ "gpt-4o-realtime-preview-2025-06-03",
21
+ "gpt-4o-realtime-preview-2024-12-17",
22
+ "gpt-4o-realtime-preview-2024-10-01",
23
+ "gpt-4o-mini-realtime-preview-2024-12-17",
24
+ ],
25
+ str,
26
+ ]
27
+ """The name of a realtime model."""
28
+
29
+
30
+ RealtimeAudioFormat: TypeAlias = Union[Literal["pcm16", "g711_ulaw", "g711_alaw"], str]
31
+ """The audio format for realtime audio streams."""
32
+
33
+
34
+ class RealtimeClientMessage(TypedDict):
35
+ """A raw message to be sent to the model."""
36
+
37
+ type: str # explicitly required
38
+ """The type of the message."""
39
+
40
+ other_data: NotRequired[dict[str, Any]]
41
+ """Merged into the message body."""
42
+
43
+
44
+ class RealtimeInputAudioTranscriptionConfig(TypedDict):
45
+ """Configuration for audio transcription in realtime sessions."""
46
+
47
+ language: NotRequired[str]
48
+ """The language code for transcription."""
49
+
50
+ model: NotRequired[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] | str]
51
+ """The transcription model to use."""
52
+
53
+ prompt: NotRequired[str]
54
+ """An optional prompt to guide transcription."""
55
+
56
+
57
+ class RealtimeTurnDetectionConfig(TypedDict):
58
+ """Turn detection config. Allows extra vendor keys if needed."""
59
+
60
+ type: NotRequired[Literal["semantic_vad", "server_vad"]]
61
+ """The type of voice activity detection to use."""
62
+
63
+ create_response: NotRequired[bool]
64
+ """Whether to create a response when a turn is detected."""
65
+
66
+ eagerness: NotRequired[Literal["auto", "low", "medium", "high"]]
67
+ """How eagerly to detect turn boundaries."""
68
+
69
+ interrupt_response: NotRequired[bool]
70
+ """Whether to allow interrupting the assistant's response."""
71
+
72
+ prefix_padding_ms: NotRequired[int]
73
+ """Padding time in milliseconds before turn detection."""
74
+
75
+ silence_duration_ms: NotRequired[int]
76
+ """Duration of silence in milliseconds to trigger turn detection."""
77
+
78
+ threshold: NotRequired[float]
79
+ """The threshold for voice activity detection."""
80
+
81
+
82
+ class RealtimeSessionModelSettings(TypedDict):
83
+ """Model settings for a realtime model session."""
84
+
85
+ model_name: NotRequired[RealtimeModelName]
86
+ """The name of the realtime model to use."""
87
+
88
+ instructions: NotRequired[str]
89
+ """System instructions for the model."""
90
+
91
+ modalities: NotRequired[list[Literal["text", "audio"]]]
92
+ """The modalities the model should support."""
93
+
94
+ voice: NotRequired[str]
95
+ """The voice to use for audio output."""
96
+
97
+ input_audio_format: NotRequired[RealtimeAudioFormat]
98
+ """The format for input audio streams."""
99
+
100
+ output_audio_format: NotRequired[RealtimeAudioFormat]
101
+ """The format for output audio streams."""
102
+
103
+ input_audio_transcription: NotRequired[RealtimeInputAudioTranscriptionConfig]
104
+ """Configuration for transcribing input audio."""
105
+
106
+ turn_detection: NotRequired[RealtimeTurnDetectionConfig]
107
+ """Configuration for detecting conversation turns."""
108
+
109
+ tool_choice: NotRequired[ToolChoice]
110
+ """How the model should choose which tools to call."""
111
+
112
+ tools: NotRequired[list[Tool]]
113
+ """List of tools available to the model."""
114
+
115
+ handoffs: NotRequired[list[Handoff]]
116
+ """List of handoff configurations."""
117
+
118
+ tracing: NotRequired[RealtimeModelTracingConfig | None]
119
+ """Configuration for request tracing."""
120
+
121
+
122
+ class RealtimeGuardrailsSettings(TypedDict):
123
+ """Settings for output guardrails in realtime sessions."""
124
+
125
+ debounce_text_length: NotRequired[int]
126
+ """
127
+ The minimum number of characters to accumulate before running guardrails on transcript
128
+ deltas. Defaults to 100. Guardrails run every time the accumulated text reaches
129
+ 1x, 2x, 3x, etc. times this threshold.
130
+ """
131
+
132
+
133
+ class RealtimeModelTracingConfig(TypedDict):
134
+ """Configuration for tracing in realtime model sessions."""
135
+
136
+ workflow_name: NotRequired[str]
137
+ """The workflow name to use for tracing."""
138
+
139
+ group_id: NotRequired[str]
140
+ """A group identifier to use for tracing, to link multiple traces together."""
141
+
142
+ metadata: NotRequired[dict[str, Any]]
143
+ """Additional metadata to include with the trace."""
144
+
145
+
146
+ class RealtimeRunConfig(TypedDict):
147
+ """Configuration for running a realtime agent session."""
148
+
149
+ model_settings: NotRequired[RealtimeSessionModelSettings]
150
+ """Settings for the realtime model session."""
151
+
152
+ output_guardrails: NotRequired[list[OutputGuardrail[Any]]]
153
+ """List of output guardrails to run on the agent's responses."""
154
+
155
+ guardrails_settings: NotRequired[RealtimeGuardrailsSettings]
156
+ """Settings for guardrail execution."""
157
+
158
+ tracing_disabled: NotRequired[bool]
159
+ """Whether tracing is disabled for this run."""
160
+
161
+ # TODO (rm) Add history audio storage config
162
+
163
+
164
+ class RealtimeUserInputText(TypedDict):
165
+ """A text input from the user."""
166
+
167
+ type: Literal["input_text"]
168
+ """The type identifier for text input."""
169
+
170
+ text: str
171
+ """The text content from the user."""
172
+
173
+
174
+ class RealtimeUserInputMessage(TypedDict):
175
+ """A message input from the user."""
176
+
177
+ type: Literal["message"]
178
+ """The type identifier for message inputs."""
179
+
180
+ role: Literal["user"]
181
+ """The role identifier for user messages."""
182
+
183
+ content: list[RealtimeUserInputText]
184
+ """List of text content items in the message."""
185
+
186
+
187
+ RealtimeUserInput: TypeAlias = Union[str, RealtimeUserInputMessage]
188
+ """User input that can be a string or structured message."""
@@ -0,0 +1,216 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Literal, Union
5
+
6
+ from typing_extensions import TypeAlias
7
+
8
+ from ..guardrail import OutputGuardrailResult
9
+ from ..run_context import RunContextWrapper
10
+ from ..tool import Tool
11
+ from .agent import RealtimeAgent
12
+ from .items import RealtimeItem
13
+ from .model_events import RealtimeModelAudioEvent, RealtimeModelEvent
14
+
15
+
16
+ @dataclass
17
+ class RealtimeEventInfo:
18
+ context: RunContextWrapper
19
+ """The context for the event."""
20
+
21
+
22
+ @dataclass
23
+ class RealtimeAgentStartEvent:
24
+ """A new agent has started."""
25
+
26
+ agent: RealtimeAgent
27
+ """The new agent."""
28
+
29
+ info: RealtimeEventInfo
30
+ """Common info for all events, such as the context."""
31
+
32
+ type: Literal["agent_start"] = "agent_start"
33
+
34
+
35
+ @dataclass
36
+ class RealtimeAgentEndEvent:
37
+ """An agent has ended."""
38
+
39
+ agent: RealtimeAgent
40
+ """The agent that ended."""
41
+
42
+ info: RealtimeEventInfo
43
+ """Common info for all events, such as the context."""
44
+
45
+ type: Literal["agent_end"] = "agent_end"
46
+
47
+
48
+ @dataclass
49
+ class RealtimeHandoffEvent:
50
+ """An agent has handed off to another agent."""
51
+
52
+ from_agent: RealtimeAgent
53
+ """The agent that handed off."""
54
+
55
+ to_agent: RealtimeAgent
56
+ """The agent that was handed off to."""
57
+
58
+ info: RealtimeEventInfo
59
+ """Common info for all events, such as the context."""
60
+
61
+ type: Literal["handoff"] = "handoff"
62
+
63
+
64
+ @dataclass
65
+ class RealtimeToolStart:
66
+ """An agent is starting a tool call."""
67
+
68
+ agent: RealtimeAgent
69
+ """The agent that updated."""
70
+
71
+ tool: Tool
72
+
73
+ info: RealtimeEventInfo
74
+ """Common info for all events, such as the context."""
75
+
76
+ type: Literal["tool_start"] = "tool_start"
77
+
78
+
79
+ @dataclass
80
+ class RealtimeToolEnd:
81
+ """An agent has ended a tool call."""
82
+
83
+ agent: RealtimeAgent
84
+ """The agent that ended the tool call."""
85
+
86
+ tool: Tool
87
+ """The tool that was called."""
88
+
89
+ output: Any
90
+ """The output of the tool call."""
91
+
92
+ info: RealtimeEventInfo
93
+ """Common info for all events, such as the context."""
94
+
95
+ type: Literal["tool_end"] = "tool_end"
96
+
97
+
98
+ @dataclass
99
+ class RealtimeRawModelEvent:
100
+ """Forwards raw events from the model layer."""
101
+
102
+ data: RealtimeModelEvent
103
+ """The raw data from the model layer."""
104
+
105
+ info: RealtimeEventInfo
106
+ """Common info for all events, such as the context."""
107
+
108
+ type: Literal["raw_model_event"] = "raw_model_event"
109
+
110
+
111
+ @dataclass
112
+ class RealtimeAudioEnd:
113
+ """Triggered when the agent stops generating audio."""
114
+
115
+ info: RealtimeEventInfo
116
+ """Common info for all events, such as the context."""
117
+
118
+ type: Literal["audio_end"] = "audio_end"
119
+
120
+
121
+ @dataclass
122
+ class RealtimeAudio:
123
+ """Triggered when the agent generates new audio to be played."""
124
+
125
+ audio: RealtimeModelAudioEvent
126
+ """The audio event from the model layer."""
127
+
128
+ info: RealtimeEventInfo
129
+ """Common info for all events, such as the context."""
130
+
131
+ type: Literal["audio"] = "audio"
132
+
133
+
134
+ @dataclass
135
+ class RealtimeAudioInterrupted:
136
+ """Triggered when the agent is interrupted. Can be listened to by the user to stop audio
137
+ playback or give visual indicators to the user.
138
+ """
139
+
140
+ info: RealtimeEventInfo
141
+ """Common info for all events, such as the context."""
142
+
143
+ type: Literal["audio_interrupted"] = "audio_interrupted"
144
+
145
+
146
+ @dataclass
147
+ class RealtimeError:
148
+ """An error has occurred."""
149
+
150
+ error: Any
151
+ """The error that occurred."""
152
+
153
+ info: RealtimeEventInfo
154
+ """Common info for all events, such as the context."""
155
+
156
+ type: Literal["error"] = "error"
157
+
158
+
159
+ @dataclass
160
+ class RealtimeHistoryUpdated:
161
+ """The history has been updated. Contains the full history of the session."""
162
+
163
+ history: list[RealtimeItem]
164
+ """The full history of the session."""
165
+
166
+ info: RealtimeEventInfo
167
+ """Common info for all events, such as the context."""
168
+
169
+ type: Literal["history_updated"] = "history_updated"
170
+
171
+
172
+ @dataclass
173
+ class RealtimeHistoryAdded:
174
+ """A new item has been added to the history."""
175
+
176
+ item: RealtimeItem
177
+ """The new item that was added to the history."""
178
+
179
+ info: RealtimeEventInfo
180
+ """Common info for all events, such as the context."""
181
+
182
+ type: Literal["history_added"] = "history_added"
183
+
184
+
185
+ @dataclass
186
+ class RealtimeGuardrailTripped:
187
+ """A guardrail has been tripped and the agent has been interrupted."""
188
+
189
+ guardrail_results: list[OutputGuardrailResult]
190
+ """The results from all triggered guardrails."""
191
+
192
+ message: str
193
+ """The message that was being generated when the guardrail was triggered."""
194
+
195
+ info: RealtimeEventInfo
196
+ """Common info for all events, such as the context."""
197
+
198
+ type: Literal["guardrail_tripped"] = "guardrail_tripped"
199
+
200
+
201
+ RealtimeSessionEvent: TypeAlias = Union[
202
+ RealtimeAgentStartEvent,
203
+ RealtimeAgentEndEvent,
204
+ RealtimeHandoffEvent,
205
+ RealtimeToolStart,
206
+ RealtimeToolEnd,
207
+ RealtimeRawModelEvent,
208
+ RealtimeAudioEnd,
209
+ RealtimeAudio,
210
+ RealtimeAudioInterrupted,
211
+ RealtimeError,
212
+ RealtimeHistoryUpdated,
213
+ RealtimeHistoryAdded,
214
+ RealtimeGuardrailTripped,
215
+ ]
216
+ """An event emitted by the realtime session."""
@@ -0,0 +1,165 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import TYPE_CHECKING, Any, Callable, cast, overload
5
+
6
+ from pydantic import TypeAdapter
7
+ from typing_extensions import TypeVar
8
+
9
+ from ..exceptions import ModelBehaviorError, UserError
10
+ from ..handoffs import Handoff
11
+ from ..run_context import RunContextWrapper, TContext
12
+ from ..strict_schema import ensure_strict_json_schema
13
+ from ..tracing.spans import SpanError
14
+ from ..util import _error_tracing, _json
15
+ from ..util._types import MaybeAwaitable
16
+
17
+ if TYPE_CHECKING:
18
+ from ..agent import AgentBase
19
+ from . import RealtimeAgent
20
+
21
+
22
+ # The handoff input type is the type of data passed when the agent is called via a handoff.
23
+ THandoffInput = TypeVar("THandoffInput", default=Any)
24
+
25
+ OnHandoffWithInput = Callable[[RunContextWrapper[Any], THandoffInput], Any]
26
+ OnHandoffWithoutInput = Callable[[RunContextWrapper[Any]], Any]
27
+
28
+
29
+ @overload
30
+ def realtime_handoff(
31
+ agent: RealtimeAgent[TContext],
32
+ *,
33
+ tool_name_override: str | None = None,
34
+ tool_description_override: str | None = None,
35
+ is_enabled: bool
36
+ | Callable[[RunContextWrapper[Any], RealtimeAgent[Any]], MaybeAwaitable[bool]] = True,
37
+ ) -> Handoff[TContext, RealtimeAgent[TContext]]: ...
38
+
39
+
40
+ @overload
41
+ def realtime_handoff(
42
+ agent: RealtimeAgent[TContext],
43
+ *,
44
+ on_handoff: OnHandoffWithInput[THandoffInput],
45
+ input_type: type[THandoffInput],
46
+ tool_description_override: str | None = None,
47
+ tool_name_override: str | None = None,
48
+ is_enabled: bool
49
+ | Callable[[RunContextWrapper[Any], RealtimeAgent[Any]], MaybeAwaitable[bool]] = True,
50
+ ) -> Handoff[TContext, RealtimeAgent[TContext]]: ...
51
+
52
+
53
+ @overload
54
+ def realtime_handoff(
55
+ agent: RealtimeAgent[TContext],
56
+ *,
57
+ on_handoff: OnHandoffWithoutInput,
58
+ tool_description_override: str | None = None,
59
+ tool_name_override: str | None = None,
60
+ is_enabled: bool
61
+ | Callable[[RunContextWrapper[Any], RealtimeAgent[Any]], MaybeAwaitable[bool]] = True,
62
+ ) -> Handoff[TContext, RealtimeAgent[TContext]]: ...
63
+
64
+
65
+ def realtime_handoff(
66
+ agent: RealtimeAgent[TContext],
67
+ tool_name_override: str | None = None,
68
+ tool_description_override: str | None = None,
69
+ on_handoff: OnHandoffWithInput[THandoffInput] | OnHandoffWithoutInput | None = None,
70
+ input_type: type[THandoffInput] | None = None,
71
+ is_enabled: bool
72
+ | Callable[[RunContextWrapper[Any], RealtimeAgent[Any]], MaybeAwaitable[bool]] = True,
73
+ ) -> Handoff[TContext, RealtimeAgent[TContext]]:
74
+ """Create a handoff from a RealtimeAgent.
75
+
76
+ Args:
77
+ agent: The RealtimeAgent to handoff to, or a function that returns a RealtimeAgent.
78
+ tool_name_override: Optional override for the name of the tool that represents the handoff.
79
+ tool_description_override: Optional override for the description of the tool that
80
+ represents the handoff.
81
+ on_handoff: A function that runs when the handoff is invoked.
82
+ input_type: the type of the input to the handoff. If provided, the input will be validated
83
+ against this type. Only relevant if you pass a function that takes an input.
84
+ is_enabled: Whether the handoff is enabled. Can be a bool or a callable that takes the run
85
+ context and agent and returns whether the handoff is enabled. Disabled handoffs are
86
+ hidden from the LLM at runtime.
87
+
88
+ Note: input_filter is not supported for RealtimeAgent handoffs.
89
+ """
90
+ assert (on_handoff and input_type) or not (on_handoff and input_type), (
91
+ "You must provide either both on_handoff and input_type, or neither"
92
+ )
93
+ type_adapter: TypeAdapter[Any] | None
94
+ if input_type is not None:
95
+ assert callable(on_handoff), "on_handoff must be callable"
96
+ sig = inspect.signature(on_handoff)
97
+ if len(sig.parameters) != 2:
98
+ raise UserError("on_handoff must take two arguments: context and input")
99
+
100
+ type_adapter = TypeAdapter(input_type)
101
+ input_json_schema = type_adapter.json_schema()
102
+ else:
103
+ type_adapter = None
104
+ input_json_schema = {}
105
+ if on_handoff is not None:
106
+ sig = inspect.signature(on_handoff)
107
+ if len(sig.parameters) != 1:
108
+ raise UserError("on_handoff must take one argument: context")
109
+
110
+ async def _invoke_handoff(
111
+ ctx: RunContextWrapper[Any], input_json: str | None = None
112
+ ) -> RealtimeAgent[TContext]:
113
+ if input_type is not None and type_adapter is not None:
114
+ if input_json is None:
115
+ _error_tracing.attach_error_to_current_span(
116
+ SpanError(
117
+ message="Handoff function expected non-null input, but got None",
118
+ data={"details": "input_json is None"},
119
+ )
120
+ )
121
+ raise ModelBehaviorError("Handoff function expected non-null input, but got None")
122
+
123
+ validated_input = _json.validate_json(
124
+ json_str=input_json,
125
+ type_adapter=type_adapter,
126
+ partial=False,
127
+ )
128
+ input_func = cast(OnHandoffWithInput[THandoffInput], on_handoff)
129
+ if inspect.iscoroutinefunction(input_func):
130
+ await input_func(ctx, validated_input)
131
+ else:
132
+ input_func(ctx, validated_input)
133
+ elif on_handoff is not None:
134
+ no_input_func = cast(OnHandoffWithoutInput, on_handoff)
135
+ if inspect.iscoroutinefunction(no_input_func):
136
+ await no_input_func(ctx)
137
+ else:
138
+ no_input_func(ctx)
139
+
140
+ return agent
141
+
142
+ tool_name = tool_name_override or Handoff.default_tool_name(agent)
143
+ tool_description = tool_description_override or Handoff.default_tool_description(agent)
144
+
145
+ # Always ensure the input JSON schema is in strict mode
146
+ # If there is a need, we can make this configurable in the future
147
+ input_json_schema = ensure_strict_json_schema(input_json_schema)
148
+
149
+ async def _is_enabled(ctx: RunContextWrapper[Any], agent_base: AgentBase[Any]) -> bool:
150
+ assert callable(is_enabled), "is_enabled must be non-null here"
151
+ assert isinstance(agent_base, RealtimeAgent), "Can't handoff to a non-RealtimeAgent"
152
+ result = is_enabled(ctx, agent_base)
153
+ if inspect.isawaitable(result):
154
+ return await result
155
+ return result
156
+
157
+ return Handoff(
158
+ tool_name=tool_name,
159
+ tool_description=tool_description,
160
+ input_json_schema=input_json_schema,
161
+ on_invoke_handoff=_invoke_handoff,
162
+ input_filter=None, # Not supported for RealtimeAgent handoffs
163
+ agent_name=agent.name,
164
+ is_enabled=_is_enabled if callable(is_enabled) else is_enabled,
165
+ )