openai 4.28.4 → 4.29.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 (152) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +7 -10
  3. package/index.d.mts +1 -0
  4. package/index.d.ts +1 -0
  5. package/index.d.ts.map +1 -1
  6. package/index.js.map +1 -1
  7. package/index.mjs.map +1 -1
  8. package/lib/AbstractAssistantStreamRunner.d.ts +74 -0
  9. package/lib/AbstractAssistantStreamRunner.d.ts.map +1 -0
  10. package/lib/AbstractAssistantStreamRunner.js +246 -0
  11. package/lib/AbstractAssistantStreamRunner.js.map +1 -0
  12. package/lib/AbstractAssistantStreamRunner.mjs +242 -0
  13. package/lib/AbstractAssistantStreamRunner.mjs.map +1 -0
  14. package/lib/AssistantStream.d.ts +56 -0
  15. package/lib/AssistantStream.d.ts.map +1 -0
  16. package/lib/AssistantStream.js +526 -0
  17. package/lib/AssistantStream.js.map +1 -0
  18. package/lib/AssistantStream.mjs +499 -0
  19. package/lib/AssistantStream.mjs.map +1 -0
  20. package/lib/ChatCompletionRunFunctions.test.js +35 -0
  21. package/lib/ChatCompletionRunFunctions.test.js.map +1 -1
  22. package/lib/ChatCompletionRunFunctions.test.mjs +35 -0
  23. package/lib/ChatCompletionRunFunctions.test.mjs.map +1 -1
  24. package/lib/ChatCompletionStream.d.ts.map +1 -1
  25. package/lib/ChatCompletionStream.js +21 -3
  26. package/lib/ChatCompletionStream.js.map +1 -1
  27. package/lib/ChatCompletionStream.mjs +21 -3
  28. package/lib/ChatCompletionStream.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/resources/audio/speech.d.ts +3 -6
  31. package/resources/audio/speech.d.ts.map +1 -1
  32. package/resources/audio/speech.js.map +1 -1
  33. package/resources/audio/speech.mjs.map +1 -1
  34. package/resources/audio/transcriptions.d.ts +14 -4
  35. package/resources/audio/transcriptions.d.ts.map +1 -1
  36. package/resources/audio/transcriptions.js.map +1 -1
  37. package/resources/audio/transcriptions.mjs.map +1 -1
  38. package/resources/audio/translations.d.ts +2 -1
  39. package/resources/audio/translations.d.ts.map +1 -1
  40. package/resources/audio/translations.js.map +1 -1
  41. package/resources/audio/translations.mjs.map +1 -1
  42. package/resources/beta/assistants/assistants.d.ts +629 -60
  43. package/resources/beta/assistants/assistants.d.ts.map +1 -1
  44. package/resources/beta/assistants/assistants.js.map +1 -1
  45. package/resources/beta/assistants/assistants.mjs.map +1 -1
  46. package/resources/beta/assistants/index.d.ts +1 -1
  47. package/resources/beta/assistants/index.d.ts.map +1 -1
  48. package/resources/beta/assistants/index.js.map +1 -1
  49. package/resources/beta/assistants/index.mjs.map +1 -1
  50. package/resources/beta/beta.d.ts +12 -0
  51. package/resources/beta/beta.d.ts.map +1 -1
  52. package/resources/beta/beta.js.map +1 -1
  53. package/resources/beta/beta.mjs.map +1 -1
  54. package/resources/beta/index.d.ts +2 -2
  55. package/resources/beta/index.d.ts.map +1 -1
  56. package/resources/beta/index.js.map +1 -1
  57. package/resources/beta/index.mjs.map +1 -1
  58. package/resources/beta/threads/index.d.ts +3 -3
  59. package/resources/beta/threads/index.d.ts.map +1 -1
  60. package/resources/beta/threads/index.js +2 -2
  61. package/resources/beta/threads/index.js.map +1 -1
  62. package/resources/beta/threads/index.mjs +1 -1
  63. package/resources/beta/threads/index.mjs.map +1 -1
  64. package/resources/beta/threads/messages/index.d.ts +1 -1
  65. package/resources/beta/threads/messages/index.d.ts.map +1 -1
  66. package/resources/beta/threads/messages/index.js +2 -2
  67. package/resources/beta/threads/messages/index.js.map +1 -1
  68. package/resources/beta/threads/messages/index.mjs +1 -1
  69. package/resources/beta/threads/messages/index.mjs.map +1 -1
  70. package/resources/beta/threads/messages/messages.d.ts +289 -86
  71. package/resources/beta/threads/messages/messages.d.ts.map +1 -1
  72. package/resources/beta/threads/messages/messages.js +5 -5
  73. package/resources/beta/threads/messages/messages.js.map +1 -1
  74. package/resources/beta/threads/messages/messages.mjs +3 -3
  75. package/resources/beta/threads/messages/messages.mjs.map +1 -1
  76. package/resources/beta/threads/runs/index.d.ts +2 -2
  77. package/resources/beta/threads/runs/index.d.ts.map +1 -1
  78. package/resources/beta/threads/runs/index.js.map +1 -1
  79. package/resources/beta/threads/runs/index.mjs.map +1 -1
  80. package/resources/beta/threads/runs/runs.d.ts +153 -49
  81. package/resources/beta/threads/runs/runs.d.ts.map +1 -1
  82. package/resources/beta/threads/runs/runs.js +14 -7
  83. package/resources/beta/threads/runs/runs.js.map +1 -1
  84. package/resources/beta/threads/runs/runs.mjs +14 -7
  85. package/resources/beta/threads/runs/runs.mjs.map +1 -1
  86. package/resources/beta/threads/runs/steps.d.ts +219 -5
  87. package/resources/beta/threads/runs/steps.d.ts.map +1 -1
  88. package/resources/beta/threads/runs/steps.js.map +1 -1
  89. package/resources/beta/threads/runs/steps.mjs.map +1 -1
  90. package/resources/beta/threads/threads.d.ts +144 -22
  91. package/resources/beta/threads/threads.d.ts.map +1 -1
  92. package/resources/beta/threads/threads.js +9 -4
  93. package/resources/beta/threads/threads.js.map +1 -1
  94. package/resources/beta/threads/threads.mjs +9 -4
  95. package/resources/beta/threads/threads.mjs.map +1 -1
  96. package/resources/chat/completions.d.ts +28 -18
  97. package/resources/chat/completions.d.ts.map +1 -1
  98. package/resources/chat/completions.js.map +1 -1
  99. package/resources/chat/completions.mjs.map +1 -1
  100. package/resources/completions.d.ts +2 -0
  101. package/resources/completions.d.ts.map +1 -1
  102. package/resources/completions.js.map +1 -1
  103. package/resources/completions.mjs.map +1 -1
  104. package/resources/files.d.ts +4 -4
  105. package/resources/images.d.ts +6 -3
  106. package/resources/images.d.ts.map +1 -1
  107. package/resources/images.js.map +1 -1
  108. package/resources/images.mjs.map +1 -1
  109. package/resources/moderations.d.ts +3 -5
  110. package/resources/moderations.d.ts.map +1 -1
  111. package/resources/moderations.js +1 -1
  112. package/resources/moderations.js.map +1 -1
  113. package/resources/moderations.mjs +1 -1
  114. package/resources/moderations.mjs.map +1 -1
  115. package/resources/shared.d.ts +6 -0
  116. package/resources/shared.d.ts.map +1 -1
  117. package/src/index.ts +1 -0
  118. package/src/lib/AbstractAssistantStreamRunner.ts +340 -0
  119. package/src/lib/AssistantStream.ts +698 -0
  120. package/src/lib/ChatCompletionRunFunctions.test.ts +52 -1
  121. package/src/lib/ChatCompletionStream.ts +29 -6
  122. package/src/resources/audio/speech.ts +3 -6
  123. package/src/resources/audio/transcriptions.ts +14 -4
  124. package/src/resources/audio/translations.ts +2 -1
  125. package/src/resources/beta/assistants/assistants.ts +768 -76
  126. package/src/resources/beta/assistants/index.ts +9 -0
  127. package/src/resources/beta/beta.ts +12 -0
  128. package/src/resources/beta/index.ts +12 -0
  129. package/src/resources/beta/threads/index.ts +30 -5
  130. package/src/resources/beta/threads/messages/index.ts +21 -5
  131. package/src/resources/beta/threads/messages/messages.ts +332 -94
  132. package/src/resources/beta/threads/runs/index.ts +18 -1
  133. package/src/resources/beta/threads/runs/runs.ts +225 -60
  134. package/src/resources/beta/threads/runs/steps.ts +254 -5
  135. package/src/resources/beta/threads/threads.ts +182 -25
  136. package/src/resources/chat/completions.ts +28 -18
  137. package/src/resources/completions.ts +2 -0
  138. package/src/resources/files.ts +4 -4
  139. package/src/resources/images.ts +6 -3
  140. package/src/resources/moderations.ts +3 -5
  141. package/src/resources/shared.ts +10 -0
  142. package/src/streaming.ts +31 -0
  143. package/src/version.ts +1 -1
  144. package/streaming.d.ts +2 -0
  145. package/streaming.d.ts.map +1 -1
  146. package/streaming.js +32 -1
  147. package/streaming.js.map +1 -1
  148. package/streaming.mjs +30 -0
  149. package/streaming.mjs.map +1 -1
  150. package/version.d.ts +1 -1
  151. package/version.js +1 -1
  152. package/version.mjs +1 -1
@@ -0,0 +1,698 @@
1
+ import {
2
+ TextContentBlock,
3
+ ImageFileContentBlock,
4
+ Message,
5
+ MessageContentDelta,
6
+ Text,
7
+ ImageFile,
8
+ TextDelta,
9
+ Messages,
10
+ } from "../resources/beta/threads/messages/messages";
11
+ import * as Core from "../core";
12
+ import { RequestOptions } from "../core";
13
+ import {
14
+ Run,
15
+ RunCreateParamsBase,
16
+ RunCreateParamsStreaming,
17
+ Runs,
18
+ RunSubmitToolOutputsParamsBase,
19
+ RunSubmitToolOutputsParamsStreaming,
20
+ } from "../resources/beta/threads/runs/runs";
21
+ import {
22
+ AbstractAssistantRunnerEvents,
23
+ AbstractAssistantStreamRunner,
24
+ } from './AbstractAssistantStreamRunner';
25
+ import { type ReadableStream } from "../_shims/index";
26
+ import { Stream } from "../streaming";
27
+ import { APIUserAbortError, OpenAIError } from "../error";
28
+ import {
29
+ AssistantStreamEvent,
30
+ MessageStreamEvent,
31
+ RunStepStreamEvent,
32
+ RunStreamEvent,
33
+ } from "../resources/beta/assistants/assistants";
34
+ import { RunStep, RunStepDelta, ToolCall, ToolCallDelta } from "../resources/beta/threads/runs/steps";
35
+ import { ThreadCreateAndRunParamsBase, Threads } from "../resources/beta/threads/threads";
36
+ import MessageDelta = Messages.MessageDelta;
37
+
38
+ export interface AssistantStreamEvents extends AbstractAssistantRunnerEvents {
39
+ //New event structure
40
+ messageCreated: (message: Message) => void;
41
+ messageDelta: (message: MessageDelta, snapshot: Message) => void;
42
+ messageDone: (message: Message) => void;
43
+
44
+ runStepCreated: (runStep: RunStep) => void;
45
+ runStepDelta: (delta: RunStepDelta, snapshot: Runs.RunStep) => void;
46
+ runStepDone: (runStep: Runs.RunStep, snapshot: Runs.RunStep) => void;
47
+
48
+ toolCallCreated: (toolCall: ToolCall) => void;
49
+ toolCallDelta: (delta: ToolCallDelta, snapshot: ToolCall) => void;
50
+ toolCallDone: (toolCall: ToolCall) => void;
51
+
52
+ textCreated: (content: Text) => void;
53
+ textDelta: (delta: TextDelta, snapshot: Text) => void;
54
+ textDone: (content: Text, snapshot: Message) => void;
55
+
56
+ //No created or delta as this is not streamed
57
+ imageFileDone: (content: ImageFile, snapshot: Message) => void;
58
+
59
+ end: () => void;
60
+
61
+ event: (event: AssistantStreamEvent) => void;
62
+ }
63
+
64
+ export type ThreadCreateAndRunParamsBaseStream = Omit<ThreadCreateAndRunParamsBase, 'stream'> & {
65
+ stream?: true;
66
+ };
67
+
68
+ export type RunCreateParamsBaseStream = Omit<RunCreateParamsBase, 'stream'> & {
69
+ stream?: true;
70
+ };
71
+
72
+ export type RunSubmitToolOutputsParamsStream = Omit<RunSubmitToolOutputsParamsBase, 'stream'> & {
73
+ stream?: true;
74
+ };
75
+
76
+ export class AssistantStream
77
+ extends AbstractAssistantStreamRunner<AssistantStreamEvents>
78
+ implements AsyncIterable<AssistantStreamEvent>
79
+ {
80
+ //Track all events in a single list for reference
81
+ #events: AssistantStreamEvent[] = [];
82
+
83
+ //Used to accumulate deltas
84
+ //We are accumulating many types so the value here is not strict
85
+ #runStepSnapshots: { [id: string]: Runs.RunStep } = {};
86
+ #messageSnapshots: { [id: string]: Message } = {};
87
+ #messageSnapshot: Message | undefined;
88
+ #finalRun: Run | undefined;
89
+ #currentContentIndex: number | undefined;
90
+ #currentContent: TextContentBlock | ImageFileContentBlock | undefined;
91
+ #currentToolCallIndex: number | undefined;
92
+ #currentToolCall: ToolCall | undefined;
93
+
94
+ //For current snapshot methods
95
+ #currentEvent: AssistantStreamEvent | undefined;
96
+ #currentRunSnapshot: Run | undefined;
97
+ #currentRunStepSnapshot: Runs.RunStep | undefined;
98
+
99
+ [Symbol.asyncIterator](): AsyncIterator<AssistantStreamEvent> {
100
+ const pushQueue: AssistantStreamEvent[] = [];
101
+ const readQueue: {
102
+ resolve: (chunk: AssistantStreamEvent | undefined) => void;
103
+ reject: (err: unknown) => void;
104
+ }[] = [];
105
+ let done = false;
106
+
107
+ //Catch all for passing along all events
108
+ this.on('event', (event) => {
109
+ const reader = readQueue.shift();
110
+ if (reader) {
111
+ reader.resolve(event);
112
+ } else {
113
+ pushQueue.push(event);
114
+ }
115
+ });
116
+
117
+ this.on('end', () => {
118
+ done = true;
119
+ for (const reader of readQueue) {
120
+ reader.resolve(undefined);
121
+ }
122
+ readQueue.length = 0;
123
+ });
124
+
125
+ this.on('abort', (err) => {
126
+ done = true;
127
+ for (const reader of readQueue) {
128
+ reader.reject(err);
129
+ }
130
+ readQueue.length = 0;
131
+ });
132
+
133
+ this.on('error', (err) => {
134
+ done = true;
135
+ for (const reader of readQueue) {
136
+ reader.reject(err);
137
+ }
138
+ readQueue.length = 0;
139
+ });
140
+
141
+ return {
142
+ next: async (): Promise<IteratorResult<AssistantStreamEvent>> => {
143
+ if (!pushQueue.length) {
144
+ if (done) {
145
+ return { value: undefined, done: true };
146
+ }
147
+ return new Promise<AssistantStreamEvent | undefined>((resolve, reject) =>
148
+ readQueue.push({ resolve, reject }),
149
+ ).then((chunk) => (chunk ? { value: chunk, done: false } : { value: undefined, done: true }));
150
+ }
151
+ const chunk = pushQueue.shift()!;
152
+ return { value: chunk, done: false };
153
+ },
154
+ return: async () => {
155
+ this.abort();
156
+ return { value: undefined, done: true };
157
+ },
158
+ };
159
+ }
160
+
161
+ toReadableStream(): ReadableStream {
162
+ const stream = new Stream(this[Symbol.asyncIterator].bind(this), this.controller);
163
+ return stream.toReadableStream();
164
+ }
165
+
166
+ static createToolAssistantStream(
167
+ threadId: string,
168
+ runId: string,
169
+ runs: Runs,
170
+ body: RunSubmitToolOutputsParamsStream,
171
+ options: RequestOptions | undefined,
172
+ ) {
173
+ const runner = new AssistantStream();
174
+ runner._run(() =>
175
+ runner._runToolAssistantStream(threadId, runId, runs, body, {
176
+ ...options,
177
+ headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' },
178
+ }),
179
+ );
180
+ return runner;
181
+ }
182
+
183
+ protected override async _createToolAssistantStream(
184
+ run: Runs,
185
+ threadId: string,
186
+ runId: string,
187
+ params: RunSubmitToolOutputsParamsStream,
188
+ options?: Core.RequestOptions,
189
+ ): Promise<Run> {
190
+ const signal = options?.signal;
191
+ if (signal) {
192
+ if (signal.aborted) this.controller.abort();
193
+ signal.addEventListener('abort', () => this.controller.abort());
194
+ }
195
+
196
+ const body: RunSubmitToolOutputsParamsStreaming = { ...params, stream: true };
197
+ const stream = await run.submitToolOutputs(threadId, runId, body, {
198
+ ...options,
199
+ signal: this.controller.signal,
200
+ });
201
+
202
+ this._connected();
203
+
204
+ for await (const event of stream) {
205
+ this.#addEvent(event);
206
+ }
207
+ if (stream.controller.signal?.aborted) {
208
+ throw new APIUserAbortError();
209
+ }
210
+
211
+ return this._addRun(this.#endRequest());
212
+ }
213
+
214
+ static createThreadAssistantStream(
215
+ body: ThreadCreateAndRunParamsBaseStream,
216
+ thread: Threads,
217
+ options?: RequestOptions,
218
+ ) {
219
+ const runner = new AssistantStream();
220
+ runner._run(() =>
221
+ runner._threadAssistantStream(body, thread, {
222
+ ...options,
223
+ headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' },
224
+ }),
225
+ );
226
+ return runner;
227
+ }
228
+
229
+ static createAssistantStream(
230
+ threadId: string,
231
+ runs: Runs,
232
+ params: RunCreateParamsBaseStream,
233
+ options?: RequestOptions,
234
+ ) {
235
+ const runner = new AssistantStream();
236
+ runner._run(() =>
237
+ runner._runAssistantStream(threadId, runs, params, {
238
+ ...options,
239
+ headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' },
240
+ }),
241
+ );
242
+ return runner;
243
+ }
244
+
245
+ currentEvent(): AssistantStreamEvent | undefined {
246
+ return this.#currentEvent;
247
+ }
248
+
249
+ currentRun(): Run | undefined {
250
+ return this.#currentRunSnapshot;
251
+ }
252
+
253
+ currentMessageSnapshot(): Message | undefined {
254
+ return this.#messageSnapshot;
255
+ }
256
+
257
+ currentRunStepSnapshot(): Runs.RunStep | undefined {
258
+ return this.#currentRunStepSnapshot;
259
+ }
260
+
261
+ async finalRunSteps(): Promise<Runs.RunStep[]> {
262
+ await this.done();
263
+
264
+ return Object.values(this.#runStepSnapshots);
265
+ }
266
+
267
+ async finalMessages(): Promise<Message[]> {
268
+ await this.done();
269
+
270
+ return Object.values(this.#messageSnapshots);
271
+ }
272
+
273
+ async finalRun(): Promise<Run> {
274
+ await this.done();
275
+ if (!this.#finalRun) throw Error('Final run was not received.');
276
+
277
+ return this.#finalRun;
278
+ }
279
+
280
+ protected override async _createThreadAssistantStream(
281
+ thread: Threads,
282
+ params: ThreadCreateAndRunParamsBase,
283
+ options?: Core.RequestOptions,
284
+ ): Promise<Run> {
285
+ const signal = options?.signal;
286
+ if (signal) {
287
+ if (signal.aborted) this.controller.abort();
288
+ signal.addEventListener('abort', () => this.controller.abort());
289
+ }
290
+
291
+ const body: RunCreateParamsStreaming = { ...params, stream: true };
292
+ const stream = await thread.createAndRun(body, { ...options, signal: this.controller.signal });
293
+
294
+ this._connected();
295
+
296
+ for await (const event of stream) {
297
+ this.#addEvent(event);
298
+ }
299
+ if (stream.controller.signal?.aborted) {
300
+ throw new APIUserAbortError();
301
+ }
302
+
303
+ return this._addRun(this.#endRequest());
304
+ }
305
+
306
+ protected override async _createAssistantStream(
307
+ run: Runs,
308
+ threadId: string,
309
+ params: RunCreateParamsBase,
310
+ options?: Core.RequestOptions,
311
+ ): Promise<Run> {
312
+ const signal = options?.signal;
313
+ if (signal) {
314
+ if (signal.aborted) this.controller.abort();
315
+ signal.addEventListener('abort', () => this.controller.abort());
316
+ }
317
+
318
+ const body: RunCreateParamsStreaming = { ...params, stream: true };
319
+ const stream = await run.create(threadId, body, { ...options, signal: this.controller.signal });
320
+
321
+ this._connected();
322
+
323
+ for await (const event of stream) {
324
+ this.#addEvent(event);
325
+ }
326
+ if (stream.controller.signal?.aborted) {
327
+ throw new APIUserAbortError();
328
+ }
329
+
330
+ return this._addRun(this.#endRequest());
331
+ }
332
+
333
+ #addEvent(event: AssistantStreamEvent) {
334
+ if (this.ended) return;
335
+
336
+ this.#currentEvent = event;
337
+
338
+ this.#handleEvent(event);
339
+
340
+ switch (event.event) {
341
+ case 'thread.created':
342
+ //No action on this event.
343
+ break;
344
+
345
+ case 'thread.run.created':
346
+ case 'thread.run.queued':
347
+ case 'thread.run.in_progress':
348
+ case 'thread.run.requires_action':
349
+ case 'thread.run.completed':
350
+ case 'thread.run.failed':
351
+ case 'thread.run.cancelling':
352
+ case 'thread.run.cancelled':
353
+ case 'thread.run.expired':
354
+ this.#handleRun(event);
355
+ break;
356
+
357
+ case 'thread.run.step.created':
358
+ case 'thread.run.step.in_progress':
359
+ case 'thread.run.step.delta':
360
+ case 'thread.run.step.completed':
361
+ case 'thread.run.step.failed':
362
+ case 'thread.run.step.cancelled':
363
+ case 'thread.run.step.expired':
364
+ this.#handleRunStep(event);
365
+ break;
366
+
367
+ case 'thread.message.created':
368
+ case 'thread.message.in_progress':
369
+ case 'thread.message.delta':
370
+ case 'thread.message.completed':
371
+ case 'thread.message.incomplete':
372
+ this.#handleMessage(event);
373
+ break;
374
+
375
+ case 'error':
376
+ //This is included for completeness, but errors are processed in the SSE event processing so this should not occur
377
+ throw new Error(
378
+ 'Encountered an error event in event processing - errors should be processed earlier',
379
+ );
380
+ }
381
+ }
382
+
383
+ #endRequest(): Run {
384
+ if (this.ended) {
385
+ throw new OpenAIError(`stream has ended, this shouldn't happen`);
386
+ }
387
+
388
+ if (!this.#finalRun) throw Error('Final run has been been received');
389
+
390
+ return this.#finalRun;
391
+ }
392
+
393
+ #handleMessage(event: MessageStreamEvent) {
394
+ const [accumulatedMessage, newContent] = this.#accumulateMessage(event, this.#messageSnapshot);
395
+ this.#messageSnapshot = accumulatedMessage;
396
+ this.#messageSnapshots[accumulatedMessage.id] = accumulatedMessage;
397
+
398
+ for (const content of newContent) {
399
+ const snapshotContent = accumulatedMessage.content[content.index];
400
+ if (snapshotContent?.type == 'text') {
401
+ this._emit('textCreated', snapshotContent.text);
402
+ }
403
+ }
404
+
405
+ switch (event.event) {
406
+ case 'thread.message.created':
407
+ this._emit('messageCreated', event.data);
408
+ break;
409
+
410
+ case 'thread.message.in_progress':
411
+ break;
412
+
413
+ case 'thread.message.delta':
414
+ this._emit('messageDelta', event.data.delta, accumulatedMessage);
415
+
416
+ if (event.data.delta.content) {
417
+ for (const content of event.data.delta.content) {
418
+ //If it is text delta, emit a text delta event
419
+ if (content.type == 'text' && content.text) {
420
+ let textDelta = content.text;
421
+ let snapshot = accumulatedMessage.content[content.index];
422
+ if (snapshot && snapshot.type == 'text') {
423
+ this._emit('textDelta', textDelta, snapshot.text);
424
+ } else {
425
+ throw Error('The snapshot associated with this text delta is not text or missing');
426
+ }
427
+ }
428
+
429
+ if (content.index != this.#currentContentIndex) {
430
+ //See if we have in progress content
431
+ if (this.#currentContent) {
432
+ switch (this.#currentContent.type) {
433
+ case 'text':
434
+ this._emit('textDone', this.#currentContent.text, this.#messageSnapshot);
435
+ break;
436
+ case 'image_file':
437
+ this._emit('imageFileDone', this.#currentContent.image_file, this.#messageSnapshot);
438
+ break;
439
+ }
440
+ }
441
+
442
+ this.#currentContentIndex = content.index;
443
+ }
444
+
445
+ this.#currentContent = accumulatedMessage.content[content.index];
446
+ }
447
+ }
448
+
449
+ break;
450
+
451
+ case 'thread.message.completed':
452
+ case 'thread.message.incomplete':
453
+ //We emit the latest content we were working on on completion (including incomplete)
454
+ if (this.#currentContentIndex !== undefined) {
455
+ const currentContent = event.data.content[this.#currentContentIndex];
456
+ if (currentContent) {
457
+ switch (currentContent.type) {
458
+ case 'image_file':
459
+ this._emit('imageFileDone', currentContent.image_file, this.#messageSnapshot);
460
+ break;
461
+ case 'text':
462
+ this._emit('textDone', currentContent.text, this.#messageSnapshot);
463
+ break;
464
+ }
465
+ }
466
+ }
467
+
468
+ if (this.#messageSnapshot) {
469
+ this._emit('messageDone', event.data);
470
+ }
471
+
472
+ this.#messageSnapshot = undefined;
473
+ }
474
+ }
475
+
476
+ #handleRunStep(event: RunStepStreamEvent) {
477
+ const accumulatedRunStep = this.#accumulateRunStep(event);
478
+ this.#currentRunStepSnapshot = accumulatedRunStep;
479
+
480
+ switch (event.event) {
481
+ case 'thread.run.step.created':
482
+ this._emit('runStepCreated', event.data);
483
+ break;
484
+ case 'thread.run.step.delta':
485
+ const delta = event.data.delta;
486
+ if (
487
+ delta.step_details &&
488
+ delta.step_details.type == 'tool_calls' &&
489
+ delta.step_details.tool_calls &&
490
+ accumulatedRunStep.step_details.type == 'tool_calls'
491
+ ) {
492
+ for (const toolCall of delta.step_details.tool_calls) {
493
+ if (toolCall.index == this.#currentToolCallIndex) {
494
+ this._emit(
495
+ 'toolCallDelta',
496
+ toolCall,
497
+ accumulatedRunStep.step_details.tool_calls[toolCall.index] as ToolCall,
498
+ );
499
+ } else {
500
+ if (this.#currentToolCall) {
501
+ this._emit('toolCallDone', this.#currentToolCall);
502
+ }
503
+
504
+ this.#currentToolCallIndex = toolCall.index;
505
+ this.#currentToolCall = accumulatedRunStep.step_details.tool_calls[toolCall.index];
506
+ if (this.#currentToolCall) this._emit('toolCallCreated', this.#currentToolCall);
507
+ }
508
+ }
509
+ }
510
+
511
+ this._emit('runStepDelta', event.data.delta, accumulatedRunStep);
512
+ break;
513
+ case 'thread.run.step.completed':
514
+ case 'thread.run.step.failed':
515
+ case 'thread.run.step.cancelled':
516
+ case 'thread.run.step.expired':
517
+ this.#currentRunStepSnapshot = undefined;
518
+ const details = event.data.step_details;
519
+ if (details.type == 'tool_calls') {
520
+ if (this.#currentToolCall) {
521
+ this._emit('toolCallDone', this.#currentToolCall as ToolCall);
522
+ this.#currentToolCall = undefined;
523
+ }
524
+ }
525
+ this._emit('runStepDone', event.data, accumulatedRunStep);
526
+ break;
527
+ case 'thread.run.step.in_progress':
528
+ break;
529
+ }
530
+ }
531
+
532
+ #handleEvent(event: AssistantStreamEvent) {
533
+ this.#events.push(event);
534
+ this._emit('event', event);
535
+ }
536
+
537
+ #accumulateRunStep(event: RunStepStreamEvent): Runs.RunStep {
538
+ switch (event.event) {
539
+ case 'thread.run.step.created':
540
+ this.#runStepSnapshots[event.data.id] = event.data;
541
+ return event.data;
542
+
543
+ case 'thread.run.step.delta':
544
+ let snapshot = this.#runStepSnapshots[event.data.id] as Runs.RunStep;
545
+ if (!snapshot) {
546
+ throw Error('Received a RunStepDelta before creation of a snapshot');
547
+ }
548
+
549
+ let data = event.data;
550
+
551
+ if (data.delta) {
552
+ const accumulated = AssistantStream.accumulateDelta(snapshot, data.delta) as Runs.RunStep;
553
+ this.#runStepSnapshots[event.data.id] = accumulated;
554
+ }
555
+
556
+ return this.#runStepSnapshots[event.data.id] as Runs.RunStep;
557
+
558
+ case 'thread.run.step.completed':
559
+ case 'thread.run.step.failed':
560
+ case 'thread.run.step.cancelled':
561
+ case 'thread.run.step.expired':
562
+ case 'thread.run.step.in_progress':
563
+ this.#runStepSnapshots[event.data.id] = event.data;
564
+ break;
565
+ }
566
+
567
+ if (this.#runStepSnapshots[event.data.id]) return this.#runStepSnapshots[event.data.id] as Runs.RunStep;
568
+ throw new Error('No snapshot available');
569
+ }
570
+
571
+ #accumulateMessage(
572
+ event: AssistantStreamEvent,
573
+ snapshot: Message | undefined,
574
+ ): [Message, MessageContentDelta[]] {
575
+ let newContent: MessageContentDelta[] = [];
576
+
577
+ switch (event.event) {
578
+ case 'thread.message.created':
579
+ //On creation the snapshot is just the initial message
580
+ return [event.data, newContent];
581
+
582
+ case 'thread.message.delta':
583
+ if (!snapshot) {
584
+ throw Error(
585
+ 'Received a delta with no existing snapshot (there should be one from message creation)',
586
+ );
587
+ }
588
+
589
+ let data = event.data;
590
+
591
+ //If this delta does not have content, nothing to process
592
+ if (data.delta.content) {
593
+ for (const contentElement of data.delta.content) {
594
+ if (contentElement.index in snapshot.content) {
595
+ let currentContent = snapshot.content[contentElement.index];
596
+ snapshot.content[contentElement.index] = this.#accumulateContent(
597
+ contentElement,
598
+ currentContent,
599
+ );
600
+ } else {
601
+ snapshot.content[contentElement.index] = contentElement as
602
+ | TextContentBlock
603
+ | ImageFileContentBlock;
604
+ //This is a new element
605
+ newContent.push(contentElement);
606
+ }
607
+ }
608
+ }
609
+
610
+ return [snapshot, newContent];
611
+
612
+ case 'thread.message.in_progress':
613
+ case 'thread.message.completed':
614
+ case 'thread.message.incomplete':
615
+ //No changes on other thread events
616
+ if (snapshot) {
617
+ return [snapshot, newContent];
618
+ } else {
619
+ throw Error('Received thread message event with no existing snapshot');
620
+ }
621
+ }
622
+ throw Error('Tried to accumulate a non-message event');
623
+ }
624
+
625
+ #accumulateContent(
626
+ contentElement: MessageContentDelta,
627
+ currentContent: TextContentBlock | ImageFileContentBlock | undefined,
628
+ ): TextContentBlock | ImageFileContentBlock {
629
+ return AssistantStream.accumulateDelta(currentContent as unknown as Record<any, any>, contentElement) as
630
+ | TextContentBlock
631
+ | ImageFileContentBlock;
632
+ }
633
+
634
+ static accumulateDelta(acc: Record<string, any>, delta: Record<string, any>): Record<string, any> {
635
+ for (const [key, deltaValue] of Object.entries(delta)) {
636
+ if (!acc.hasOwnProperty(key)) {
637
+ acc[key] = deltaValue;
638
+ continue;
639
+ }
640
+
641
+ let accValue = acc[key];
642
+ if (accValue === null || accValue === undefined) {
643
+ acc[key] = deltaValue;
644
+ continue;
645
+ }
646
+
647
+ // We don't accumulate these special properties
648
+ if (key === 'index' || key === 'type') {
649
+ acc[key] = deltaValue;
650
+ continue;
651
+ }
652
+
653
+ // Type-specific accumulation logic
654
+ if (typeof accValue === 'string' && typeof deltaValue === 'string') {
655
+ accValue += deltaValue;
656
+ } else if (typeof accValue === 'number' && typeof deltaValue === 'number') {
657
+ accValue += deltaValue;
658
+ } else if (Core.isObj(accValue) && Core.isObj(deltaValue)) {
659
+ accValue = this.accumulateDelta(accValue as Record<string, any>, deltaValue as Record<string, any>);
660
+ } else if (Array.isArray(accValue) && Array.isArray(deltaValue)) {
661
+ if (accValue.every((x) => typeof x === 'string' || typeof x === 'number')) {
662
+ accValue.push(...deltaValue); // Use spread syntax for efficient addition
663
+ continue;
664
+ }
665
+ } else {
666
+ throw Error(`Unhandled record type: ${key}, deltaValue: ${deltaValue}, accValue: ${accValue}`);
667
+ }
668
+ acc[key] = accValue;
669
+ }
670
+
671
+ return acc;
672
+ }
673
+
674
+ #handleRun(event: RunStreamEvent) {
675
+ this.#currentRunSnapshot = event.data;
676
+ switch (event.event) {
677
+ case 'thread.run.created':
678
+ break;
679
+ case 'thread.run.queued':
680
+ break;
681
+ case 'thread.run.in_progress':
682
+ break;
683
+ case 'thread.run.requires_action':
684
+ case 'thread.run.cancelled':
685
+ case 'thread.run.failed':
686
+ case 'thread.run.completed':
687
+ case 'thread.run.expired':
688
+ this.#finalRun = event.data;
689
+ if (this.#currentToolCall) {
690
+ this._emit('toolCallDone', this.#currentToolCall);
691
+ this.#currentToolCall = undefined;
692
+ }
693
+ break;
694
+ case 'thread.run.cancelling':
695
+ break;
696
+ }
697
+ }
698
+ }