openai 7.23.0 → 7.24.0

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.
Files changed (206) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +4 -1
  3. package/client.d.mts +1 -1
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +1 -1
  6. package/client.d.ts.map +1 -1
  7. package/client.js +43 -5
  8. package/client.js.map +1 -1
  9. package/client.mjs +43 -5
  10. package/client.mjs.map +1 -1
  11. package/internal/responses/canonical-output-text.d.mts +8 -4
  12. package/internal/responses/canonical-output-text.d.mts.map +1 -1
  13. package/internal/responses/canonical-output-text.d.ts +8 -4
  14. package/internal/responses/canonical-output-text.d.ts.map +1 -1
  15. package/internal/responses/canonical-output-text.js +11 -0
  16. package/internal/responses/canonical-output-text.js.map +1 -1
  17. package/internal/responses/canonical-output-text.mjs +11 -0
  18. package/internal/responses/canonical-output-text.mjs.map +1 -1
  19. package/internal/responses/response-accumulator.d.mts +12 -1
  20. package/internal/responses/response-accumulator.d.mts.map +1 -1
  21. package/internal/responses/response-accumulator.d.ts +12 -1
  22. package/internal/responses/response-accumulator.d.ts.map +1 -1
  23. package/internal/responses/response-accumulator.js +99 -25
  24. package/internal/responses/response-accumulator.js.map +1 -1
  25. package/internal/responses/response-accumulator.mjs +97 -26
  26. package/internal/responses/response-accumulator.mjs.map +1 -1
  27. package/internal/ws.d.mts +6 -2
  28. package/internal/ws.d.mts.map +1 -1
  29. package/internal/ws.d.ts +6 -2
  30. package/internal/ws.d.ts.map +1 -1
  31. package/internal/ws.js +5 -3
  32. package/internal/ws.js.map +1 -1
  33. package/internal/ws.mjs +5 -3
  34. package/internal/ws.mjs.map +1 -1
  35. package/lib/live/transcript-grouping.d.mts.map +1 -1
  36. package/lib/live/transcript-grouping.d.ts.map +1 -1
  37. package/lib/live/transcript-grouping.js +6 -5
  38. package/lib/live/transcript-grouping.js.map +1 -1
  39. package/lib/live/transcript-grouping.mjs +6 -5
  40. package/lib/live/transcript-grouping.mjs.map +1 -1
  41. package/lib/responses/responses-websocket-accumulator.d.mts +72 -0
  42. package/lib/responses/responses-websocket-accumulator.d.mts.map +1 -0
  43. package/lib/responses/responses-websocket-accumulator.d.ts +72 -0
  44. package/lib/responses/responses-websocket-accumulator.d.ts.map +1 -0
  45. package/lib/responses/responses-websocket-accumulator.js +291 -0
  46. package/lib/responses/responses-websocket-accumulator.js.map +1 -0
  47. package/lib/responses/responses-websocket-accumulator.mjs +287 -0
  48. package/lib/responses/responses-websocket-accumulator.mjs.map +1 -0
  49. package/package.json +1 -1
  50. package/realtime/translations/ws.d.mts +84 -0
  51. package/realtime/translations/ws.d.mts.map +1 -0
  52. package/realtime/translations/ws.d.ts +84 -0
  53. package/realtime/translations/ws.d.ts.map +1 -0
  54. package/realtime/translations/ws.js +444 -0
  55. package/realtime/translations/ws.js.map +1 -0
  56. package/realtime/translations/ws.mjs +439 -0
  57. package/realtime/translations/ws.mjs.map +1 -0
  58. package/resources/audio/speech.d.mts +1 -1
  59. package/resources/audio/speech.d.mts.map +1 -1
  60. package/resources/audio/speech.d.ts +1 -1
  61. package/resources/audio/speech.d.ts.map +1 -1
  62. package/resources/audio/transcriptions.d.mts +2 -1
  63. package/resources/audio/transcriptions.d.mts.map +1 -1
  64. package/resources/audio/transcriptions.d.ts +2 -1
  65. package/resources/audio/transcriptions.d.ts.map +1 -1
  66. package/resources/audio/transcriptions.js.map +1 -1
  67. package/resources/audio/transcriptions.mjs.map +1 -1
  68. package/resources/beta/agents/agents.d.mts +12 -7
  69. package/resources/beta/agents/agents.d.mts.map +1 -1
  70. package/resources/beta/agents/agents.d.ts +12 -7
  71. package/resources/beta/agents/agents.d.ts.map +1 -1
  72. package/resources/beta/agents/agents.js.map +1 -1
  73. package/resources/beta/agents/agents.mjs.map +1 -1
  74. package/resources/beta/agents/environments/templates.d.mts +6 -6
  75. package/resources/beta/agents/environments/templates.d.ts +6 -6
  76. package/resources/beta/agents/sessions/sessions.d.mts +4 -2
  77. package/resources/beta/agents/sessions/sessions.d.mts.map +1 -1
  78. package/resources/beta/agents/sessions/sessions.d.ts +4 -2
  79. package/resources/beta/agents/sessions/sessions.d.ts.map +1 -1
  80. package/resources/beta/agents/sessions/sessions.js.map +1 -1
  81. package/resources/beta/agents/sessions/sessions.mjs.map +1 -1
  82. package/resources/beta/agents/vaults/credentials.d.mts +24 -7
  83. package/resources/beta/agents/vaults/credentials.d.mts.map +1 -1
  84. package/resources/beta/agents/vaults/credentials.d.ts +24 -7
  85. package/resources/beta/agents/vaults/credentials.d.ts.map +1 -1
  86. package/resources/beta/agents/vaults/credentials.js +2 -6
  87. package/resources/beta/agents/vaults/credentials.js.map +1 -1
  88. package/resources/beta/agents/vaults/credentials.mjs +2 -6
  89. package/resources/beta/agents/vaults/credentials.mjs.map +1 -1
  90. package/resources/beta/responses/responses.d.mts +69 -10
  91. package/resources/beta/responses/responses.d.mts.map +1 -1
  92. package/resources/beta/responses/responses.d.ts +69 -10
  93. package/resources/beta/responses/responses.d.ts.map +1 -1
  94. package/resources/beta/responses/responses.js.map +1 -1
  95. package/resources/beta/responses/responses.mjs.map +1 -1
  96. package/resources/files.d.mts +2 -2
  97. package/resources/files.d.mts.map +1 -1
  98. package/resources/files.d.ts +2 -2
  99. package/resources/files.d.ts.map +1 -1
  100. package/resources/index.d.mts +1 -1
  101. package/resources/index.d.mts.map +1 -1
  102. package/resources/index.d.ts +1 -1
  103. package/resources/index.d.ts.map +1 -1
  104. package/resources/live/forks/ws-base.js +1 -1
  105. package/resources/live/forks/ws-base.js.map +1 -1
  106. package/resources/live/forks/ws-base.mjs +1 -1
  107. package/resources/live/forks/ws-base.mjs.map +1 -1
  108. package/resources/live/sideband/ws-base.js +1 -1
  109. package/resources/live/sideband/ws-base.js.map +1 -1
  110. package/resources/live/sideband/ws-base.mjs +1 -1
  111. package/resources/live/sideband/ws-base.mjs.map +1 -1
  112. package/resources/live/ws-base.js +1 -1
  113. package/resources/live/ws-base.js.map +1 -1
  114. package/resources/live/ws-base.mjs +1 -1
  115. package/resources/live/ws-base.mjs.map +1 -1
  116. package/resources/realtime/index.d.mts +1 -0
  117. package/resources/realtime/index.d.mts.map +1 -1
  118. package/resources/realtime/index.d.ts +1 -0
  119. package/resources/realtime/index.d.ts.map +1 -1
  120. package/resources/realtime/index.js +3 -1
  121. package/resources/realtime/index.js.map +1 -1
  122. package/resources/realtime/index.mjs +1 -0
  123. package/resources/realtime/index.mjs.map +1 -1
  124. package/resources/realtime/realtime.d.mts +4 -0
  125. package/resources/realtime/realtime.d.mts.map +1 -1
  126. package/resources/realtime/realtime.d.ts +4 -0
  127. package/resources/realtime/realtime.d.ts.map +1 -1
  128. package/resources/realtime/realtime.js +4 -0
  129. package/resources/realtime/realtime.js.map +1 -1
  130. package/resources/realtime/realtime.mjs +4 -0
  131. package/resources/realtime/realtime.mjs.map +1 -1
  132. package/resources/realtime/translations/client-secrets.d.mts +67 -0
  133. package/resources/realtime/translations/client-secrets.d.mts.map +1 -0
  134. package/resources/realtime/translations/client-secrets.d.ts +67 -0
  135. package/resources/realtime/translations/client-secrets.d.ts.map +1 -0
  136. package/resources/realtime/translations/client-secrets.js +39 -0
  137. package/resources/realtime/translations/client-secrets.js.map +1 -0
  138. package/resources/realtime/translations/client-secrets.mjs +35 -0
  139. package/resources/realtime/translations/client-secrets.mjs.map +1 -0
  140. package/resources/realtime/translations/index.d.mts +3 -0
  141. package/resources/realtime/translations/index.d.mts.map +1 -0
  142. package/resources/realtime/translations/index.d.ts +3 -0
  143. package/resources/realtime/translations/index.d.ts.map +1 -0
  144. package/resources/realtime/translations/index.js +9 -0
  145. package/resources/realtime/translations/index.js.map +1 -0
  146. package/resources/realtime/translations/index.mjs +4 -0
  147. package/resources/realtime/translations/index.mjs.map +1 -0
  148. package/resources/realtime/translations/translations.d.mts +10 -0
  149. package/resources/realtime/translations/translations.d.mts.map +1 -0
  150. package/resources/realtime/translations/translations.d.ts +10 -0
  151. package/resources/realtime/translations/translations.d.ts.map +1 -0
  152. package/resources/realtime/translations/translations.js +17 -0
  153. package/resources/realtime/translations/translations.js.map +1 -0
  154. package/resources/realtime/translations/translations.mjs +12 -0
  155. package/resources/realtime/translations/translations.mjs.map +1 -0
  156. package/resources/realtime/translations.d.mts +2 -0
  157. package/resources/realtime/translations.d.mts.map +1 -0
  158. package/resources/realtime/translations.d.ts +2 -0
  159. package/resources/realtime/translations.d.ts.map +1 -0
  160. package/resources/realtime/translations.js +6 -0
  161. package/resources/realtime/translations.js.map +1 -0
  162. package/resources/realtime/translations.mjs +3 -0
  163. package/resources/realtime/translations.mjs.map +1 -0
  164. package/resources/responses/responses.d.mts +66 -7
  165. package/resources/responses/responses.d.mts.map +1 -1
  166. package/resources/responses/responses.d.ts +66 -7
  167. package/resources/responses/responses.d.ts.map +1 -1
  168. package/resources/responses/responses.js.map +1 -1
  169. package/resources/responses/responses.mjs.map +1 -1
  170. package/resources/shared.d.mts +1 -1
  171. package/resources/shared.d.mts.map +1 -1
  172. package/resources/shared.d.ts +1 -1
  173. package/resources/shared.d.ts.map +1 -1
  174. package/src/client.ts +42 -6
  175. package/src/internal/responses/canonical-output-text.ts +30 -5
  176. package/src/internal/responses/response-accumulator.ts +155 -56
  177. package/src/internal/ws.ts +5 -3
  178. package/src/lib/live/transcript-grouping.ts +6 -5
  179. package/src/lib/responses/responses-websocket-accumulator.ts +374 -0
  180. package/src/realtime/translations/ws.ts +539 -0
  181. package/src/resources/audio/speech.ts +3 -0
  182. package/src/resources/audio/transcriptions.ts +2 -1
  183. package/src/resources/beta/agents/agents.ts +13 -6
  184. package/src/resources/beta/agents/environments/templates.ts +6 -6
  185. package/src/resources/beta/agents/sessions/sessions.ts +4 -2
  186. package/src/resources/beta/agents/vaults/credentials.ts +21 -7
  187. package/src/resources/beta/responses/responses.ts +75 -6
  188. package/src/resources/files.ts +3 -3
  189. package/src/resources/index.ts +1 -1
  190. package/src/resources/live/forks/ws-base.ts +1 -1
  191. package/src/resources/live/sideband/ws-base.ts +1 -1
  192. package/src/resources/live/ws-base.ts +1 -1
  193. package/src/resources/realtime/api.md +8 -0
  194. package/src/resources/realtime/index.ts +1 -0
  195. package/src/resources/realtime/realtime.ts +6 -0
  196. package/src/resources/realtime/translations/client-secrets.ts +93 -0
  197. package/src/resources/realtime/translations/index.ts +4 -0
  198. package/src/resources/realtime/translations/translations.ts +15 -0
  199. package/src/resources/realtime/translations.ts +3 -0
  200. package/src/resources/responses/responses.ts +72 -6
  201. package/src/resources/shared.ts +1 -0
  202. package/src/version.ts +1 -1
  203. package/version.d.mts +1 -1
  204. package/version.d.ts +1 -1
  205. package/version.js +1 -1
  206. package/version.mjs +1 -1
@@ -0,0 +1,539 @@
1
+ import * as WS from 'ws';
2
+ import type { ClientOptions, OpenAI } from '../../client';
3
+ import { EventEmitter } from '../../lib/EventEmitter';
4
+ import { assertX509WebSocketSupported } from '../../internal/auth/x509-workload-identity-auth';
5
+ import { brand_privateBedrockClient } from '../../internal/bedrock';
6
+ import { isRunningInBrowser } from '../../internal/detect-platform';
7
+ import { resolveRealtimeAPIKey } from '../../internal/realtime-credentials';
8
+ import { snapshotWebSocketCredentials } from '../../internal/ws';
9
+ import { ReadyState } from '../../internal/ws-adapter';
10
+ import { NodeWebSocket } from '../../internal/ws-adapter-node';
11
+ import type {
12
+ RealtimeError,
13
+ RealtimeErrorEvent,
14
+ RealtimeTranslationClientEvent,
15
+ RealtimeTranslationServerEvent,
16
+ RealtimeTranslationSession,
17
+ } from '../../resources/realtime/realtime';
18
+ import { isAzure, OpenAIRealtimeError } from '../internal-base';
19
+
20
+ /** An event envelope, including event types added by the service in the future. */
21
+ export interface RealtimeTranslationEvent {
22
+ type: string;
23
+ // oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- Future wire events retain fields whose schema is not yet known to this SDK.
24
+ [key: string]: unknown;
25
+ }
26
+
27
+ /** Options for a Node.js translation session. */
28
+ export interface RealtimeTranslationConnectOptions {
29
+ /** Translation model used to create the session. */
30
+ model: string;
31
+ /** Node `ws` options. Redirects are always disabled. */
32
+ options?:
33
+ | (Omit<WS.ClientOptions, 'headers'> & {
34
+ /** Case-insensitive overrides; null removes a default and undefined preserves it. */
35
+ headers?: Record<string, string | null | undefined> | undefined;
36
+ })
37
+ | undefined;
38
+ }
39
+
40
+ type TranslationEvents = {
41
+ event: (event: RealtimeTranslationServerEvent | RealtimeTranslationEvent) => void;
42
+ error: (error: OpenAIRealtimeError) => void;
43
+ } & {
44
+ [Type in Exclude<RealtimeTranslationServerEvent['type'], 'error'>]: (
45
+ event: Extract<RealtimeTranslationServerEvent, { type: Type }>,
46
+ ) => void;
47
+ };
48
+
49
+ function parseEvent(data: string): RealtimeTranslationEvent {
50
+ let event: unknown;
51
+ try {
52
+ event = JSON.parse(data);
53
+ } catch {
54
+ throw new OpenAIRealtimeError('Could not parse translation WebSocket event as JSON.', null);
55
+ }
56
+ if (
57
+ typeof event !== 'object' ||
58
+ event === null ||
59
+ Array.isArray(event) ||
60
+ typeof Object.getOwnPropertyDescriptor(event, 'type')?.value !== 'string'
61
+ ) {
62
+ throw new OpenAIRealtimeError('Translation event must be an object with a string type.', null);
63
+ }
64
+ // SAFETY: The envelope was checked above; payload fields remain unknown until selected by event type.
65
+ return event as RealtimeTranslationEvent;
66
+ }
67
+
68
+ /** Copies a freshly parsed JSON tree's containers, sharing its immutable string payloads. */
69
+ function copyEvent(event: RealtimeTranslationEvent): RealtimeTranslationEvent {
70
+ const copy = { ...event };
71
+ const containers: object[] = [copy];
72
+ for (const container of containers) {
73
+ for (const [key, value] of Object.entries(container)) {
74
+ if (typeof value === 'object' && value !== null) {
75
+ const child = Array.isArray(value) ? [...value] : { ...value };
76
+ Object.defineProperty(container, key, {
77
+ value: child,
78
+ writable: true,
79
+ enumerable: true,
80
+ configurable: true,
81
+ });
82
+ containers.push(child);
83
+ }
84
+ }
85
+ }
86
+ return copy;
87
+ }
88
+
89
+ type RequiredFields<T> = {
90
+ [Key in keyof T as T[Key] extends Required<T>[Key] ? Key : never]-?: (value: unknown) => boolean;
91
+ };
92
+
93
+ function isObject(value: unknown): value is object {
94
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
95
+ }
96
+
97
+ function isString(value: unknown): value is string {
98
+ return typeof value === 'string';
99
+ }
100
+
101
+ function hasRequiredFields(value: unknown, fields: Record<string, (field: unknown) => boolean>): boolean {
102
+ return (
103
+ isObject(value) &&
104
+ Object.entries(fields).every(([key, check]) => check(Object.getOwnPropertyDescriptor(value, key)?.value))
105
+ );
106
+ }
107
+
108
+ function hasOptionalFields(value: unknown, fields: Record<string, (field: unknown) => boolean>): boolean {
109
+ return (
110
+ isObject(value) &&
111
+ Object.entries(fields).every(([key, check]) => {
112
+ const field = Object.getOwnPropertyDescriptor(value, key);
113
+ return field === undefined || check(field.value);
114
+ })
115
+ );
116
+ }
117
+
118
+ const inputAudioFields = {
119
+ transcription: (value: unknown) => value === null || hasRequiredFields(value, { model: isString }),
120
+ noise_reduction: (value: unknown) =>
121
+ value === null ||
122
+ hasRequiredFields(value, {
123
+ type: (kind: unknown) => kind === 'near_field' || kind === 'far_field',
124
+ }),
125
+ } satisfies RequiredFields<Required<RealtimeTranslationSession.Audio.Input>>;
126
+
127
+ const audioFields = {
128
+ input: (value: unknown) => hasOptionalFields(value, inputAudioFields),
129
+ output: (value: unknown) => hasOptionalFields(value, { language: isString }),
130
+ } satisfies RequiredFields<Required<RealtimeTranslationSession.Audio>>;
131
+
132
+ const sessionFields = {
133
+ id: isString,
134
+ model: isString,
135
+ expires_at: (value: unknown) => typeof value === 'number',
136
+ type: (value: unknown) => value === 'translation',
137
+ audio: (value: unknown) => hasOptionalFields(value, audioFields),
138
+ } satisfies RequiredFields<RealtimeTranslationSession>;
139
+
140
+ function isOptionalNullableString(value: unknown): boolean {
141
+ return value === undefined || value === null || isString(value);
142
+ }
143
+
144
+ function isOptionalNumber(value: unknown): boolean {
145
+ return value === undefined || typeof value === 'number';
146
+ }
147
+
148
+ const errorFields = {
149
+ message: isString,
150
+ type: isString,
151
+ code: isOptionalNullableString,
152
+ event_id: isOptionalNullableString,
153
+ param: isOptionalNullableString,
154
+ } satisfies RequiredFields<Required<RealtimeError>>;
155
+ const commonFields = { type: isString, event_id: isString };
156
+ const deltaFields = {
157
+ ...commonFields,
158
+ delta: isString,
159
+ elapsed_ms: (value: unknown) => value === null || isOptionalNumber(value),
160
+ };
161
+ const sessionEventFields = {
162
+ ...commonFields,
163
+ session: (value: unknown) => hasRequiredFields(value, sessionFields),
164
+ };
165
+
166
+ // Regeneration that adds an event or field must also update its dispatcher.
167
+ const translationEventFields = {
168
+ 'session.closed': commonFields,
169
+ 'session.input_transcript.delta': deltaFields,
170
+ 'session.output_transcript.delta': deltaFields,
171
+ 'session.output_audio.delta': {
172
+ ...deltaFields,
173
+ channels: isOptionalNumber,
174
+ format: (value: unknown) => value === undefined || value === 'pcm16',
175
+ sample_rate: isOptionalNumber,
176
+ },
177
+ 'session.created': sessionEventFields,
178
+ 'session.updated': sessionEventFields,
179
+ error: { ...commonFields, error: (value: unknown) => hasRequiredFields(value, errorFields) },
180
+ } satisfies {
181
+ [Event in RealtimeTranslationServerEvent as Event['type']]: RequiredFields<Required<Event>>;
182
+ };
183
+
184
+ /** Check the required fields before exposing an envelope through a typed listener. */
185
+ function isCompleteTranslationEvent(event: RealtimeTranslationEvent): boolean {
186
+ // SAFETY: Only the above schema-checked map's own data properties can supply a validator.
187
+ const fields = Object.getOwnPropertyDescriptor(translationEventFields, event.type)?.value as
188
+ | Record<string, (field: unknown) => boolean>
189
+ | undefined;
190
+ return fields !== undefined && hasRequiredFields(event, fields);
191
+ }
192
+
193
+ function buildTranslationURL(client: OpenAI, model: string): URL {
194
+ if (typeof model !== 'string' || !model) {
195
+ throw new Error('A translation model is required.');
196
+ }
197
+ const endpoint = new URL(client.baseURL);
198
+ endpoint.pathname = `${endpoint.pathname.replace(/\/$/u, '')}/realtime/translations`;
199
+ const url = new URL(client.buildURL(endpoint.toString(), { model }));
200
+ if (url.protocol !== 'https:') {
201
+ throw new Error('The translation endpoint must use HTTPS.');
202
+ }
203
+ if (url.searchParams.has('intent')) {
204
+ throw new Error('Realtime translation does not accept an intent query parameter.');
205
+ }
206
+ url.hash = '';
207
+ url.protocol = 'wss:';
208
+ return url;
209
+ }
210
+
211
+ /**
212
+ * Node.js translation WebSocket. Install the optional `ws` peer dependency.
213
+ * Wait for `socket` to open before sending. Events are never buffered or replayed.
214
+ * Compression is off by default; set `options.perMessageDeflate` to opt in.
215
+ */
216
+ // oxlint-disable-next-line unicorn/prefer-event-target -- Reuse the SDK typed emitter and its public on/off event contract.
217
+ export class OpenAIRealtimeTranslationWS extends EventEmitter<TranslationEvents> {
218
+ /** Adapter exposing lifecycle events and the underlying Node socket as `platformSocket`. */
219
+ readonly socket: NodeWebSocket;
220
+ /** The configured endpoint and model for this connection. */
221
+ readonly url: URL;
222
+ /** Sends the protocol close once; use `finish` to await trailing output. */
223
+ readonly session = { close: (): void => this.send({ type: 'session.close' }) };
224
+
225
+ private _inputClosed = false;
226
+ private _terminalReceived = false;
227
+ private _terminalDelivered = false;
228
+ private _transportClosed = false;
229
+ private _failure: OpenAIRealtimeError | undefined;
230
+ private _finishPromise: Promise<void> | undefined;
231
+ private _resolveFinish: (() => void) | undefined;
232
+ private _rejectFinish: ((error: OpenAIRealtimeError) => void) | undefined;
233
+ private _finishTimer: ReturnType<typeof setTimeout> | undefined;
234
+ private _closeTimer: ReturnType<typeof setTimeout> | undefined;
235
+ private _signal: AbortSignal | undefined;
236
+
237
+ private constructor(url: URL, options: WS.ClientOptions) {
238
+ super();
239
+ this.url = url;
240
+ this.socket = new NodeWebSocket(new WS.WebSocket(url, options));
241
+ this.socket.on('message', this._onMessage);
242
+ this.socket.on('error', this._onError);
243
+ this.socket.on('close', this._onClose);
244
+ }
245
+
246
+ /** Resolves the API key and starts connecting; resolves before the socket opens. */
247
+ static async create(
248
+ client: OpenAI,
249
+ props: RealtimeTranslationConnectOptions,
250
+ ): Promise<OpenAIRealtimeTranslationWS> {
251
+ // SAFETY: Probe optional host capabilities without requiring Node ambient types in published source.
252
+ const scope = globalThis as { process?: { versions?: { node?: string } } };
253
+ if (isRunningInBrowser() || !scope.process?.versions?.node) {
254
+ throw new Error('Realtime translation WebSockets require Node.js.');
255
+ }
256
+ assertX509WebSocketSupported(client);
257
+ // SAFETY: OpenAI owns these options; this read only rejects unsupported authentication modes.
258
+ const clientOptions: ClientOptions = client['_options'];
259
+ if (
260
+ isAzure(client) ||
261
+ brand_privateBedrockClient in client ||
262
+ clientOptions.provider ||
263
+ clientOptions.workloadIdentity
264
+ ) {
265
+ throw new Error('Realtime translation WebSockets require an ordinary OpenAI API-key client.');
266
+ }
267
+ const url = buildTranslationURL(client, props.model);
268
+ const { apiKey } = await resolveRealtimeAPIKey(client);
269
+ if (!apiKey) {
270
+ throw new Error('Realtime translation WebSockets require an API key.');
271
+ }
272
+ const headers = new Map(
273
+ Object.entries(client._buildWebSocketHeaders({ Authorization: `Bearer ${apiKey}` })),
274
+ );
275
+ for (const [name, value] of Object.entries(props.options?.headers ?? {})) {
276
+ if (value === null) {
277
+ headers.delete(name.toLowerCase());
278
+ } else if (value !== undefined) {
279
+ headers.set(name.toLowerCase(), value);
280
+ }
281
+ }
282
+ const options = {
283
+ ...props.options,
284
+ maxPayload: props.options?.maxPayload ?? 0,
285
+ perMessageDeflate: props.options?.perMessageDeflate ?? false,
286
+ headers: Object.fromEntries(headers),
287
+ followRedirects: false,
288
+ };
289
+ snapshotWebSocketCredentials(options);
290
+ return new OpenAIRealtimeTranslationWS(url, options);
291
+ }
292
+
293
+ /** Sends a typed event, future event envelope, or raw JSON envelope after the socket opens. */
294
+ send(event: RealtimeTranslationClientEvent | RealtimeTranslationEvent | string): void {
295
+ const data = typeof event === 'string' ? event : JSON.stringify(event);
296
+ const envelope = parseEvent(data);
297
+ if (envelope.type === 'session.close') {
298
+ if (this._inputClosed) {
299
+ return;
300
+ }
301
+ this._inputClosed = true;
302
+ } else if (this._inputClosed) {
303
+ throw new OpenAIRealtimeError('Translation input is closed.', null);
304
+ }
305
+ try {
306
+ if (this.socket.readyState !== ReadyState.OPEN) {
307
+ throw new Error('The translation WebSocket is not open.');
308
+ }
309
+ this.socket.send(data);
310
+ } catch {
311
+ const error = new OpenAIRealtimeError('Could not send translation WebSocket event.', null);
312
+ this._fail(error);
313
+ throw error;
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Stops input, sends `session.close` once, and delivers all events through `session.closed`.
319
+ * The first call owns the finite deadline and optional cancellation signal. Repeated calls
320
+ * share its result. API error events remain observable and do not end the drain.
321
+ * A timeout, abort, or transport failure rejects; no connection or input is replayed.
322
+ * Resolves after terminal delivery and transport closure. The deadline includes transport
323
+ * cleanup; a stalled close handshake is terminated and rejects the operation.
324
+ */
325
+ finish({ timeoutMs, signal }: { timeoutMs: number; signal?: AbortSignal | undefined }): Promise<void> {
326
+ if (this._finishPromise) {
327
+ return this._finishPromise;
328
+ }
329
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {
330
+ return Promise.reject(
331
+ new Error('timeoutMs must be a positive finite WebSocket deadline of at most 2147483647.'),
332
+ );
333
+ }
334
+ // oxlint-disable-next-line promise/avoid-new -- The existing socket dispatcher settles this completion; there is no second event reader.
335
+ this._finishPromise = new Promise<void>((resolve, reject) => {
336
+ this._resolveFinish = resolve;
337
+ this._rejectFinish = reject;
338
+ });
339
+ if (this._transportClosed) {
340
+ this._settleFinish();
341
+ return this._finishPromise;
342
+ }
343
+ clearTimeout(this._closeTimer);
344
+ this._closeTimer = undefined;
345
+ this._signal = signal;
346
+ this._finishTimer = setTimeout(() => {
347
+ this._fail(
348
+ new OpenAIRealtimeError(
349
+ 'Timed out finishing the translation session and closing its transport.',
350
+ null,
351
+ ),
352
+ );
353
+ }, timeoutMs);
354
+ signal?.addEventListener('abort', this._onAbort, { once: true });
355
+ if (signal?.aborted) {
356
+ this._onAbort();
357
+ } else if (!this._terminalReceived && !this._failure) {
358
+ try {
359
+ this.session.close();
360
+ } catch {
361
+ // send already records the failure and terminates the connection.
362
+ }
363
+ }
364
+ this._settleFinish();
365
+ return this._finishPromise;
366
+ }
367
+
368
+ /** Closes the transport without waiting for terminal output. Prefer `finish` for a complete session. */
369
+ close(): void {
370
+ this._inputClosed = true;
371
+ this._closeTransport();
372
+ }
373
+
374
+ private _onMessage = (data: string | Buffer): void => {
375
+ if (this._terminalReceived) {
376
+ return;
377
+ }
378
+ const wireData = data.toString();
379
+ let event: RealtimeTranslationEvent;
380
+ try {
381
+ event = parseEvent(wireData);
382
+ } catch (error) {
383
+ // SAFETY: parseEvent only throws normalized, payload-free OpenAIRealtimeError instances.
384
+ this._reportError(error as OpenAIRealtimeError);
385
+ return;
386
+ }
387
+ const { type } = event;
388
+ const typed = isCompleteTranslationEvent(event);
389
+ const terminal = type === 'session.closed' && typed;
390
+ if (terminal) {
391
+ this._inputClosed = true;
392
+ this._terminalReceived = true;
393
+ }
394
+ try {
395
+ try {
396
+ // Raw listeners may mutate their event. Keep typed dispatch and finish
397
+ // anchored to the original validated wire event, including nested data.
398
+ if (this._hasListener('event')) {
399
+ this._emit('event', typed ? copyEvent(event) : event);
400
+ }
401
+ } finally {
402
+ if (type === 'error' && typed) {
403
+ // SAFETY: The error envelope is preserved as server data, as for other Realtime events.
404
+ // oxlint-disable-next-line anti-slop/no-chained-type-assertions -- Forward server error fields unchanged through the existing Realtime error wrapper.
405
+ const apiErrorEvent = event as unknown as RealtimeErrorEvent;
406
+ const error = new OpenAIRealtimeError(
407
+ `Translation API error: ${apiErrorEvent.error.message}`,
408
+ apiErrorEvent,
409
+ );
410
+ this._reportError(error);
411
+ } else if (type !== 'error' && typed) {
412
+ // SAFETY: The wire discriminator selects its listener; future event names remain visible on `event`.
413
+ this._emit(type as Exclude<keyof TranslationEvents, 'event' | 'error'>, event as never);
414
+ }
415
+ }
416
+ } finally {
417
+ if (terminal) {
418
+ this._terminalDelivered = true;
419
+ try {
420
+ this._closeTransport();
421
+ } finally {
422
+ this._settleFinish();
423
+ }
424
+ }
425
+ }
426
+ };
427
+
428
+ private _onError = (cause: Error): void => {
429
+ // Closing before open also emits a ws error. The caller already requested
430
+ // cleanup; _onClose still records an incomplete drain for a later finish().
431
+ if (this._closeTimer !== undefined && !this._terminalReceived && !this._failure) {
432
+ return;
433
+ }
434
+ const error = new OpenAIRealtimeError('Translation WebSocket transport failed.', null);
435
+ Object.defineProperty(error, 'cause', { value: cause, writable: true, configurable: true });
436
+ this._fail(error);
437
+ // finish already rejects transport failures; don't report that same failure a second time.
438
+ if (this._hasListener('error') || !this._finishPromise) {
439
+ this._reportError(error);
440
+ }
441
+ };
442
+
443
+ private _reportError(error: OpenAIRealtimeError): void {
444
+ if (this._hasListener('error')) {
445
+ this._emit('error', error);
446
+ } else {
447
+ error.message += " Bind an error listener, e.g. connection.on('error', (error) => ...).";
448
+ // oxlint-disable-next-line promise/no-promise-in-callback -- Match Realtime's explicit unhandled rejection contract when no SDK listener or completion operation observes the error.
449
+ Promise.reject(error);
450
+ }
451
+ }
452
+
453
+ private _onClose = (code: number): void => {
454
+ const reportPrematureClose =
455
+ !this._terminalReceived && !this._failure && !this._finishPromise && this._closeTimer === undefined;
456
+ this._inputClosed = true;
457
+ this._transportClosed = true;
458
+ clearTimeout(this._closeTimer);
459
+ this.socket.off('message', this._onMessage);
460
+ this.socket.off('error', this._onError);
461
+ this.socket.off('close', this._onClose);
462
+ if (!this._terminalReceived) {
463
+ this._failure ??= new OpenAIRealtimeError('Translation WebSocket closed before session.closed.', null);
464
+ } else if (code !== 1000 && code !== 1001 && code !== 1005) {
465
+ this._failure ??= new OpenAIRealtimeError(
466
+ 'Translation transport closed abnormally after session.closed.',
467
+ null,
468
+ );
469
+ }
470
+ this._settleFinish();
471
+ if (reportPrematureClose && this._failure) {
472
+ this._reportError(this._failure);
473
+ }
474
+ };
475
+
476
+ private _onAbort = (): void => {
477
+ const error = new OpenAIRealtimeError('Translation finish was aborted.', null);
478
+ Object.defineProperty(error, 'cause', {
479
+ value: this._signal?.reason,
480
+ writable: true,
481
+ configurable: true,
482
+ });
483
+ this._fail(error);
484
+ };
485
+
486
+ private _fail(error: OpenAIRealtimeError): void {
487
+ this._inputClosed = true;
488
+ this._failure ??= error;
489
+ this._settleFinish();
490
+ if (this.socket.readyState !== ReadyState.CLOSED) {
491
+ this.socket.platformSocket.terminate();
492
+ }
493
+ }
494
+
495
+ private _settleFinish(): void {
496
+ if (!this._transportClosed || (!this._failure && !this._terminalDelivered)) {
497
+ return;
498
+ }
499
+ clearTimeout(this._finishTimer);
500
+ this._signal?.removeEventListener('abort', this._onAbort);
501
+ this._signal = undefined;
502
+ if (this._failure) {
503
+ this._rejectFinish?.(this._failure);
504
+ } else {
505
+ this._resolveFinish?.();
506
+ }
507
+ this._resolveFinish = undefined;
508
+ this._rejectFinish = undefined;
509
+ }
510
+
511
+ private _closeTransport(): void {
512
+ if (this.socket.readyState === ReadyState.CLOSED || this.socket.readyState === ReadyState.CLOSING) {
513
+ return;
514
+ }
515
+ if (!this._finishPromise) {
516
+ this._closeTimer = setTimeout(() => {
517
+ const error = new OpenAIRealtimeError('Timed out closing the translation transport.', null);
518
+ this._fail(error);
519
+ this._reportError(error);
520
+ }, 1000);
521
+ const timer: unknown = this._closeTimer;
522
+ if (
523
+ typeof timer === 'object' &&
524
+ timer !== null &&
525
+ 'unref' in timer &&
526
+ typeof timer.unref === 'function'
527
+ ) {
528
+ timer.unref();
529
+ }
530
+ }
531
+ try {
532
+ this.socket.close(1000, 'OK');
533
+ } catch {
534
+ const error = new OpenAIRealtimeError('Could not close the translation transport.', null);
535
+ this._fail(error);
536
+ this._emit('error', error);
537
+ }
538
+ }
539
+ }
@@ -82,6 +82,9 @@ export interface SpeechCreateParams {
82
82
  | 'verse'
83
83
  | 'marin'
84
84
  | 'cedar'
85
+ | 'fable'
86
+ | 'onyx'
87
+ | 'nova'
85
88
  | SpeechCreateParams.ID;
86
89
 
87
90
  /**
@@ -24,7 +24,8 @@ export class Transcriptions extends APIResource {
24
24
  * Transcribes audio into the input language.
25
25
  *
26
26
  * Returns a transcription object in `json`, `diarized_json`, or `verbose_json`
27
- * format, or a stream of transcript events.
27
+ * format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript
28
+ * events. Supported formats depend on the model.
28
29
  *
29
30
  * @example
30
31
  * ```ts
@@ -422,7 +422,7 @@ export interface Agent {
422
422
  /**
423
423
  * The resolved service-tier policy used for model requests.
424
424
  */
425
- service_tier: 'auto' | 'default' | 'flex' | 'priority' | 'fast';
425
+ service_tier: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | 'ultrafast';
426
426
 
427
427
  /**
428
428
  * The resolved configuration for text generated by the agent.
@@ -1085,7 +1085,7 @@ export namespace AgentSession {
1085
1085
  /**
1086
1086
  * The effective service-tier policy for model requests. Defaults to `auto`.
1087
1087
  */
1088
- service_tier: 'auto' | 'default' | 'flex' | 'priority' | 'fast';
1088
+ service_tier: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | 'ultrafast';
1089
1089
 
1090
1090
  /**
1091
1091
  * Configuration for text generated by the agent.
@@ -3188,7 +3188,7 @@ export namespace EnvironmentParam {
3188
3188
 
3189
3189
  /**
3190
3190
  * Network access policy for the environment. Defaults to disabled for GA requests
3191
- * and enabled for alpha/beta requests.
3191
+ * and enabled for beta requests.
3192
3192
  */
3193
3193
  network?: EnvironmentParamOpenAIHosted.Network | null;
3194
3194
 
@@ -3217,7 +3217,7 @@ export namespace EnvironmentParam {
3217
3217
  export namespace EnvironmentParamOpenAIHosted {
3218
3218
  /**
3219
3219
  * Network access policy for the environment. Defaults to disabled for GA requests
3220
- * and enabled for alpha/beta requests.
3220
+ * and enabled for beta requests.
3221
3221
  */
3222
3222
  export interface Network {
3223
3223
  /**
@@ -4317,8 +4317,11 @@ export interface SessionTurnError {
4317
4317
  * billing limit.
4318
4318
  * - `credit_balance_exhausted` - The organization has no API credits remaining.
4319
4319
  * - `rate_limit_exceeded` - The request exceeds the available rate limit.
4320
+ * - `flex_unavailable` - Flex processing is temporarily unavailable.
4320
4321
  * - `server_overloaded` - The model service is temporarily overloaded.
4321
4322
  * - `cyber_policy` - The request was rejected by a safety policy.
4323
+ * - `misalignment_policy_violation` - The request was blocked by the safety
4324
+ * systems.
4322
4325
  * - `connection_failed` - The request could not connect to the model service.
4323
4326
  * - `server_error` - The model service encountered an unexpected error.
4324
4327
  * - `authentication_error` - The API credentials are invalid or lack the required
@@ -4340,8 +4343,10 @@ export interface SessionTurnError {
4340
4343
  | 'usage_limit_exceeded'
4341
4344
  | 'credit_balance_exhausted'
4342
4345
  | 'rate_limit_exceeded'
4346
+ | 'flex_unavailable'
4343
4347
  | 'server_overloaded'
4344
4348
  | 'cyber_policy'
4349
+ | 'misalignment_policy_violation'
4345
4350
  | 'connection_failed'
4346
4351
  | 'server_error'
4347
4352
  | 'authentication_error'
@@ -4679,8 +4684,9 @@ export interface AgentCreateParams {
4679
4684
  * - `flex` - Uses the flex service tier.
4680
4685
  * - `priority` - Uses the priority service tier.
4681
4686
  * - `fast` - Uses the fast service tier.
4687
+ * - `ultrafast` - Uses the ultrafast service tier.
4682
4688
  */
4683
- service_tier?: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | null;
4689
+ service_tier?: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | 'ultrafast' | null;
4684
4690
 
4685
4691
  /**
4686
4692
  * Configuration for generated text. Defaults to the `text` format and medium
@@ -4737,8 +4743,9 @@ export interface AgentUpdateParams {
4737
4743
  * - `flex` - Uses the flex service tier.
4738
4744
  * - `priority` - Uses the priority service tier.
4739
4745
  * - `fast` - Uses the fast service tier.
4746
+ * - `ultrafast` - Uses the ultrafast service tier.
4740
4747
  */
4741
- service_tier?: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | null;
4748
+ service_tier?: 'auto' | 'default' | 'flex' | 'priority' | 'fast' | 'ultrafast' | null;
4742
4749
 
4743
4750
  /**
4744
4751
  * Configuration for text generated by the agent.
@@ -581,7 +581,7 @@ export interface TemplateCreateParams {
581
581
 
582
582
  /**
583
583
  * Network access policy for the environment. Defaults to disabled for GA requests
584
- * and enabled for alpha/beta requests.
584
+ * and enabled for beta requests.
585
585
  */
586
586
  network?: TemplateCreateParams.Network | null;
587
587
 
@@ -610,7 +610,7 @@ export interface TemplateCreateParams {
610
610
  export namespace TemplateCreateParams {
611
611
  /**
612
612
  * Network access policy for the environment. Defaults to disabled for GA requests
613
- * and enabled for alpha/beta requests.
613
+ * and enabled for beta requests.
614
614
  */
615
615
  export interface Network {
616
616
  /**
@@ -672,8 +672,8 @@ export interface TemplateUpdateParams {
672
672
 
673
673
  /**
674
674
  * Network access available after setup completes. Omit to preserve the current
675
- * policy, or pass `null` to reset to disabled for GA requests or enabled for
676
- * alpha/beta requests.
675
+ * policy, or pass `null` to reset to disabled for GA requests or enabled for beta
676
+ * requests.
677
677
  */
678
678
  network?: TemplateUpdateParams.Network | null;
679
679
 
@@ -701,8 +701,8 @@ export interface TemplateUpdateParams {
701
701
  export namespace TemplateUpdateParams {
702
702
  /**
703
703
  * Network access available after setup completes. Omit to preserve the current
704
- * policy, or pass `null` to reset to disabled for GA requests or enabled for
705
- * alpha/beta requests.
704
+ * policy, or pass `null` to reset to disabled for GA requests or enabled for beta
705
+ * requests.
706
706
  */
707
707
  export interface Network {
708
708
  /**