Skip to content

Commit 5956ac5

Browse files
authored
Update model_events.py
1 parent ca0804e commit 5956ac5

File tree

1 file changed

+198
-89
lines changed

1 file changed

+198
-89
lines changed

src/agents/realtime/model_events.py

Lines changed: 198 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,262 @@
1+
"""
2+
Realtime model events module with Pydantic validation.
3+
4+
This module defines all model-level events that can be emitted during realtime model operations.
5+
"""
6+
17
from __future__ import annotations
28

3-
from dataclasses import dataclass
49
from typing import Any, Literal, Union
510

11+
from pydantic import BaseModel, ConfigDict, Field
612
from typing_extensions import TypeAlias
713

814
from .items import RealtimeItem
915

1016
RealtimeConnectionStatus: TypeAlias = Literal["connecting", "connected", "disconnected"]
1117

1218

13-
@dataclass
14-
class RealtimeModelErrorEvent:
19+
class RealtimeModelErrorEvent(BaseModel):
1520
"""Represents a transport‑layer error."""
1621

17-
error: Any
22+
model_config = ConfigDict(
23+
arbitrary_types_allowed=True,
24+
validate_assignment=True,
25+
extra="forbid",
26+
frozen=True,
27+
)
1828

19-
type: Literal["error"] = "error"
29+
error: Any = Field(..., description="The error that occurred.")
30+
type: Literal["error"] = Field(default="error", description="Event type identifier.")
2031

2132

22-
@dataclass
23-
class RealtimeModelToolCallEvent:
33+
class RealtimeModelToolCallEvent(BaseModel):
2434
"""Model attempted a tool/function call."""
2535

26-
name: str
27-
call_id: str
28-
arguments: str
29-
30-
id: str | None = None
31-
previous_item_id: str | None = None
32-
33-
type: Literal["function_call"] = "function_call"
34-
35-
36-
@dataclass
37-
class RealtimeModelAudioEvent:
36+
model_config = ConfigDict(
37+
arbitrary_types_allowed=True,
38+
validate_assignment=True,
39+
extra="forbid",
40+
frozen=True,
41+
)
42+
43+
name: str = Field(..., description="The name of the tool/function being called.")
44+
call_id: str = Field(..., description="Unique identifier for this tool call.")
45+
arguments: str = Field(..., description="JSON string of arguments for the tool call.")
46+
id: str | None = Field(default=None, description="Optional item ID.")
47+
previous_item_id: str | None = Field(
48+
default=None, description="ID of the previous item in the conversation."
49+
)
50+
type: Literal["function_call"] = Field(
51+
default="function_call", description="Event type identifier."
52+
)
53+
54+
55+
class RealtimeModelAudioEvent(BaseModel):
3856
"""Raw audio bytes emitted by the model."""
3957

40-
data: bytes
41-
response_id: str
42-
43-
item_id: str
44-
"""The ID of the item containing audio."""
45-
46-
content_index: int
47-
"""The index of the audio content in `item.content`"""
58+
model_config = ConfigDict(
59+
arbitrary_types_allowed=True,
60+
validate_assignment=True,
61+
extra="forbid",
62+
frozen=True,
63+
)
4864

49-
type: Literal["audio"] = "audio"
65+
data: bytes = Field(..., description="The raw audio data bytes.")
66+
response_id: str = Field(..., description="ID of the response containing this audio.")
67+
item_id: str = Field(..., description="The ID of the item containing audio.")
68+
content_index: int = Field(..., description="The index of the audio content in `item.content`")
69+
type: Literal["audio"] = Field(default="audio", description="Event type identifier.")
5070

5171

52-
@dataclass
53-
class RealtimeModelAudioInterruptedEvent:
72+
class RealtimeModelAudioInterruptedEvent(BaseModel):
5473
"""Audio interrupted."""
5574

56-
item_id: str
57-
"""The ID of the item containing audio."""
75+
model_config = ConfigDict(
76+
arbitrary_types_allowed=True,
77+
validate_assignment=True,
78+
extra="forbid",
79+
frozen=True,
80+
)
5881

59-
content_index: int
60-
"""The index of the audio content in `item.content`"""
82+
item_id: str = Field(..., description="The ID of the item containing audio.")
83+
content_index: int = Field(..., description="The index of the audio content in `item.content`")
84+
type: Literal["audio_interrupted"] = Field(
85+
default="audio_interrupted", description="Event type identifier."
86+
)
6187

62-
type: Literal["audio_interrupted"] = "audio_interrupted"
6388

64-
65-
@dataclass
66-
class RealtimeModelAudioDoneEvent:
89+
class RealtimeModelAudioDoneEvent(BaseModel):
6790
"""Audio done."""
6891

69-
item_id: str
70-
"""The ID of the item containing audio."""
71-
72-
content_index: int
73-
"""The index of the audio content in `item.content`"""
92+
model_config = ConfigDict(
93+
arbitrary_types_allowed=True,
94+
validate_assignment=True,
95+
extra="forbid",
96+
frozen=True,
97+
)
7498

75-
type: Literal["audio_done"] = "audio_done"
99+
item_id: str = Field(..., description="The ID of the item containing audio.")
100+
content_index: int = Field(..., description="The index of the audio content in `item.content`")
101+
type: Literal["audio_done"] = Field(default="audio_done", description="Event type identifier.")
76102

77103

78-
@dataclass
79-
class RealtimeModelInputAudioTranscriptionCompletedEvent:
104+
class RealtimeModelInputAudioTranscriptionCompletedEvent(BaseModel):
80105
"""Input audio transcription completed."""
81106

82-
item_id: str
83-
transcript: str
107+
model_config = ConfigDict(
108+
arbitrary_types_allowed=True,
109+
validate_assignment=True,
110+
extra="forbid",
111+
frozen=True,
112+
)
84113

85-
type: Literal["input_audio_transcription_completed"] = "input_audio_transcription_completed"
114+
item_id: str = Field(..., description="ID of the audio item that was transcribed.")
115+
transcript: str = Field(..., description="The completed transcript text.")
116+
type: Literal["input_audio_transcription_completed"] = Field(
117+
default="input_audio_transcription_completed", description="Event type identifier."
118+
)
86119

87120

88-
@dataclass
89-
class RealtimeModelTranscriptDeltaEvent:
121+
class RealtimeModelTranscriptDeltaEvent(BaseModel):
90122
"""Partial transcript update."""
91123

92-
item_id: str
93-
delta: str
94-
response_id: str
124+
model_config = ConfigDict(
125+
arbitrary_types_allowed=True,
126+
validate_assignment=True,
127+
extra="forbid",
128+
frozen=True,
129+
)
95130

96-
type: Literal["transcript_delta"] = "transcript_delta"
131+
item_id: str = Field(..., description="ID of the item being transcribed.")
132+
delta: str = Field(..., description="The incremental transcript text.")
133+
response_id: str = Field(..., description="ID of the response containing this transcript.")
134+
type: Literal["transcript_delta"] = Field(
135+
default="transcript_delta", description="Event type identifier."
136+
)
97137

98138

99-
@dataclass
100-
class RealtimeModelItemUpdatedEvent:
139+
class RealtimeModelItemUpdatedEvent(BaseModel):
101140
"""Item added to the history or updated."""
102141

103-
item: RealtimeItem
142+
model_config = ConfigDict(
143+
arbitrary_types_allowed=True,
144+
validate_assignment=True,
145+
extra="forbid",
146+
frozen=True,
147+
)
104148

105-
type: Literal["item_updated"] = "item_updated"
149+
item: RealtimeItem = Field(..., description="The item that was updated or added.")
150+
type: Literal["item_updated"] = Field(
151+
default="item_updated", description="Event type identifier."
152+
)
106153

107154

108-
@dataclass
109-
class RealtimeModelItemDeletedEvent:
155+
class RealtimeModelItemDeletedEvent(BaseModel):
110156
"""Item deleted from the history."""
111157

112-
item_id: str
158+
model_config = ConfigDict(
159+
arbitrary_types_allowed=True,
160+
validate_assignment=True,
161+
extra="forbid",
162+
frozen=True,
163+
)
113164

114-
type: Literal["item_deleted"] = "item_deleted"
165+
item_id: str = Field(..., description="ID of the deleted item.")
166+
type: Literal["item_deleted"] = Field(
167+
default="item_deleted", description="Event type identifier."
168+
)
115169

116170

117-
@dataclass
118-
class RealtimeModelConnectionStatusEvent:
171+
class RealtimeModelConnectionStatusEvent(BaseModel):
119172
"""Connection status changed."""
120173

121-
status: RealtimeConnectionStatus
174+
model_config = ConfigDict(
175+
arbitrary_types_allowed=True,
176+
validate_assignment=True,
177+
extra="forbid",
178+
frozen=True,
179+
)
122180

123-
type: Literal["connection_status"] = "connection_status"
181+
status: RealtimeConnectionStatus = Field(..., description="The new connection status.")
182+
type: Literal["connection_status"] = Field(
183+
default="connection_status", description="Event type identifier."
184+
)
124185

125186

126-
@dataclass
127-
class RealtimeModelTurnStartedEvent:
187+
class RealtimeModelTurnStartedEvent(BaseModel):
128188
"""Triggered when the model starts generating a response for a turn."""
129189

130-
type: Literal["turn_started"] = "turn_started"
190+
model_config = ConfigDict(
191+
arbitrary_types_allowed=True,
192+
validate_assignment=True,
193+
extra="forbid",
194+
frozen=True,
195+
)
131196

197+
type: Literal["turn_started"] = Field(
198+
default="turn_started", description="Event type identifier."
199+
)
132200

133-
@dataclass
134-
class RealtimeModelTurnEndedEvent:
201+
202+
class RealtimeModelTurnEndedEvent(BaseModel):
135203
"""Triggered when the model finishes generating a response for a turn."""
136204

137-
type: Literal["turn_ended"] = "turn_ended"
205+
model_config = ConfigDict(
206+
arbitrary_types_allowed=True,
207+
validate_assignment=True,
208+
extra="forbid",
209+
frozen=True,
210+
)
211+
212+
type: Literal["turn_ended"] = Field(default="turn_ended", description="Event type identifier.")
138213

139214

140-
@dataclass
141-
class RealtimeModelOtherEvent:
215+
class RealtimeModelOtherEvent(BaseModel):
142216
"""Used as a catchall for vendor-specific events."""
143217

144-
data: Any
218+
model_config = ConfigDict(
219+
arbitrary_types_allowed=True,
220+
validate_assignment=True,
221+
extra="forbid",
222+
frozen=True,
223+
)
145224

146-
type: Literal["other"] = "other"
225+
data: Any = Field(..., description="The vendor-specific event data.")
226+
type: Literal["other"] = Field(default="other", description="Event type identifier.")
147227

148228

149-
@dataclass
150-
class RealtimeModelExceptionEvent:
229+
class RealtimeModelExceptionEvent(BaseModel):
151230
"""Exception occurred during model operation."""
152231

153-
exception: Exception
154-
context: str | None = None
232+
model_config = ConfigDict(
233+
arbitrary_types_allowed=True,
234+
validate_assignment=True,
235+
extra="forbid",
236+
frozen=True,
237+
)
155238

156-
type: Literal["exception"] = "exception"
239+
exception: Exception = Field(..., description="The exception that occurred.")
240+
context: str | None = Field(
241+
default=None, description="Additional context about where the exception occurred."
242+
)
243+
type: Literal["exception"] = Field(default="exception", description="Event type identifier.")
157244

158245

159-
@dataclass
160-
class RealtimeModelRawServerEvent:
246+
class RealtimeModelRawServerEvent(BaseModel):
161247
"""Raw events forwarded from the server."""
162248

163-
data: Any
249+
model_config = ConfigDict(
250+
arbitrary_types_allowed=True,
251+
validate_assignment=True,
252+
extra="forbid",
253+
frozen=True,
254+
)
164255

165-
type: Literal["raw_server_event"] = "raw_server_event"
166-
167-
168-
# TODO (rm) Add usage events
256+
data: Any = Field(..., description="The raw server event data.")
257+
type: Literal["raw_server_event"] = Field(
258+
default="raw_server_event", description="Event type identifier."
259+
)
169260

170261

171262
RealtimeModelEvent: TypeAlias = Union[
@@ -185,3 +276,21 @@ class RealtimeModelRawServerEvent:
185276
RealtimeModelExceptionEvent,
186277
RealtimeModelRawServerEvent,
187278
]
279+
280+
281+
# Rebuild models after all definitions
282+
RealtimeModelErrorEvent.model_rebuild()
283+
RealtimeModelToolCallEvent.model_rebuild()
284+
RealtimeModelAudioEvent.model_rebuild()
285+
RealtimeModelAudioInterruptedEvent.model_rebuild()
286+
RealtimeModelAudioDoneEvent.model_rebuild()
287+
RealtimeModelInputAudioTranscriptionCompletedEvent.model_rebuild()
288+
RealtimeModelTranscriptDeltaEvent.model_rebuild()
289+
RealtimeModelItemUpdatedEvent.model_rebuild()
290+
RealtimeModelItemDeletedEvent.model_rebuild()
291+
RealtimeModelConnectionStatusEvent.model_rebuild()
292+
RealtimeModelTurnStartedEvent.model_rebuild()
293+
RealtimeModelTurnEndedEvent.model_rebuild()
294+
RealtimeModelOtherEvent.model_rebuild()
295+
RealtimeModelExceptionEvent.model_rebuild()
296+
RealtimeModelRawServerEvent.model_rebuild()

0 commit comments

Comments
 (0)