openai 7.15.0 → 7.16.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.
- package/CHANGELOG.md +34 -0
- package/README.md +6 -0
- package/bedrock.d.mts.map +1 -1
- package/bedrock.d.ts.map +1 -1
- package/bedrock.js +9 -6
- package/bedrock.js.map +1 -1
- package/bedrock.mjs +9 -6
- package/bedrock.mjs.map +1 -1
- package/client.d.mts +13 -3
- package/client.d.mts.map +1 -1
- package/client.d.ts +13 -3
- package/client.d.ts.map +1 -1
- package/client.js +37 -20
- package/client.js.map +1 -1
- package/client.mjs +37 -20
- package/client.mjs.map +1 -1
- package/internal/realtime-credentials.d.mts +1 -1
- package/internal/realtime-credentials.d.ts +1 -1
- package/internal/realtime-credentials.js +1 -1
- package/internal/realtime-credentials.mjs +1 -1
- package/internal/utils/abort.d.mts +11 -0
- package/internal/utils/abort.d.mts.map +1 -0
- package/internal/utils/abort.d.ts +11 -0
- package/internal/utils/abort.d.ts.map +1 -0
- package/internal/utils/abort.js +87 -0
- package/internal/utils/abort.js.map +1 -0
- package/internal/utils/abort.mjs +83 -0
- package/internal/utils/abort.mjs.map +1 -0
- package/internal/ws.d.mts +13 -0
- package/internal/ws.d.mts.map +1 -1
- package/internal/ws.d.ts +13 -0
- package/internal/ws.d.ts.map +1 -1
- package/internal/ws.js +10 -0
- package/internal/ws.js.map +1 -1
- package/internal/ws.mjs +9 -0
- package/internal/ws.mjs.map +1 -1
- package/lib/live/transcript-grouping.d.mts +2 -2
- package/lib/live/transcript-grouping.d.mts.map +1 -1
- package/lib/live/transcript-grouping.d.ts +2 -2
- package/lib/live/transcript-grouping.d.ts.map +1 -1
- package/lib/live/transcript-grouping.js +44 -22
- package/lib/live/transcript-grouping.js.map +1 -1
- package/lib/live/transcript-grouping.mjs +44 -22
- package/lib/live/transcript-grouping.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/beta/responses/internal-base.d.mts.map +1 -1
- package/resources/beta/responses/internal-base.d.ts.map +1 -1
- package/resources/beta/responses/internal-base.js +6 -0
- package/resources/beta/responses/internal-base.js.map +1 -1
- package/resources/beta/responses/internal-base.mjs +6 -0
- package/resources/beta/responses/internal-base.mjs.map +1 -1
- package/resources/beta/responses/ws-base.d.mts +13 -8
- package/resources/beta/responses/ws-base.d.mts.map +1 -1
- package/resources/beta/responses/ws-base.d.ts +13 -8
- package/resources/beta/responses/ws-base.d.ts.map +1 -1
- package/resources/beta/responses/ws-base.js +85 -14
- package/resources/beta/responses/ws-base.js.map +1 -1
- package/resources/beta/responses/ws-base.mjs +87 -16
- package/resources/beta/responses/ws-base.mjs.map +1 -1
- package/resources/beta/responses/ws.d.mts +1 -0
- package/resources/beta/responses/ws.d.mts.map +1 -1
- package/resources/beta/responses/ws.d.ts +1 -0
- package/resources/beta/responses/ws.d.ts.map +1 -1
- package/resources/beta/responses/ws.js.map +1 -1
- package/resources/beta/responses/ws.mjs.map +1 -1
- package/resources/live/forks/ws-base.d.mts +13 -8
- package/resources/live/forks/ws-base.d.mts.map +1 -1
- package/resources/live/forks/ws-base.d.ts +13 -8
- package/resources/live/forks/ws-base.d.ts.map +1 -1
- package/resources/live/forks/ws-base.js +85 -14
- package/resources/live/forks/ws-base.js.map +1 -1
- package/resources/live/forks/ws-base.mjs +87 -16
- package/resources/live/forks/ws-base.mjs.map +1 -1
- package/resources/live/forks/ws.d.mts +1 -0
- package/resources/live/forks/ws.d.mts.map +1 -1
- package/resources/live/forks/ws.d.ts +1 -0
- package/resources/live/forks/ws.d.ts.map +1 -1
- package/resources/live/forks/ws.js.map +1 -1
- package/resources/live/forks/ws.mjs.map +1 -1
- package/resources/live/sideband/ws-base.d.mts +13 -8
- package/resources/live/sideband/ws-base.d.mts.map +1 -1
- package/resources/live/sideband/ws-base.d.ts +13 -8
- package/resources/live/sideband/ws-base.d.ts.map +1 -1
- package/resources/live/sideband/ws-base.js +85 -14
- package/resources/live/sideband/ws-base.js.map +1 -1
- package/resources/live/sideband/ws-base.mjs +87 -16
- package/resources/live/sideband/ws-base.mjs.map +1 -1
- package/resources/live/sideband/ws.d.mts +1 -0
- package/resources/live/sideband/ws.d.mts.map +1 -1
- package/resources/live/sideband/ws.d.ts +1 -0
- package/resources/live/sideband/ws.d.ts.map +1 -1
- package/resources/live/sideband/ws.js.map +1 -1
- package/resources/live/sideband/ws.mjs.map +1 -1
- package/resources/live/ws-base.d.mts +13 -8
- package/resources/live/ws-base.d.mts.map +1 -1
- package/resources/live/ws-base.d.ts +13 -8
- package/resources/live/ws-base.d.ts.map +1 -1
- package/resources/live/ws-base.js +85 -14
- package/resources/live/ws-base.js.map +1 -1
- package/resources/live/ws-base.mjs +87 -16
- package/resources/live/ws-base.mjs.map +1 -1
- package/resources/live/ws.d.mts +1 -0
- package/resources/live/ws.d.mts.map +1 -1
- package/resources/live/ws.d.ts +1 -0
- package/resources/live/ws.d.ts.map +1 -1
- package/resources/live/ws.js.map +1 -1
- package/resources/live/ws.mjs.map +1 -1
- package/resources/responses/internal-base.d.mts.map +1 -1
- package/resources/responses/internal-base.d.ts.map +1 -1
- package/resources/responses/internal-base.js +6 -0
- package/resources/responses/internal-base.js.map +1 -1
- package/resources/responses/internal-base.mjs +6 -0
- package/resources/responses/internal-base.mjs.map +1 -1
- package/resources/responses/ws-base.d.mts +13 -8
- package/resources/responses/ws-base.d.mts.map +1 -1
- package/resources/responses/ws-base.d.ts +13 -8
- package/resources/responses/ws-base.d.ts.map +1 -1
- package/resources/responses/ws-base.js +85 -14
- package/resources/responses/ws-base.js.map +1 -1
- package/resources/responses/ws-base.mjs +87 -16
- package/resources/responses/ws-base.mjs.map +1 -1
- package/resources/responses/ws.d.mts +1 -0
- package/resources/responses/ws.d.mts.map +1 -1
- package/resources/responses/ws.d.ts +1 -0
- package/resources/responses/ws.d.ts.map +1 -1
- package/resources/responses/ws.js.map +1 -1
- package/resources/responses/ws.mjs.map +1 -1
- package/src/bedrock.ts +18 -7
- package/src/client.ts +43 -19
- package/src/internal/realtime-credentials.ts +1 -1
- package/src/internal/utils/abort.ts +111 -0
- package/src/internal/ws.ts +22 -0
- package/src/lib/live/transcript-grouping.ts +59 -27
- package/src/resources/beta/responses/internal-base.ts +5 -0
- package/src/resources/beta/responses/ws-base.ts +110 -21
- package/src/resources/beta/responses/ws.ts +2 -0
- package/src/resources/live/forks/ws-base.ts +110 -21
- package/src/resources/live/forks/ws.ts +2 -0
- package/src/resources/live/sideband/ws-base.ts +110 -21
- package/src/resources/live/sideband/ws.ts +2 -0
- package/src/resources/live/ws-base.ts +110 -21
- package/src/resources/live/ws.ts +2 -0
- package/src/resources/responses/internal-base.ts +5 -0
- package/src/resources/responses/ws-base.ts +110 -21
- package/src/resources/responses/ws.ts +2 -0
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -6,6 +6,8 @@ import { sleep } from '../../../internal/utils/sleep';
|
|
|
6
6
|
import { type WebSocketLike, ReadyState } from '../../../internal/ws-adapter';
|
|
7
7
|
import {
|
|
8
8
|
SendQueue,
|
|
9
|
+
getMaxBufferedEvents,
|
|
10
|
+
type WebSocketStreamOptions,
|
|
9
11
|
flattenRawData,
|
|
10
12
|
isRecoverableClose,
|
|
11
13
|
type RawWebSocketData,
|
|
@@ -51,11 +53,11 @@ export interface ResponsesWSBaseOptions {
|
|
|
51
53
|
reconnect?: ResponsesWSReconnectOptions | null | undefined;
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* Default: 1
|
|
56
|
+
* Byte budget for outgoing messages queued while the socket is connecting
|
|
57
|
+
* or reconnecting. An empty queue accepts one message even if it exceeds
|
|
58
|
+
* this budget. Further messages are discarded and an `error` event is emitted
|
|
59
|
+
* if the total queued size would exceed the budget.
|
|
60
|
+
* Default: 1 MiB (1,048,576 bytes).
|
|
59
61
|
*/
|
|
60
62
|
maxQueueSize?: number | undefined;
|
|
61
63
|
}
|
|
@@ -169,6 +171,11 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
169
171
|
* The iterator will exit if the socket closes but exiting the iterator
|
|
170
172
|
* does not close the socket.
|
|
171
173
|
*
|
|
174
|
+
* Pass `maxBufferedEvents` to limit queued records for this iterator, including
|
|
175
|
+
* lifecycle events. Overflow discards its backlog and rejects `next()` with a
|
|
176
|
+
* WebSocketError; the shared socket and other iterators remain active.
|
|
177
|
+
* Omitted means unlimited. This is an event-count limit, not a byte limit.
|
|
178
|
+
*
|
|
172
179
|
* @example
|
|
173
180
|
* ```ts
|
|
174
181
|
* for await (const event of client.stream()) {
|
|
@@ -186,26 +193,59 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
186
193
|
* }
|
|
187
194
|
* ```
|
|
188
195
|
*/
|
|
189
|
-
stream(): AsyncIterableIterator<ResponsesStreamMessage> {
|
|
190
|
-
return this[Symbol.asyncIterator]();
|
|
196
|
+
stream(options?: WebSocketStreamOptions): AsyncIterableIterator<ResponsesStreamMessage> {
|
|
197
|
+
return options === undefined ? this[Symbol.asyncIterator]() : this[Symbol.asyncIterator](options);
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<ResponsesStreamMessage> {
|
|
200
|
+
[Symbol.asyncIterator](options?: WebSocketStreamOptions): AsyncIterableIterator<ResponsesStreamMessage> {
|
|
194
201
|
if (!this.socket) {
|
|
195
202
|
throw new OpenAIError('Internal error: failed to initialize socket. Please report this issue.');
|
|
196
203
|
}
|
|
197
204
|
|
|
205
|
+
const maxBufferedEvents = getMaxBufferedEvents(options);
|
|
206
|
+
|
|
198
207
|
// Two-queue async iterator: `queue` buffers incoming messages,
|
|
199
208
|
// `resolvers` buffers waiting next() calls. A push wakes the
|
|
200
209
|
// oldest next(); a next() drains the oldest message.
|
|
201
|
-
const queue: ResponsesStreamMessage[] = [];
|
|
202
|
-
const resolvers: (() => void)[] = [];
|
|
210
|
+
const queue: (ResponsesStreamMessage | undefined)[] = [];
|
|
211
|
+
const resolvers: ((() => void) | undefined)[] = [];
|
|
212
|
+
let queueHead = 0;
|
|
213
|
+
let resolverHead = 0;
|
|
203
214
|
let done = false;
|
|
215
|
+
let failure: WebSocketError | undefined;
|
|
204
216
|
let currentSocket = this.socket;
|
|
205
217
|
|
|
218
|
+
const wakeResolver = () => {
|
|
219
|
+
if (resolverHead >= resolvers.length) return;
|
|
220
|
+
|
|
221
|
+
const resolver = resolvers[resolverHead];
|
|
222
|
+
resolvers[resolverHead] = undefined;
|
|
223
|
+
resolverHead += 1;
|
|
224
|
+
if (resolverHead >= 64 && resolverHead * 2 >= resolvers.length) {
|
|
225
|
+
resolvers.splice(0, resolverHead);
|
|
226
|
+
resolverHead = 0;
|
|
227
|
+
}
|
|
228
|
+
resolver?.();
|
|
229
|
+
};
|
|
230
|
+
|
|
206
231
|
const push = (msg: ResponsesStreamMessage) => {
|
|
232
|
+
if (done) return;
|
|
233
|
+
|
|
234
|
+
if (maxBufferedEvents !== undefined && queue.length - queueHead >= maxBufferedEvents) {
|
|
235
|
+
failure = new WebSocketError(
|
|
236
|
+
`WebSocket stream exceeded maxBufferedEvents (${maxBufferedEvents})`,
|
|
237
|
+
null,
|
|
238
|
+
);
|
|
239
|
+
done = true;
|
|
240
|
+
queue.length = 0;
|
|
241
|
+
queueHead = 0;
|
|
242
|
+
cleanup();
|
|
243
|
+
flushResolvers();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
207
247
|
queue.push(msg);
|
|
208
|
-
|
|
248
|
+
wakeResolver();
|
|
209
249
|
};
|
|
210
250
|
|
|
211
251
|
const onEvent = (event: ResponsesAPI.BetaResponsesServerEvent) => {
|
|
@@ -235,9 +275,14 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
235
275
|
};
|
|
236
276
|
|
|
237
277
|
const flushResolvers = () => {
|
|
238
|
-
|
|
239
|
-
resolver
|
|
278
|
+
while (resolverHead < resolvers.length) {
|
|
279
|
+
const resolver = resolvers[resolverHead];
|
|
280
|
+
resolvers[resolverHead] = undefined;
|
|
281
|
+
resolverHead += 1;
|
|
282
|
+
resolver?.();
|
|
240
283
|
}
|
|
284
|
+
resolvers.length = 0;
|
|
285
|
+
resolverHead = 0;
|
|
241
286
|
};
|
|
242
287
|
|
|
243
288
|
const onClose = (
|
|
@@ -251,8 +296,9 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
251
296
|
cleanup();
|
|
252
297
|
};
|
|
253
298
|
|
|
254
|
-
const onSocketSwap = (
|
|
255
|
-
|
|
299
|
+
const onSocketSwap = (_oldSocket: TSocket, newSocket: TSocket) => {
|
|
300
|
+
if (currentSocket === newSocket) return;
|
|
301
|
+
currentSocket.off('open', onOpen);
|
|
256
302
|
newSocket.on('open', onOpen);
|
|
257
303
|
currentSocket = newSocket;
|
|
258
304
|
};
|
|
@@ -309,9 +355,21 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
309
355
|
}
|
|
310
356
|
}
|
|
311
357
|
|
|
312
|
-
const resolve = (
|
|
313
|
-
|
|
314
|
-
|
|
358
|
+
const resolve = (
|
|
359
|
+
res: (value: IteratorResult<ResponsesStreamMessage>) => void,
|
|
360
|
+
reject: (error: WebSocketError) => void,
|
|
361
|
+
) => {
|
|
362
|
+
if (failure) {
|
|
363
|
+
reject(failure);
|
|
364
|
+
} else if (queueHead < queue.length) {
|
|
365
|
+
const queued = queue[queueHead]!;
|
|
366
|
+
queue[queueHead] = undefined;
|
|
367
|
+
queueHead += 1;
|
|
368
|
+
if (queueHead >= 64 && queueHead * 2 >= queue.length) {
|
|
369
|
+
queue.splice(0, queueHead);
|
|
370
|
+
queueHead = 0;
|
|
371
|
+
}
|
|
372
|
+
res({ value: queued, done: false });
|
|
315
373
|
} else if (done) {
|
|
316
374
|
res({ value: undefined, done: true });
|
|
317
375
|
} else {
|
|
@@ -321,10 +379,10 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
321
379
|
};
|
|
322
380
|
|
|
323
381
|
const next = (): Promise<IteratorResult<ResponsesStreamMessage>> =>
|
|
324
|
-
new Promise((res) => {
|
|
325
|
-
if (resolve(res)) return;
|
|
382
|
+
new Promise((res, reject) => {
|
|
383
|
+
if (resolve(res, reject)) return;
|
|
326
384
|
resolvers.push(() => {
|
|
327
|
-
resolve(res);
|
|
385
|
+
resolve(res, reject);
|
|
328
386
|
});
|
|
329
387
|
});
|
|
330
388
|
|
|
@@ -332,6 +390,8 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
332
390
|
next,
|
|
333
391
|
return: (): Promise<IteratorReturnResult<undefined>> => {
|
|
334
392
|
done = true;
|
|
393
|
+
queue.length = 0;
|
|
394
|
+
queueHead = 0;
|
|
335
395
|
cleanup();
|
|
336
396
|
flushResolvers();
|
|
337
397
|
return Promise.resolve({ value: undefined, done: true });
|
|
@@ -364,6 +424,35 @@ export abstract class ResponsesWSBase<TSocket extends WebSocketLike> extends Res
|
|
|
364
424
|
return;
|
|
365
425
|
}
|
|
366
426
|
|
|
427
|
+
if (
|
|
428
|
+
event === null ||
|
|
429
|
+
typeof event !== 'object' ||
|
|
430
|
+
Array.isArray(event) ||
|
|
431
|
+
!Object.prototype.hasOwnProperty.call(event, 'type') ||
|
|
432
|
+
typeof event.type !== 'string'
|
|
433
|
+
) {
|
|
434
|
+
this._onError(
|
|
435
|
+
null,
|
|
436
|
+
'received invalid WebSocket event: expected an object with an own string type',
|
|
437
|
+
undefined,
|
|
438
|
+
);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const eventType: string = event.type;
|
|
443
|
+
const reservedEventType =
|
|
444
|
+
eventType === 'raw' ||
|
|
445
|
+
eventType === 'close' ||
|
|
446
|
+
eventType === 'event' ||
|
|
447
|
+
eventType === 'reconnecting' ||
|
|
448
|
+
eventType === 'reconnected' ||
|
|
449
|
+
eventType === 'open' ||
|
|
450
|
+
eventType === 'error';
|
|
451
|
+
if (reservedEventType && eventType !== 'error') {
|
|
452
|
+
this._onError(null, 'received reserved WebSocket event type', undefined);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
367
456
|
this._emit('event', event);
|
|
368
457
|
|
|
369
458
|
if (event.type === 'error') {
|
|
@@ -6,6 +6,8 @@ import { ResponsesWSBase, type ResponsesWSBaseOptions } from './ws-base';
|
|
|
6
6
|
import { OpenAI } from '../../../client';
|
|
7
7
|
import { VERSION } from '../../../version';
|
|
8
8
|
|
|
9
|
+
export type { WebSocketStreamOptions } from '../../../internal/ws';
|
|
10
|
+
|
|
9
11
|
export type { ResponsesWSReconnectOptions } from './ws-base';
|
|
10
12
|
|
|
11
13
|
export interface ResponsesWSClientOptions extends WS.ClientOptions, ResponsesWSBaseOptions {}
|
|
@@ -6,6 +6,8 @@ import { sleep } from '../../../internal/utils/sleep';
|
|
|
6
6
|
import { type WebSocketLike, ReadyState } from '../../../internal/ws-adapter';
|
|
7
7
|
import {
|
|
8
8
|
SendQueue,
|
|
9
|
+
getMaxBufferedEvents,
|
|
10
|
+
type WebSocketStreamOptions,
|
|
9
11
|
flattenRawData,
|
|
10
12
|
isRecoverableClose,
|
|
11
13
|
type RawWebSocketData,
|
|
@@ -55,11 +57,11 @@ export interface ForksWSBaseOptions {
|
|
|
55
57
|
reconnect?: ForksWSReconnectOptions | null | undefined;
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* Default: 1
|
|
60
|
+
* Byte budget for outgoing messages queued while the socket is connecting
|
|
61
|
+
* or reconnecting. An empty queue accepts one message even if it exceeds
|
|
62
|
+
* this budget. Further messages are discarded and an `error` event is emitted
|
|
63
|
+
* if the total queued size would exceed the budget.
|
|
64
|
+
* Default: 1 MiB (1,048,576 bytes).
|
|
63
65
|
*/
|
|
64
66
|
maxQueueSize?: number | undefined;
|
|
65
67
|
}
|
|
@@ -169,6 +171,11 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
169
171
|
* The iterator will exit if the socket closes but exiting the iterator
|
|
170
172
|
* does not close the socket.
|
|
171
173
|
*
|
|
174
|
+
* Pass `maxBufferedEvents` to limit queued records for this iterator, including
|
|
175
|
+
* lifecycle events. Overflow discards its backlog and rejects `next()` with a
|
|
176
|
+
* WebSocketError; the shared socket and other iterators remain active.
|
|
177
|
+
* Omitted means unlimited. This is an event-count limit, not a byte limit.
|
|
178
|
+
*
|
|
172
179
|
* @example
|
|
173
180
|
* ```ts
|
|
174
181
|
* for await (const event of client.stream()) {
|
|
@@ -186,26 +193,59 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
186
193
|
* }
|
|
187
194
|
* ```
|
|
188
195
|
*/
|
|
189
|
-
stream(): AsyncIterableIterator<ForksStreamMessage> {
|
|
190
|
-
return this[Symbol.asyncIterator]();
|
|
196
|
+
stream(options?: WebSocketStreamOptions): AsyncIterableIterator<ForksStreamMessage> {
|
|
197
|
+
return options === undefined ? this[Symbol.asyncIterator]() : this[Symbol.asyncIterator](options);
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<ForksStreamMessage> {
|
|
200
|
+
[Symbol.asyncIterator](options?: WebSocketStreamOptions): AsyncIterableIterator<ForksStreamMessage> {
|
|
194
201
|
if (!this.socket) {
|
|
195
202
|
throw new OpenAIError('Internal error: failed to initialize socket. Please report this issue.');
|
|
196
203
|
}
|
|
197
204
|
|
|
205
|
+
const maxBufferedEvents = getMaxBufferedEvents(options);
|
|
206
|
+
|
|
198
207
|
// Two-queue async iterator: `queue` buffers incoming messages,
|
|
199
208
|
// `resolvers` buffers waiting next() calls. A push wakes the
|
|
200
209
|
// oldest next(); a next() drains the oldest message.
|
|
201
|
-
const queue: ForksStreamMessage[] = [];
|
|
202
|
-
const resolvers: (() => void)[] = [];
|
|
210
|
+
const queue: (ForksStreamMessage | undefined)[] = [];
|
|
211
|
+
const resolvers: ((() => void) | undefined)[] = [];
|
|
212
|
+
let queueHead = 0;
|
|
213
|
+
let resolverHead = 0;
|
|
203
214
|
let done = false;
|
|
215
|
+
let failure: WebSocketError | undefined;
|
|
204
216
|
let currentSocket = this.socket;
|
|
205
217
|
|
|
218
|
+
const wakeResolver = () => {
|
|
219
|
+
if (resolverHead >= resolvers.length) return;
|
|
220
|
+
|
|
221
|
+
const resolver = resolvers[resolverHead];
|
|
222
|
+
resolvers[resolverHead] = undefined;
|
|
223
|
+
resolverHead += 1;
|
|
224
|
+
if (resolverHead >= 64 && resolverHead * 2 >= resolvers.length) {
|
|
225
|
+
resolvers.splice(0, resolverHead);
|
|
226
|
+
resolverHead = 0;
|
|
227
|
+
}
|
|
228
|
+
resolver?.();
|
|
229
|
+
};
|
|
230
|
+
|
|
206
231
|
const push = (msg: ForksStreamMessage) => {
|
|
232
|
+
if (done) return;
|
|
233
|
+
|
|
234
|
+
if (maxBufferedEvents !== undefined && queue.length - queueHead >= maxBufferedEvents) {
|
|
235
|
+
failure = new WebSocketError(
|
|
236
|
+
`WebSocket stream exceeded maxBufferedEvents (${maxBufferedEvents})`,
|
|
237
|
+
null,
|
|
238
|
+
);
|
|
239
|
+
done = true;
|
|
240
|
+
queue.length = 0;
|
|
241
|
+
queueHead = 0;
|
|
242
|
+
cleanup();
|
|
243
|
+
flushResolvers();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
207
247
|
queue.push(msg);
|
|
208
|
-
|
|
248
|
+
wakeResolver();
|
|
209
249
|
};
|
|
210
250
|
|
|
211
251
|
const onEvent = (event: ForksAPI.ForkServerEvent) => {
|
|
@@ -235,9 +275,14 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
235
275
|
};
|
|
236
276
|
|
|
237
277
|
const flushResolvers = () => {
|
|
238
|
-
|
|
239
|
-
resolver
|
|
278
|
+
while (resolverHead < resolvers.length) {
|
|
279
|
+
const resolver = resolvers[resolverHead];
|
|
280
|
+
resolvers[resolverHead] = undefined;
|
|
281
|
+
resolverHead += 1;
|
|
282
|
+
resolver?.();
|
|
240
283
|
}
|
|
284
|
+
resolvers.length = 0;
|
|
285
|
+
resolverHead = 0;
|
|
241
286
|
};
|
|
242
287
|
|
|
243
288
|
const onClose = (code: number, reason: string, unsent: UnsentMessage<ForksAPI.ForkClientEvent>[]) => {
|
|
@@ -247,8 +292,9 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
247
292
|
cleanup();
|
|
248
293
|
};
|
|
249
294
|
|
|
250
|
-
const onSocketSwap = (
|
|
251
|
-
|
|
295
|
+
const onSocketSwap = (_oldSocket: TSocket, newSocket: TSocket) => {
|
|
296
|
+
if (currentSocket === newSocket) return;
|
|
297
|
+
currentSocket.off('open', onOpen);
|
|
252
298
|
newSocket.on('open', onOpen);
|
|
253
299
|
currentSocket = newSocket;
|
|
254
300
|
};
|
|
@@ -305,9 +351,21 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
305
351
|
}
|
|
306
352
|
}
|
|
307
353
|
|
|
308
|
-
const resolve = (
|
|
309
|
-
|
|
310
|
-
|
|
354
|
+
const resolve = (
|
|
355
|
+
res: (value: IteratorResult<ForksStreamMessage>) => void,
|
|
356
|
+
reject: (error: WebSocketError) => void,
|
|
357
|
+
) => {
|
|
358
|
+
if (failure) {
|
|
359
|
+
reject(failure);
|
|
360
|
+
} else if (queueHead < queue.length) {
|
|
361
|
+
const queued = queue[queueHead]!;
|
|
362
|
+
queue[queueHead] = undefined;
|
|
363
|
+
queueHead += 1;
|
|
364
|
+
if (queueHead >= 64 && queueHead * 2 >= queue.length) {
|
|
365
|
+
queue.splice(0, queueHead);
|
|
366
|
+
queueHead = 0;
|
|
367
|
+
}
|
|
368
|
+
res({ value: queued, done: false });
|
|
311
369
|
} else if (done) {
|
|
312
370
|
res({ value: undefined, done: true });
|
|
313
371
|
} else {
|
|
@@ -317,10 +375,10 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
317
375
|
};
|
|
318
376
|
|
|
319
377
|
const next = (): Promise<IteratorResult<ForksStreamMessage>> =>
|
|
320
|
-
new Promise((res) => {
|
|
321
|
-
if (resolve(res)) return;
|
|
378
|
+
new Promise((res, reject) => {
|
|
379
|
+
if (resolve(res, reject)) return;
|
|
322
380
|
resolvers.push(() => {
|
|
323
|
-
resolve(res);
|
|
381
|
+
resolve(res, reject);
|
|
324
382
|
});
|
|
325
383
|
});
|
|
326
384
|
|
|
@@ -328,6 +386,8 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
328
386
|
next,
|
|
329
387
|
return: (): Promise<IteratorReturnResult<undefined>> => {
|
|
330
388
|
done = true;
|
|
389
|
+
queue.length = 0;
|
|
390
|
+
queueHead = 0;
|
|
331
391
|
cleanup();
|
|
332
392
|
flushResolvers();
|
|
333
393
|
return Promise.resolve({ value: undefined, done: true });
|
|
@@ -360,6 +420,35 @@ export abstract class ForksWSBase<TSocket extends WebSocketLike> extends ForksEm
|
|
|
360
420
|
return;
|
|
361
421
|
}
|
|
362
422
|
|
|
423
|
+
if (
|
|
424
|
+
event === null ||
|
|
425
|
+
typeof event !== 'object' ||
|
|
426
|
+
Array.isArray(event) ||
|
|
427
|
+
!Object.prototype.hasOwnProperty.call(event, 'type') ||
|
|
428
|
+
typeof event.type !== 'string'
|
|
429
|
+
) {
|
|
430
|
+
this._onError(
|
|
431
|
+
null,
|
|
432
|
+
'received invalid WebSocket event: expected an object with an own string type',
|
|
433
|
+
undefined,
|
|
434
|
+
);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const eventType: string = event.type;
|
|
439
|
+
const reservedEventType =
|
|
440
|
+
eventType === 'raw' ||
|
|
441
|
+
eventType === 'close' ||
|
|
442
|
+
eventType === 'event' ||
|
|
443
|
+
eventType === 'reconnecting' ||
|
|
444
|
+
eventType === 'reconnected' ||
|
|
445
|
+
eventType === 'open' ||
|
|
446
|
+
eventType === 'error';
|
|
447
|
+
if (reservedEventType && eventType !== 'error') {
|
|
448
|
+
this._onError(null, 'received reserved WebSocket event type', undefined);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
363
452
|
this._emit('event', event);
|
|
364
453
|
|
|
365
454
|
if (event.type === 'error') {
|
|
@@ -6,6 +6,8 @@ import { ForksWSBase, type ForksWSBaseOptions, type ForksWSParameters } from './
|
|
|
6
6
|
import { OpenAI } from '../../../client';
|
|
7
7
|
import { VERSION } from '../../../version';
|
|
8
8
|
|
|
9
|
+
export type { WebSocketStreamOptions } from '../../../internal/ws';
|
|
10
|
+
|
|
9
11
|
export type { ForksWSParameters, ForksWSReconnectOptions } from './ws-base';
|
|
10
12
|
|
|
11
13
|
export interface ForksWSClientOptions extends WS.ClientOptions, ForksWSBaseOptions {}
|
|
@@ -6,6 +6,8 @@ import { sleep } from '../../../internal/utils/sleep';
|
|
|
6
6
|
import { type WebSocketLike, ReadyState } from '../../../internal/ws-adapter';
|
|
7
7
|
import {
|
|
8
8
|
SendQueue,
|
|
9
|
+
getMaxBufferedEvents,
|
|
10
|
+
type WebSocketStreamOptions,
|
|
9
11
|
flattenRawData,
|
|
10
12
|
isRecoverableClose,
|
|
11
13
|
type RawWebSocketData,
|
|
@@ -61,11 +63,11 @@ export interface SidebandWSBaseOptions {
|
|
|
61
63
|
reconnect?: SidebandWSReconnectOptions | null | undefined;
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* Default: 1
|
|
66
|
+
* Byte budget for outgoing messages queued while the socket is connecting
|
|
67
|
+
* or reconnecting. An empty queue accepts one message even if it exceeds
|
|
68
|
+
* this budget. Further messages are discarded and an `error` event is emitted
|
|
69
|
+
* if the total queued size would exceed the budget.
|
|
70
|
+
* Default: 1 MiB (1,048,576 bytes).
|
|
69
71
|
*/
|
|
70
72
|
maxQueueSize?: number | undefined;
|
|
71
73
|
}
|
|
@@ -175,6 +177,11 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
175
177
|
* The iterator will exit if the socket closes but exiting the iterator
|
|
176
178
|
* does not close the socket.
|
|
177
179
|
*
|
|
180
|
+
* Pass `maxBufferedEvents` to limit queued records for this iterator, including
|
|
181
|
+
* lifecycle events. Overflow discards its backlog and rejects `next()` with a
|
|
182
|
+
* WebSocketError; the shared socket and other iterators remain active.
|
|
183
|
+
* Omitted means unlimited. This is an event-count limit, not a byte limit.
|
|
184
|
+
*
|
|
178
185
|
* @example
|
|
179
186
|
* ```ts
|
|
180
187
|
* for await (const event of client.stream()) {
|
|
@@ -192,26 +199,59 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
192
199
|
* }
|
|
193
200
|
* ```
|
|
194
201
|
*/
|
|
195
|
-
stream(): AsyncIterableIterator<SidebandStreamMessage> {
|
|
196
|
-
return this[Symbol.asyncIterator]();
|
|
202
|
+
stream(options?: WebSocketStreamOptions): AsyncIterableIterator<SidebandStreamMessage> {
|
|
203
|
+
return options === undefined ? this[Symbol.asyncIterator]() : this[Symbol.asyncIterator](options);
|
|
197
204
|
}
|
|
198
205
|
|
|
199
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<SidebandStreamMessage> {
|
|
206
|
+
[Symbol.asyncIterator](options?: WebSocketStreamOptions): AsyncIterableIterator<SidebandStreamMessage> {
|
|
200
207
|
if (!this.socket) {
|
|
201
208
|
throw new OpenAIError('Internal error: failed to initialize socket. Please report this issue.');
|
|
202
209
|
}
|
|
203
210
|
|
|
211
|
+
const maxBufferedEvents = getMaxBufferedEvents(options);
|
|
212
|
+
|
|
204
213
|
// Two-queue async iterator: `queue` buffers incoming messages,
|
|
205
214
|
// `resolvers` buffers waiting next() calls. A push wakes the
|
|
206
215
|
// oldest next(); a next() drains the oldest message.
|
|
207
|
-
const queue: SidebandStreamMessage[] = [];
|
|
208
|
-
const resolvers: (() => void)[] = [];
|
|
216
|
+
const queue: (SidebandStreamMessage | undefined)[] = [];
|
|
217
|
+
const resolvers: ((() => void) | undefined)[] = [];
|
|
218
|
+
let queueHead = 0;
|
|
219
|
+
let resolverHead = 0;
|
|
209
220
|
let done = false;
|
|
221
|
+
let failure: WebSocketError | undefined;
|
|
210
222
|
let currentSocket = this.socket;
|
|
211
223
|
|
|
224
|
+
const wakeResolver = () => {
|
|
225
|
+
if (resolverHead >= resolvers.length) return;
|
|
226
|
+
|
|
227
|
+
const resolver = resolvers[resolverHead];
|
|
228
|
+
resolvers[resolverHead] = undefined;
|
|
229
|
+
resolverHead += 1;
|
|
230
|
+
if (resolverHead >= 64 && resolverHead * 2 >= resolvers.length) {
|
|
231
|
+
resolvers.splice(0, resolverHead);
|
|
232
|
+
resolverHead = 0;
|
|
233
|
+
}
|
|
234
|
+
resolver?.();
|
|
235
|
+
};
|
|
236
|
+
|
|
212
237
|
const push = (msg: SidebandStreamMessage) => {
|
|
238
|
+
if (done) return;
|
|
239
|
+
|
|
240
|
+
if (maxBufferedEvents !== undefined && queue.length - queueHead >= maxBufferedEvents) {
|
|
241
|
+
failure = new WebSocketError(
|
|
242
|
+
`WebSocket stream exceeded maxBufferedEvents (${maxBufferedEvents})`,
|
|
243
|
+
null,
|
|
244
|
+
);
|
|
245
|
+
done = true;
|
|
246
|
+
queue.length = 0;
|
|
247
|
+
queueHead = 0;
|
|
248
|
+
cleanup();
|
|
249
|
+
flushResolvers();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
213
253
|
queue.push(msg);
|
|
214
|
-
|
|
254
|
+
wakeResolver();
|
|
215
255
|
};
|
|
216
256
|
|
|
217
257
|
const onEvent = (event: SidebandAPI.ConnectServerEvent) => {
|
|
@@ -241,9 +281,14 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
241
281
|
};
|
|
242
282
|
|
|
243
283
|
const flushResolvers = () => {
|
|
244
|
-
|
|
245
|
-
resolver
|
|
284
|
+
while (resolverHead < resolvers.length) {
|
|
285
|
+
const resolver = resolvers[resolverHead];
|
|
286
|
+
resolvers[resolverHead] = undefined;
|
|
287
|
+
resolverHead += 1;
|
|
288
|
+
resolver?.();
|
|
246
289
|
}
|
|
290
|
+
resolvers.length = 0;
|
|
291
|
+
resolverHead = 0;
|
|
247
292
|
};
|
|
248
293
|
|
|
249
294
|
const onClose = (
|
|
@@ -257,8 +302,9 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
257
302
|
cleanup();
|
|
258
303
|
};
|
|
259
304
|
|
|
260
|
-
const onSocketSwap = (
|
|
261
|
-
|
|
305
|
+
const onSocketSwap = (_oldSocket: TSocket, newSocket: TSocket) => {
|
|
306
|
+
if (currentSocket === newSocket) return;
|
|
307
|
+
currentSocket.off('open', onOpen);
|
|
262
308
|
newSocket.on('open', onOpen);
|
|
263
309
|
currentSocket = newSocket;
|
|
264
310
|
};
|
|
@@ -315,9 +361,21 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
315
361
|
}
|
|
316
362
|
}
|
|
317
363
|
|
|
318
|
-
const resolve = (
|
|
319
|
-
|
|
320
|
-
|
|
364
|
+
const resolve = (
|
|
365
|
+
res: (value: IteratorResult<SidebandStreamMessage>) => void,
|
|
366
|
+
reject: (error: WebSocketError) => void,
|
|
367
|
+
) => {
|
|
368
|
+
if (failure) {
|
|
369
|
+
reject(failure);
|
|
370
|
+
} else if (queueHead < queue.length) {
|
|
371
|
+
const queued = queue[queueHead]!;
|
|
372
|
+
queue[queueHead] = undefined;
|
|
373
|
+
queueHead += 1;
|
|
374
|
+
if (queueHead >= 64 && queueHead * 2 >= queue.length) {
|
|
375
|
+
queue.splice(0, queueHead);
|
|
376
|
+
queueHead = 0;
|
|
377
|
+
}
|
|
378
|
+
res({ value: queued, done: false });
|
|
321
379
|
} else if (done) {
|
|
322
380
|
res({ value: undefined, done: true });
|
|
323
381
|
} else {
|
|
@@ -327,10 +385,10 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
327
385
|
};
|
|
328
386
|
|
|
329
387
|
const next = (): Promise<IteratorResult<SidebandStreamMessage>> =>
|
|
330
|
-
new Promise((res) => {
|
|
331
|
-
if (resolve(res)) return;
|
|
388
|
+
new Promise((res, reject) => {
|
|
389
|
+
if (resolve(res, reject)) return;
|
|
332
390
|
resolvers.push(() => {
|
|
333
|
-
resolve(res);
|
|
391
|
+
resolve(res, reject);
|
|
334
392
|
});
|
|
335
393
|
});
|
|
336
394
|
|
|
@@ -338,6 +396,8 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
338
396
|
next,
|
|
339
397
|
return: (): Promise<IteratorReturnResult<undefined>> => {
|
|
340
398
|
done = true;
|
|
399
|
+
queue.length = 0;
|
|
400
|
+
queueHead = 0;
|
|
341
401
|
cleanup();
|
|
342
402
|
flushResolvers();
|
|
343
403
|
return Promise.resolve({ value: undefined, done: true });
|
|
@@ -370,6 +430,35 @@ export abstract class SidebandWSBase<TSocket extends WebSocketLike> extends Side
|
|
|
370
430
|
return;
|
|
371
431
|
}
|
|
372
432
|
|
|
433
|
+
if (
|
|
434
|
+
event === null ||
|
|
435
|
+
typeof event !== 'object' ||
|
|
436
|
+
Array.isArray(event) ||
|
|
437
|
+
!Object.prototype.hasOwnProperty.call(event, 'type') ||
|
|
438
|
+
typeof event.type !== 'string'
|
|
439
|
+
) {
|
|
440
|
+
this._onError(
|
|
441
|
+
null,
|
|
442
|
+
'received invalid WebSocket event: expected an object with an own string type',
|
|
443
|
+
undefined,
|
|
444
|
+
);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const eventType: string = event.type;
|
|
449
|
+
const reservedEventType =
|
|
450
|
+
eventType === 'raw' ||
|
|
451
|
+
eventType === 'close' ||
|
|
452
|
+
eventType === 'event' ||
|
|
453
|
+
eventType === 'reconnecting' ||
|
|
454
|
+
eventType === 'reconnected' ||
|
|
455
|
+
eventType === 'open' ||
|
|
456
|
+
eventType === 'error';
|
|
457
|
+
if (reservedEventType && eventType !== 'error') {
|
|
458
|
+
this._onError(null, 'received reserved WebSocket event type', undefined);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
373
462
|
this._emit('event', event);
|
|
374
463
|
|
|
375
464
|
if (event.type === 'error') {
|
|
@@ -6,6 +6,8 @@ import { SidebandWSBase, type SidebandWSBaseOptions, type SidebandWSParameters }
|
|
|
6
6
|
import { OpenAI } from '../../../client';
|
|
7
7
|
import { VERSION } from '../../../version';
|
|
8
8
|
|
|
9
|
+
export type { WebSocketStreamOptions } from '../../../internal/ws';
|
|
10
|
+
|
|
9
11
|
export type { SidebandWSParameters, SidebandWSReconnectOptions } from './ws-base';
|
|
10
12
|
|
|
11
13
|
export interface SidebandWSClientOptions extends WS.ClientOptions, SidebandWSBaseOptions {}
|