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.
- package/CHANGELOG.md +35 -0
- package/README.md +7 -10
- package/index.d.mts +1 -0
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/index.mjs.map +1 -1
- package/lib/AbstractAssistantStreamRunner.d.ts +74 -0
- package/lib/AbstractAssistantStreamRunner.d.ts.map +1 -0
- package/lib/AbstractAssistantStreamRunner.js +246 -0
- package/lib/AbstractAssistantStreamRunner.js.map +1 -0
- package/lib/AbstractAssistantStreamRunner.mjs +242 -0
- package/lib/AbstractAssistantStreamRunner.mjs.map +1 -0
- package/lib/AssistantStream.d.ts +56 -0
- package/lib/AssistantStream.d.ts.map +1 -0
- package/lib/AssistantStream.js +526 -0
- package/lib/AssistantStream.js.map +1 -0
- package/lib/AssistantStream.mjs +499 -0
- package/lib/AssistantStream.mjs.map +1 -0
- package/lib/ChatCompletionRunFunctions.test.js +35 -0
- package/lib/ChatCompletionRunFunctions.test.js.map +1 -1
- package/lib/ChatCompletionRunFunctions.test.mjs +35 -0
- package/lib/ChatCompletionRunFunctions.test.mjs.map +1 -1
- package/lib/ChatCompletionStream.d.ts.map +1 -1
- package/lib/ChatCompletionStream.js +21 -3
- package/lib/ChatCompletionStream.js.map +1 -1
- package/lib/ChatCompletionStream.mjs +21 -3
- package/lib/ChatCompletionStream.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/audio/speech.d.ts +3 -6
- package/resources/audio/speech.d.ts.map +1 -1
- package/resources/audio/speech.js.map +1 -1
- package/resources/audio/speech.mjs.map +1 -1
- package/resources/audio/transcriptions.d.ts +14 -4
- package/resources/audio/transcriptions.d.ts.map +1 -1
- package/resources/audio/transcriptions.js.map +1 -1
- package/resources/audio/transcriptions.mjs.map +1 -1
- package/resources/audio/translations.d.ts +2 -1
- package/resources/audio/translations.d.ts.map +1 -1
- package/resources/audio/translations.js.map +1 -1
- package/resources/audio/translations.mjs.map +1 -1
- package/resources/beta/assistants/assistants.d.ts +629 -60
- package/resources/beta/assistants/assistants.d.ts.map +1 -1
- package/resources/beta/assistants/assistants.js.map +1 -1
- package/resources/beta/assistants/assistants.mjs.map +1 -1
- package/resources/beta/assistants/index.d.ts +1 -1
- package/resources/beta/assistants/index.d.ts.map +1 -1
- package/resources/beta/assistants/index.js.map +1 -1
- package/resources/beta/assistants/index.mjs.map +1 -1
- package/resources/beta/beta.d.ts +12 -0
- package/resources/beta/beta.d.ts.map +1 -1
- package/resources/beta/beta.js.map +1 -1
- package/resources/beta/beta.mjs.map +1 -1
- package/resources/beta/index.d.ts +2 -2
- package/resources/beta/index.d.ts.map +1 -1
- package/resources/beta/index.js.map +1 -1
- package/resources/beta/index.mjs.map +1 -1
- package/resources/beta/threads/index.d.ts +3 -3
- package/resources/beta/threads/index.d.ts.map +1 -1
- package/resources/beta/threads/index.js +2 -2
- package/resources/beta/threads/index.js.map +1 -1
- package/resources/beta/threads/index.mjs +1 -1
- package/resources/beta/threads/index.mjs.map +1 -1
- package/resources/beta/threads/messages/index.d.ts +1 -1
- package/resources/beta/threads/messages/index.d.ts.map +1 -1
- package/resources/beta/threads/messages/index.js +2 -2
- package/resources/beta/threads/messages/index.js.map +1 -1
- package/resources/beta/threads/messages/index.mjs +1 -1
- package/resources/beta/threads/messages/index.mjs.map +1 -1
- package/resources/beta/threads/messages/messages.d.ts +289 -86
- package/resources/beta/threads/messages/messages.d.ts.map +1 -1
- package/resources/beta/threads/messages/messages.js +5 -5
- package/resources/beta/threads/messages/messages.js.map +1 -1
- package/resources/beta/threads/messages/messages.mjs +3 -3
- package/resources/beta/threads/messages/messages.mjs.map +1 -1
- package/resources/beta/threads/runs/index.d.ts +2 -2
- package/resources/beta/threads/runs/index.d.ts.map +1 -1
- package/resources/beta/threads/runs/index.js.map +1 -1
- package/resources/beta/threads/runs/index.mjs.map +1 -1
- package/resources/beta/threads/runs/runs.d.ts +153 -49
- package/resources/beta/threads/runs/runs.d.ts.map +1 -1
- package/resources/beta/threads/runs/runs.js +14 -7
- package/resources/beta/threads/runs/runs.js.map +1 -1
- package/resources/beta/threads/runs/runs.mjs +14 -7
- package/resources/beta/threads/runs/runs.mjs.map +1 -1
- package/resources/beta/threads/runs/steps.d.ts +219 -5
- package/resources/beta/threads/runs/steps.d.ts.map +1 -1
- package/resources/beta/threads/runs/steps.js.map +1 -1
- package/resources/beta/threads/runs/steps.mjs.map +1 -1
- package/resources/beta/threads/threads.d.ts +144 -22
- package/resources/beta/threads/threads.d.ts.map +1 -1
- package/resources/beta/threads/threads.js +9 -4
- package/resources/beta/threads/threads.js.map +1 -1
- package/resources/beta/threads/threads.mjs +9 -4
- package/resources/beta/threads/threads.mjs.map +1 -1
- package/resources/chat/completions.d.ts +28 -18
- package/resources/chat/completions.d.ts.map +1 -1
- package/resources/chat/completions.js.map +1 -1
- package/resources/chat/completions.mjs.map +1 -1
- package/resources/completions.d.ts +2 -0
- package/resources/completions.d.ts.map +1 -1
- package/resources/completions.js.map +1 -1
- package/resources/completions.mjs.map +1 -1
- package/resources/files.d.ts +4 -4
- package/resources/images.d.ts +6 -3
- package/resources/images.d.ts.map +1 -1
- package/resources/images.js.map +1 -1
- package/resources/images.mjs.map +1 -1
- package/resources/moderations.d.ts +3 -5
- package/resources/moderations.d.ts.map +1 -1
- package/resources/moderations.js +1 -1
- package/resources/moderations.js.map +1 -1
- package/resources/moderations.mjs +1 -1
- package/resources/moderations.mjs.map +1 -1
- package/resources/shared.d.ts +6 -0
- package/resources/shared.d.ts.map +1 -1
- package/src/index.ts +1 -0
- package/src/lib/AbstractAssistantStreamRunner.ts +340 -0
- package/src/lib/AssistantStream.ts +698 -0
- package/src/lib/ChatCompletionRunFunctions.test.ts +52 -1
- package/src/lib/ChatCompletionStream.ts +29 -6
- package/src/resources/audio/speech.ts +3 -6
- package/src/resources/audio/transcriptions.ts +14 -4
- package/src/resources/audio/translations.ts +2 -1
- package/src/resources/beta/assistants/assistants.ts +768 -76
- package/src/resources/beta/assistants/index.ts +9 -0
- package/src/resources/beta/beta.ts +12 -0
- package/src/resources/beta/index.ts +12 -0
- package/src/resources/beta/threads/index.ts +30 -5
- package/src/resources/beta/threads/messages/index.ts +21 -5
- package/src/resources/beta/threads/messages/messages.ts +332 -94
- package/src/resources/beta/threads/runs/index.ts +18 -1
- package/src/resources/beta/threads/runs/runs.ts +225 -60
- package/src/resources/beta/threads/runs/steps.ts +254 -5
- package/src/resources/beta/threads/threads.ts +182 -25
- package/src/resources/chat/completions.ts +28 -18
- package/src/resources/completions.ts +2 -0
- package/src/resources/files.ts +4 -4
- package/src/resources/images.ts +6 -3
- package/src/resources/moderations.ts +3 -5
- package/src/resources/shared.ts +10 -0
- package/src/streaming.ts +31 -0
- package/src/version.ts +1 -1
- package/streaming.d.ts +2 -0
- package/streaming.d.ts.map +1 -1
- package/streaming.js +32 -1
- package/streaming.js.map +1 -1
- package/streaming.mjs +30 -0
- package/streaming.mjs.map +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- 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
|
+
}
|