openai 6.45.0 → 6.47.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 +49 -0
- package/README.md +9 -2
- package/client.d.mts +1 -0
- package/client.d.mts.map +1 -1
- package/client.d.ts +1 -0
- package/client.d.ts.map +1 -1
- package/client.js +11 -5
- package/client.js.map +1 -1
- package/client.mjs +11 -5
- package/client.mjs.map +1 -1
- package/core/streaming.d.mts.map +1 -1
- package/core/streaming.d.ts.map +1 -1
- package/core/streaming.js +38 -6
- package/core/streaming.js.map +1 -1
- package/core/streaming.mjs +38 -6
- package/core/streaming.mjs.map +1 -1
- package/core/uploads.d.mts +1 -1
- package/core/uploads.d.mts.map +1 -1
- package/core/uploads.d.ts +1 -1
- package/core/uploads.d.ts.map +1 -1
- package/core/uploads.js +3 -1
- package/core/uploads.js.map +1 -1
- package/core/uploads.mjs +1 -0
- package/core/uploads.mjs.map +1 -1
- package/helpers/audio.d.mts.map +1 -1
- package/helpers/audio.d.ts.map +1 -1
- package/helpers/audio.js +16 -3
- package/helpers/audio.js.map +1 -1
- package/helpers/audio.mjs +17 -4
- package/helpers/audio.mjs.map +1 -1
- package/helpers/zod.d.mts +20 -7
- package/helpers/zod.d.mts.map +1 -1
- package/helpers/zod.d.ts +20 -7
- package/helpers/zod.d.ts.map +1 -1
- package/helpers/zod.js +24 -12
- package/helpers/zod.js.map +1 -1
- package/helpers/zod.mjs +24 -12
- package/helpers/zod.mjs.map +1 -1
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +2 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/internal/uploads.d.mts +26 -1
- package/internal/uploads.d.mts.map +1 -1
- package/internal/uploads.d.ts +26 -1
- package/internal/uploads.d.ts.map +1 -1
- package/internal/uploads.js +169 -1
- package/internal/uploads.js.map +1 -1
- package/internal/uploads.mjs +169 -2
- package/internal/uploads.mjs.map +1 -1
- package/lib/AbstractChatCompletionRunner.d.mts +3 -3
- package/lib/AbstractChatCompletionRunner.d.mts.map +1 -1
- package/lib/AbstractChatCompletionRunner.d.ts +3 -3
- package/lib/AbstractChatCompletionRunner.d.ts.map +1 -1
- package/lib/AbstractChatCompletionRunner.js +18 -3
- package/lib/AbstractChatCompletionRunner.js.map +1 -1
- package/lib/AbstractChatCompletionRunner.mjs +18 -3
- package/lib/AbstractChatCompletionRunner.mjs.map +1 -1
- package/lib/AssistantStream.d.mts.map +1 -1
- package/lib/AssistantStream.d.ts.map +1 -1
- package/lib/AssistantStream.js +4 -3
- package/lib/AssistantStream.js.map +1 -1
- package/lib/AssistantStream.mjs +4 -3
- package/lib/AssistantStream.mjs.map +1 -1
- package/lib/ChatCompletionRunner.d.mts +26 -2
- package/lib/ChatCompletionRunner.d.mts.map +1 -1
- package/lib/ChatCompletionRunner.d.ts +26 -2
- package/lib/ChatCompletionRunner.d.ts.map +1 -1
- package/lib/ChatCompletionRunner.js.map +1 -1
- package/lib/ChatCompletionRunner.mjs.map +1 -1
- package/lib/ChatCompletionStream.d.mts +14 -1
- package/lib/ChatCompletionStream.d.mts.map +1 -1
- package/lib/ChatCompletionStream.d.ts +14 -1
- package/lib/ChatCompletionStream.d.ts.map +1 -1
- package/lib/ChatCompletionStream.js +75 -16
- package/lib/ChatCompletionStream.js.map +1 -1
- package/lib/ChatCompletionStream.mjs +74 -16
- package/lib/ChatCompletionStream.mjs.map +1 -1
- package/lib/ChatCompletionStreamingRunner.d.mts +25 -2
- package/lib/ChatCompletionStreamingRunner.d.mts.map +1 -1
- package/lib/ChatCompletionStreamingRunner.d.ts +25 -2
- package/lib/ChatCompletionStreamingRunner.d.ts.map +1 -1
- package/lib/ChatCompletionStreamingRunner.js +77 -0
- package/lib/ChatCompletionStreamingRunner.js.map +1 -1
- package/lib/ChatCompletionStreamingRunner.mjs +78 -1
- package/lib/ChatCompletionStreamingRunner.mjs.map +1 -1
- package/lib/EventStream.d.mts +13 -0
- package/lib/EventStream.d.mts.map +1 -1
- package/lib/EventStream.d.ts +13 -0
- package/lib/EventStream.d.ts.map +1 -1
- package/lib/EventStream.js +107 -2
- package/lib/EventStream.js.map +1 -1
- package/lib/EventStream.mjs +107 -2
- package/lib/EventStream.mjs.map +1 -1
- package/lib/RunnableFunction.d.mts +38 -21
- package/lib/RunnableFunction.d.mts.map +1 -1
- package/lib/RunnableFunction.d.ts +38 -21
- package/lib/RunnableFunction.d.ts.map +1 -1
- package/lib/RunnableFunction.js.map +1 -1
- package/lib/RunnableFunction.mjs.map +1 -1
- package/lib/parser.d.mts +2 -2
- package/lib/parser.d.mts.map +1 -1
- package/lib/parser.d.ts +2 -2
- package/lib/parser.d.ts.map +1 -1
- package/lib/parser.js.map +1 -1
- package/lib/parser.mjs.map +1 -1
- package/lib/responses/ResponseAccumulator.d.mts +6 -1
- package/lib/responses/ResponseAccumulator.d.mts.map +1 -1
- package/lib/responses/ResponseAccumulator.d.ts +6 -1
- package/lib/responses/ResponseAccumulator.d.ts.map +1 -1
- package/lib/responses/ResponseAccumulator.js +1 -0
- package/lib/responses/ResponseAccumulator.js.map +1 -1
- package/lib/responses/ResponseAccumulator.mjs +1 -0
- package/lib/responses/ResponseAccumulator.mjs.map +1 -1
- package/lib/responses/ResponseInputItems.d.mts.map +1 -1
- package/lib/responses/ResponseInputItems.d.ts.map +1 -1
- package/lib/responses/ResponseInputItems.js +2 -0
- package/lib/responses/ResponseInputItems.js.map +1 -1
- package/lib/responses/ResponseInputItems.mjs +2 -0
- package/lib/responses/ResponseInputItems.mjs.map +1 -1
- package/lib/responses/ResponseStream.d.mts +6 -1
- package/lib/responses/ResponseStream.d.mts.map +1 -1
- package/lib/responses/ResponseStream.d.ts +6 -1
- package/lib/responses/ResponseStream.d.ts.map +1 -1
- package/lib/responses/ResponseStream.js +21 -0
- package/lib/responses/ResponseStream.js.map +1 -1
- package/lib/responses/ResponseStream.mjs +21 -0
- package/lib/responses/ResponseStream.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/admin/organization/projects/api-keys.d.mts +12 -0
- package/resources/admin/organization/projects/api-keys.d.mts.map +1 -1
- package/resources/admin/organization/projects/api-keys.d.ts +12 -0
- package/resources/admin/organization/projects/api-keys.d.ts.map +1 -1
- package/resources/beta/assistants.d.mts +12 -26
- package/resources/beta/assistants.d.mts.map +1 -1
- package/resources/beta/assistants.d.ts +12 -26
- package/resources/beta/assistants.d.ts.map +1 -1
- package/resources/beta/beta.d.mts +6 -1
- package/resources/beta/beta.d.mts.map +1 -1
- package/resources/beta/beta.d.ts +6 -1
- package/resources/beta/beta.d.ts.map +1 -1
- package/resources/beta/beta.js +4 -0
- package/resources/beta/beta.js.map +1 -1
- package/resources/beta/beta.mjs +4 -0
- package/resources/beta/beta.mjs.map +1 -1
- package/resources/beta/index.d.mts +1 -0
- package/resources/beta/index.d.mts.map +1 -1
- package/resources/beta/index.d.ts +1 -0
- package/resources/beta/index.d.ts.map +1 -1
- package/resources/beta/index.js +5 -3
- package/resources/beta/index.js.map +1 -1
- package/resources/beta/index.mjs +1 -0
- package/resources/beta/index.mjs.map +1 -1
- package/resources/beta/responses/index.d.mts +6 -0
- package/resources/beta/responses/index.d.mts.map +1 -0
- package/resources/beta/responses/index.d.ts +6 -0
- package/resources/beta/responses/index.d.ts.map +1 -0
- package/resources/beta/responses/index.js +11 -0
- package/resources/beta/responses/index.js.map +1 -0
- package/resources/beta/responses/index.mjs +5 -0
- package/resources/beta/responses/index.mjs.map +1 -0
- package/resources/beta/responses/input-items.d.mts +69 -0
- package/resources/beta/responses/input-items.d.mts.map +1 -0
- package/resources/beta/responses/input-items.d.ts +69 -0
- package/resources/beta/responses/input-items.d.ts.map +1 -0
- package/resources/beta/responses/input-items.js +37 -0
- package/resources/beta/responses/input-items.js.map +1 -0
- package/resources/beta/responses/input-items.mjs +33 -0
- package/resources/beta/responses/input-items.mjs.map +1 -0
- package/resources/beta/responses/input-tokens.d.mts +191 -0
- package/resources/beta/responses/input-tokens.d.mts.map +1 -0
- package/resources/beta/responses/input-tokens.d.ts +191 -0
- package/resources/beta/responses/input-tokens.d.ts.map +1 -0
- package/resources/beta/responses/input-tokens.js +34 -0
- package/resources/beta/responses/input-tokens.js.map +1 -0
- package/resources/beta/responses/input-tokens.mjs +30 -0
- package/resources/beta/responses/input-tokens.mjs.map +1 -0
- package/resources/beta/responses/internal-base.d.mts +71 -0
- package/resources/beta/responses/internal-base.d.mts.map +1 -0
- package/resources/beta/responses/internal-base.d.ts +71 -0
- package/resources/beta/responses/internal-base.d.ts.map +1 -0
- package/resources/beta/responses/internal-base.js +48 -0
- package/resources/beta/responses/internal-base.js.map +1 -0
- package/resources/beta/responses/internal-base.mjs +42 -0
- package/resources/beta/responses/internal-base.mjs.map +1 -0
- package/resources/beta/responses/responses.d.mts +9986 -0
- package/resources/beta/responses/responses.d.mts.map +1 -0
- package/resources/beta/responses/responses.d.ts +9986 -0
- package/resources/beta/responses/responses.d.ts.map +1 -0
- package/resources/beta/responses/responses.js +121 -0
- package/resources/beta/responses/responses.js.map +1 -0
- package/resources/beta/responses/responses.mjs +116 -0
- package/resources/beta/responses/responses.mjs.map +1 -0
- package/resources/beta/responses/ws-base.d.mts +106 -0
- package/resources/beta/responses/ws-base.d.mts.map +1 -0
- package/resources/beta/responses/ws-base.d.ts +106 -0
- package/resources/beta/responses/ws-base.d.ts.map +1 -0
- package/resources/beta/responses/ws-base.js +476 -0
- package/resources/beta/responses/ws-base.js.map +1 -0
- package/resources/beta/responses/ws-base.mjs +472 -0
- package/resources/beta/responses/ws-base.mjs.map +1 -0
- package/resources/beta/responses/ws.d.mts +13 -0
- package/resources/beta/responses/ws.d.mts.map +1 -0
- package/resources/beta/responses/ws.d.ts +13 -0
- package/resources/beta/responses/ws.d.ts.map +1 -0
- package/resources/beta/responses/ws.js +31 -0
- package/resources/beta/responses/ws.js.map +1 -0
- package/resources/beta/responses/ws.mjs +26 -0
- package/resources/beta/responses/ws.mjs.map +1 -0
- package/resources/beta/responses.d.mts +2 -0
- package/resources/beta/responses.d.mts.map +1 -0
- package/resources/beta/responses.d.ts +2 -0
- package/resources/beta/responses.d.ts.map +1 -0
- package/resources/beta/responses.js +6 -0
- package/resources/beta/responses.js.map +1 -0
- package/resources/beta/responses.mjs +3 -0
- package/resources/beta/responses.mjs.map +1 -0
- package/resources/beta/threads/runs/runs.d.mts +6 -13
- package/resources/beta/threads/runs/runs.d.mts.map +1 -1
- package/resources/beta/threads/runs/runs.d.ts +6 -13
- package/resources/beta/threads/runs/runs.d.ts.map +1 -1
- package/resources/beta/threads/runs/runs.js.map +1 -1
- package/resources/beta/threads/runs/runs.mjs.map +1 -1
- package/resources/chat/completions/completions.d.mts +170 -20
- package/resources/chat/completions/completions.d.mts.map +1 -1
- package/resources/chat/completions/completions.d.ts +170 -20
- package/resources/chat/completions/completions.d.ts.map +1 -1
- package/resources/chat/completions/completions.js.map +1 -1
- package/resources/chat/completions/completions.mjs.map +1 -1
- package/resources/completions.d.mts +4 -0
- package/resources/completions.d.mts.map +1 -1
- package/resources/completions.d.ts +4 -0
- package/resources/completions.d.ts.map +1 -1
- package/resources/conversations/conversations.d.mts +19 -0
- package/resources/conversations/conversations.d.mts.map +1 -1
- package/resources/conversations/conversations.d.ts +19 -0
- package/resources/conversations/conversations.d.ts.map +1 -1
- package/resources/conversations/conversations.js.map +1 -1
- package/resources/conversations/conversations.mjs.map +1 -1
- package/resources/conversations/items.d.mts +45 -1
- package/resources/conversations/items.d.mts.map +1 -1
- package/resources/conversations/items.d.ts +45 -1
- package/resources/conversations/items.d.ts.map +1 -1
- package/resources/evals/runs/runs.d.mts +66 -143
- package/resources/evals/runs/runs.d.mts.map +1 -1
- package/resources/evals/runs/runs.d.ts +66 -143
- package/resources/evals/runs/runs.d.ts.map +1 -1
- package/resources/evals/runs/runs.js.map +1 -1
- package/resources/evals/runs/runs.mjs.map +1 -1
- package/resources/graders/grader-models.d.mts +6 -13
- package/resources/graders/grader-models.d.mts.map +1 -1
- package/resources/graders/grader-models.d.ts +6 -13
- package/resources/graders/grader-models.d.ts.map +1 -1
- package/resources/realtime/calls.d.mts +1 -1
- package/resources/realtime/calls.d.mts.map +1 -1
- package/resources/realtime/calls.d.ts +1 -1
- package/resources/realtime/calls.d.ts.map +1 -1
- package/resources/realtime/client-secrets.d.mts +5 -1
- package/resources/realtime/client-secrets.d.mts.map +1 -1
- package/resources/realtime/client-secrets.d.ts +5 -1
- package/resources/realtime/client-secrets.d.ts.map +1 -1
- package/resources/realtime/realtime.d.mts +9 -1
- package/resources/realtime/realtime.d.mts.map +1 -1
- package/resources/realtime/realtime.d.ts +9 -1
- package/resources/realtime/realtime.d.ts.map +1 -1
- package/resources/realtime/realtime.js.map +1 -1
- package/resources/realtime/realtime.mjs.map +1 -1
- package/resources/responses/input-tokens.d.mts +7 -1
- package/resources/responses/input-tokens.d.mts.map +1 -1
- package/resources/responses/input-tokens.d.ts +7 -1
- package/resources/responses/input-tokens.d.ts.map +1 -1
- package/resources/responses/responses.d.mts +819 -26
- package/resources/responses/responses.d.mts.map +1 -1
- package/resources/responses/responses.d.ts +819 -26
- package/resources/responses/responses.d.ts.map +1 -1
- package/resources/responses/responses.js +1 -1
- package/resources/responses/responses.js.map +1 -1
- package/resources/responses/responses.mjs +1 -1
- package/resources/responses/responses.mjs.map +1 -1
- package/resources/shared.d.mts +20 -28
- package/resources/shared.d.mts.map +1 -1
- package/resources/shared.d.ts +20 -28
- package/resources/shared.d.ts.map +1 -1
- package/src/client.ts +13 -5
- package/src/core/streaming.ts +36 -8
- package/src/core/uploads.ts +6 -1
- package/src/helpers/audio.ts +18 -4
- package/src/helpers/zod.ts +62 -23
- package/src/index.ts +1 -1
- package/src/internal/uploads.ts +214 -4
- package/src/lib/AbstractChatCompletionRunner.ts +32 -9
- package/src/lib/AssistantStream.ts +4 -3
- package/src/lib/ChatCompletionRunner.ts +46 -6
- package/src/lib/ChatCompletionStream.ts +115 -17
- package/src/lib/ChatCompletionStreamingRunner.ts +144 -7
- package/src/lib/EventStream.ts +114 -4
- package/src/lib/RunnableFunction.ts +60 -31
- package/src/lib/parser.ts +4 -0
- package/src/lib/responses/ResponseAccumulator.ts +10 -1
- package/src/lib/responses/ResponseInputItems.ts +2 -0
- package/src/lib/responses/ResponseStream.ts +29 -1
- package/src/resources/admin/organization/projects/api-keys.ts +15 -1
- package/src/resources/beta/assistants.ts +12 -26
- package/src/resources/beta/beta.ts +330 -2
- package/src/resources/beta/index.ts +163 -0
- package/src/resources/beta/responses/index.ts +169 -0
- package/src/resources/beta/responses/input-items.ts +105 -0
- package/src/resources/beta/responses/input-tokens.ts +245 -0
- package/src/resources/beta/responses/internal-base.ts +117 -0
- package/src/resources/beta/responses/responses.ts +12633 -0
- package/src/resources/beta/responses/ws-base.ts +615 -0
- package/src/resources/beta/responses/ws.ts +38 -0
- package/src/resources/beta/responses.ts +3 -0
- package/src/resources/beta/threads/runs/runs.ts +6 -13
- package/src/resources/chat/completions/completions.ts +237 -23
- package/src/resources/completions.ts +5 -0
- package/src/resources/conversations/conversations.ts +21 -0
- package/src/resources/conversations/items.ts +56 -0
- package/src/resources/evals/runs/runs.ts +66 -143
- package/src/resources/graders/grader-models.ts +6 -13
- package/src/resources/realtime/calls.ts +2 -0
- package/src/resources/realtime/client-secrets.ts +7 -0
- package/src/resources/realtime/realtime.ts +12 -0
- package/src/resources/responses/input-tokens.ts +8 -0
- package/src/resources/responses/responses.ts +953 -17
- package/src/resources/shared.ts +23 -27
- 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
package/src/internal/uploads.ts
CHANGED
|
@@ -1,16 +1,61 @@
|
|
|
1
1
|
import { type RequestOptions } from './request-options';
|
|
2
2
|
import type { FilePropertyBag, Fetch } from './builtin-types';
|
|
3
3
|
import type { OpenAI } from '../client';
|
|
4
|
-
import {
|
|
4
|
+
import { buildHeaders } from './headers';
|
|
5
|
+
import { ReadableStreamFrom, ReadableStreamToAsyncIterable } from './shims';
|
|
6
|
+
import type { ReadableStream } from './shim-types';
|
|
7
|
+
import { encodeUTF8 } from './utils/bytes';
|
|
5
8
|
|
|
6
9
|
export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob | DataView;
|
|
7
10
|
type FsReadStream = AsyncIterable<Uint8Array> & { path: string | { toString(): string } };
|
|
8
11
|
|
|
12
|
+
export type StreamingFileInput = AsyncIterable<BlobPart> | ReadableStream<BlobPart>;
|
|
13
|
+
|
|
14
|
+
const brand_privateStreamingFile = /* @__PURE__ */ Symbol('brand.privateStreamingFile');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A file whose contents are read lazily while the multipart request is sent.
|
|
18
|
+
* Create one with {@link toStreamingFile} when buffering an upload into a `File` is undesirable.
|
|
19
|
+
*/
|
|
20
|
+
export interface StreamingFile {
|
|
21
|
+
/** Brand check, prevent users from creating a StreamingFile without a filename. */
|
|
22
|
+
readonly [brand_privateStreamingFile]: true;
|
|
23
|
+
readonly data: StreamingFileInput;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly type?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wrap a stream as an uploadable file without reading it into memory.
|
|
30
|
+
*
|
|
31
|
+
* Unlike {@link toFile}, this helper does not create a web `File`, because the `File` constructor
|
|
32
|
+
* must consume all of its contents up front. The stream is instead encoded lazily as multipart
|
|
33
|
+
* form data when the request is sent.
|
|
34
|
+
*/
|
|
35
|
+
export function toStreamingFile(
|
|
36
|
+
data: StreamingFileInput,
|
|
37
|
+
name: string,
|
|
38
|
+
options?: Pick<FilePropertyBag, 'type'>,
|
|
39
|
+
): StreamingFile {
|
|
40
|
+
if (!name) {
|
|
41
|
+
throw new TypeError('toStreamingFile requires a non-empty file name');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
[brand_privateStreamingFile]: true,
|
|
46
|
+
data,
|
|
47
|
+
name,
|
|
48
|
+
...(options?.type ? { type: options.type } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
9
52
|
// https://github.com/oven-sh/bun/issues/5980
|
|
10
53
|
interface BunFile extends Blob {
|
|
11
54
|
readonly name?: string | undefined;
|
|
12
55
|
}
|
|
13
56
|
|
|
57
|
+
type NamedBlob = Blob & { readonly name?: string | undefined };
|
|
58
|
+
|
|
14
59
|
export const checkFileSupport = () => {
|
|
15
60
|
if (typeof File === 'undefined') {
|
|
16
61
|
const { process } = globalThis as any;
|
|
@@ -34,7 +79,15 @@ export const checkFileSupport = () => {
|
|
|
34
79
|
* For convenience, you can also pass a fetch Response, or in Node,
|
|
35
80
|
* the result of fs.createReadStream().
|
|
36
81
|
*/
|
|
37
|
-
export type Uploadable =
|
|
82
|
+
export type Uploadable =
|
|
83
|
+
| File
|
|
84
|
+
| Response
|
|
85
|
+
| FsReadStream
|
|
86
|
+
| BunFile
|
|
87
|
+
| NamedBlob
|
|
88
|
+
| AsyncIterable<BlobPart>
|
|
89
|
+
| ReadableStream<BlobPart>
|
|
90
|
+
| StreamingFile;
|
|
38
91
|
|
|
39
92
|
/**
|
|
40
93
|
* Construct a `File` instance. This is used to ensure a helpful error is thrown
|
|
@@ -78,6 +131,10 @@ export const maybeMultipartFormRequestOptions = async (
|
|
|
78
131
|
): Promise<RequestOptions> => {
|
|
79
132
|
if (!hasUploadableValue(opts.body)) return opts;
|
|
80
133
|
|
|
134
|
+
if (hasStreamingUploadableValue(opts.body)) {
|
|
135
|
+
return createStreamingFormRequestOptions(opts);
|
|
136
|
+
}
|
|
137
|
+
|
|
81
138
|
return { ...opts, body: await createForm(opts.body, fetch) };
|
|
82
139
|
};
|
|
83
140
|
|
|
@@ -87,6 +144,10 @@ export const multipartFormRequestOptions = async (
|
|
|
87
144
|
opts: MultipartFormRequestOptions,
|
|
88
145
|
fetch: OpenAI | Fetch,
|
|
89
146
|
): Promise<RequestOptions> => {
|
|
147
|
+
if (hasStreamingUploadableValue(opts.body)) {
|
|
148
|
+
return createStreamingFormRequestOptions(opts);
|
|
149
|
+
}
|
|
150
|
+
|
|
90
151
|
return { ...opts, body: await createForm(opts.body, fetch) };
|
|
91
152
|
};
|
|
92
153
|
|
|
@@ -138,12 +199,36 @@ export const createForm = async <T = Record<string, unknown>>(
|
|
|
138
199
|
|
|
139
200
|
// We check for Blob not File because Bun.File doesn't inherit from File,
|
|
140
201
|
// but they both inherit from Blob and have a `name` property at runtime.
|
|
141
|
-
const isNamedBlob = (value: unknown) => value instanceof Blob && 'name' in value;
|
|
202
|
+
const isNamedBlob = (value: unknown): value is NamedBlob => value instanceof Blob && 'name' in value;
|
|
203
|
+
|
|
204
|
+
const isReadableStream = (value: unknown): value is ReadableStream<BlobPart> =>
|
|
205
|
+
typeof value === 'object' &&
|
|
206
|
+
value !== null &&
|
|
207
|
+
'getReader' in value &&
|
|
208
|
+
typeof value.getReader === 'function';
|
|
209
|
+
|
|
210
|
+
const isStreamingFile = (value: unknown): value is StreamingFile =>
|
|
211
|
+
typeof value === 'object' && value !== null && brand_privateStreamingFile in value;
|
|
142
212
|
|
|
143
213
|
const isUploadable = (value: unknown) =>
|
|
144
214
|
typeof value === 'object' &&
|
|
145
215
|
value !== null &&
|
|
146
|
-
(value instanceof Response ||
|
|
216
|
+
(value instanceof Response ||
|
|
217
|
+
isAsyncIterable(value) ||
|
|
218
|
+
isReadableStream(value) ||
|
|
219
|
+
isStreamingFile(value) ||
|
|
220
|
+
isNamedBlob(value));
|
|
221
|
+
|
|
222
|
+
const hasStreamingUploadableValue = (value: unknown): boolean => {
|
|
223
|
+
if (isStreamingFile(value) || isAsyncIterable(value) || isReadableStream(value)) return true;
|
|
224
|
+
if (Array.isArray(value)) return value.some(hasStreamingUploadableValue);
|
|
225
|
+
if (value && typeof value === 'object' && !isNamedBlob(value) && !(value instanceof Response)) {
|
|
226
|
+
for (const k in value) {
|
|
227
|
+
if (hasStreamingUploadableValue((value as Record<string, unknown>)[k])) return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
};
|
|
147
232
|
|
|
148
233
|
const hasUploadableValue = (value: unknown): boolean => {
|
|
149
234
|
if (isUploadable(value)) return true;
|
|
@@ -156,6 +241,131 @@ const hasUploadableValue = (value: unknown): boolean => {
|
|
|
156
241
|
return false;
|
|
157
242
|
};
|
|
158
243
|
|
|
244
|
+
type FormEntry = { key: string; value: unknown };
|
|
245
|
+
|
|
246
|
+
const createStreamingFormRequestOptions = (opts: RequestOptions): RequestOptions => {
|
|
247
|
+
const boundary = `openai-${Math.random().toString(36).slice(2)}`;
|
|
248
|
+
const body = ReadableStreamFrom(iterateMultipartBody(opts.body, boundary));
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
...opts,
|
|
252
|
+
body,
|
|
253
|
+
headers: buildHeaders([{ 'content-type': `multipart/form-data; boundary=${boundary}` }, opts.headers]),
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
async function* iterateMultipartBody(body: unknown, boundary: string): AsyncGenerator<Uint8Array> {
|
|
258
|
+
for await (const { key, value } of iterateFormEntries(body)) {
|
|
259
|
+
yield encodeUTF8(`--${boundary}\r\n`);
|
|
260
|
+
if (isUploadable(value)) {
|
|
261
|
+
const filename = getStreamingFileName(value);
|
|
262
|
+
const type = getStreamingFileType(value);
|
|
263
|
+
yield encodeUTF8(
|
|
264
|
+
`Content-Disposition: form-data; name="${escapeHeaderValue(key)}"; filename="${escapeHeaderValue(
|
|
265
|
+
filename,
|
|
266
|
+
)}"\r\n` + `Content-Type: ${type}\r\n\r\n`,
|
|
267
|
+
);
|
|
268
|
+
yield* iterateBytes(getStreamingFileData(value));
|
|
269
|
+
} else {
|
|
270
|
+
yield encodeUTF8(
|
|
271
|
+
`Content-Disposition: form-data; name="${escapeHeaderValue(key)}"\r\n\r\n${String(value)}`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
yield encodeUTF8('\r\n');
|
|
275
|
+
}
|
|
276
|
+
yield encodeUTF8(`--${boundary}--\r\n`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function* iterateFormEntries(body: unknown): AsyncGenerator<FormEntry> {
|
|
280
|
+
if (!body || typeof body !== 'object') return;
|
|
281
|
+
|
|
282
|
+
for (const [key, value] of Object.entries(body)) {
|
|
283
|
+
yield* iterateFormValue(key, value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function* iterateFormValue(key: string, value: unknown): AsyncGenerator<FormEntry> {
|
|
288
|
+
if (value === undefined) return;
|
|
289
|
+
if (value == null) {
|
|
290
|
+
throw new TypeError(
|
|
291
|
+
`Received null for "${key}"; to pass null in FormData, you must use the string 'null'`,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (
|
|
296
|
+
typeof value === 'string' ||
|
|
297
|
+
typeof value === 'number' ||
|
|
298
|
+
typeof value === 'boolean' ||
|
|
299
|
+
isUploadable(value)
|
|
300
|
+
) {
|
|
301
|
+
yield { key, value };
|
|
302
|
+
} else if (Array.isArray(value)) {
|
|
303
|
+
for (const entry of value) {
|
|
304
|
+
yield* iterateFormValue(key + '[]', entry);
|
|
305
|
+
}
|
|
306
|
+
} else if (typeof value === 'object') {
|
|
307
|
+
for (const [name, prop] of Object.entries(value)) {
|
|
308
|
+
yield* iterateFormValue(`${key}[${name}]`, prop);
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
throw new TypeError(
|
|
312
|
+
`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function getStreamingFileName(value: Uploadable): string {
|
|
318
|
+
return isStreamingFile(value) ? value.name : getName(value) ?? 'unknown_file';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function getStreamingFileType(value: Uploadable): string {
|
|
322
|
+
if (isStreamingFile(value)) return value.type || 'application/octet-stream';
|
|
323
|
+
if (isNamedBlob(value) && value.type) return value.type;
|
|
324
|
+
if (value instanceof Response) return value.headers.get('content-type') || 'application/octet-stream';
|
|
325
|
+
return 'application/octet-stream';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function getStreamingFileData(value: Uploadable): unknown {
|
|
329
|
+
if (isStreamingFile(value)) return value.data;
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function* iterateBytes(value: unknown): AsyncGenerator<Uint8Array> {
|
|
334
|
+
if (typeof value === 'string') {
|
|
335
|
+
yield encodeUTF8(value);
|
|
336
|
+
} else if (ArrayBuffer.isView(value)) {
|
|
337
|
+
yield new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
338
|
+
} else if (value instanceof ArrayBuffer) {
|
|
339
|
+
yield new Uint8Array(value);
|
|
340
|
+
} else if (value instanceof Response) {
|
|
341
|
+
if (value.body) {
|
|
342
|
+
yield* iterateBytes(value.body);
|
|
343
|
+
} else {
|
|
344
|
+
yield* iterateBytes(await value.blob());
|
|
345
|
+
}
|
|
346
|
+
} else if (value instanceof Blob) {
|
|
347
|
+
if (typeof value.stream === 'function') {
|
|
348
|
+
yield* iterateBytes(value.stream());
|
|
349
|
+
} else {
|
|
350
|
+
yield new Uint8Array(await value.arrayBuffer());
|
|
351
|
+
}
|
|
352
|
+
} else if (isReadableStream(value)) {
|
|
353
|
+
for await (const chunk of ReadableStreamToAsyncIterable<unknown>(value)) {
|
|
354
|
+
yield* iterateBytes(chunk);
|
|
355
|
+
}
|
|
356
|
+
} else if (isAsyncIterable(value)) {
|
|
357
|
+
for await (const chunk of value) {
|
|
358
|
+
yield* iterateBytes(chunk);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
throw new TypeError(`Invalid streaming file chunk: ${String(value)}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function escapeHeaderValue(value: string): string {
|
|
366
|
+
return value.replace(/["\\\r\n]/g, (character) => encodeURIComponent(character));
|
|
367
|
+
}
|
|
368
|
+
|
|
159
369
|
const addFormValue = async (form: FormData, key: string, value: unknown): Promise<void> => {
|
|
160
370
|
if (value === undefined) return;
|
|
161
371
|
if (value == null) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAIError } from '../error';
|
|
2
2
|
import type OpenAI from '../index';
|
|
3
3
|
import type { RequestOptions } from '../internal/request-options';
|
|
4
|
+
import { uuid4 } from '../internal/utils/uuid';
|
|
4
5
|
import { isAutoParsableTool, parseChatCompletion } from '../lib/parser';
|
|
5
6
|
import type {
|
|
6
7
|
ChatCompletion,
|
|
@@ -14,10 +15,15 @@ import type {
|
|
|
14
15
|
ParsedChatCompletion,
|
|
15
16
|
} from '../resources/chat/completions';
|
|
16
17
|
import type { CompletionUsage } from '../resources/completions';
|
|
17
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
ChatCompletionRunner,
|
|
20
|
+
ChatCompletionToolRunnerParamsWithContext,
|
|
21
|
+
ChatCompletionToolRunnerParamsWithoutContext,
|
|
22
|
+
} from './ChatCompletionRunner';
|
|
18
23
|
import type {
|
|
19
24
|
ChatCompletionStreamingRunner,
|
|
20
|
-
|
|
25
|
+
ChatCompletionStreamingToolRunnerParamsWithContext,
|
|
26
|
+
ChatCompletionStreamingToolRunnerParamsWithoutContext,
|
|
21
27
|
} from './ChatCompletionStreamingRunner';
|
|
22
28
|
import { isAssistantMessage, isToolMessage } from './chatCompletionUtils';
|
|
23
29
|
import { BaseEvents, EventStream } from './EventStream';
|
|
@@ -30,6 +36,19 @@ import {
|
|
|
30
36
|
|
|
31
37
|
const DEFAULT_MAX_CHAT_COMPLETIONS = 10;
|
|
32
38
|
|
|
39
|
+
function normalizeToolCallIds(chatCompletion: ChatCompletion): void {
|
|
40
|
+
for (const choice of chatCompletion.choices) {
|
|
41
|
+
for (const toolCall of choice.message.tool_calls ?? []) {
|
|
42
|
+
// Some OpenAI-compatible providers omit tool call IDs or return an empty string.
|
|
43
|
+
// Generate a unique ID before the completion is stored or emitted so the assistant
|
|
44
|
+
// tool call and its result message always reference the same value.
|
|
45
|
+
if (!toolCall.id) {
|
|
46
|
+
toolCall.id = `call_${uuid4()}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
33
52
|
export interface ChatCompletionRunnerContext {
|
|
34
53
|
messages: ChatCompletionMessageParam[];
|
|
35
54
|
abort(): void;
|
|
@@ -58,6 +77,7 @@ export class AbstractChatCompletionRunner<
|
|
|
58
77
|
this: AbstractChatCompletionRunner<AbstractChatCompletionRunnerEvents, ParsedT>,
|
|
59
78
|
chatCompletion: ParsedChatCompletion<ParsedT>,
|
|
60
79
|
): ParsedChatCompletion<ParsedT> {
|
|
80
|
+
normalizeToolCallIds(chatCompletion);
|
|
61
81
|
this._chatCompletions.push(chatCompletion);
|
|
62
82
|
this._emit('chatCompletion', chatCompletion);
|
|
63
83
|
const message = chatCompletion.choices[0]?.message;
|
|
@@ -270,16 +290,19 @@ export class AbstractChatCompletionRunner<
|
|
|
270
290
|
return await this._createChatCompletion(client, params, options);
|
|
271
291
|
}
|
|
272
292
|
|
|
273
|
-
protected async _runTools<FunctionsArgs extends BaseFunctionsArgs>(
|
|
293
|
+
protected async _runTools<FunctionsArgs extends BaseFunctionsArgs, ToolContext>(
|
|
274
294
|
client: OpenAI,
|
|
275
295
|
params:
|
|
276
|
-
|
|
|
277
|
-
|
|
|
296
|
+
| ChatCompletionToolRunnerParamsWithContext<FunctionsArgs, ToolContext>
|
|
297
|
+
| ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs>
|
|
298
|
+
| ChatCompletionStreamingToolRunnerParamsWithContext<FunctionsArgs, ToolContext>
|
|
299
|
+
| ChatCompletionStreamingToolRunnerParamsWithoutContext<FunctionsArgs>,
|
|
278
300
|
runner: ChatCompletionRunner<any> | ChatCompletionStreamingRunner<any>,
|
|
279
301
|
options?: RunnerOptions,
|
|
280
302
|
) {
|
|
281
303
|
const role = 'tool' as const;
|
|
282
|
-
const { tool_choice = 'auto', stream, ...restParams } = params;
|
|
304
|
+
const { tool_choice = 'auto', stream, toolContext: inputToolContext, ...restParams } = params;
|
|
305
|
+
const toolContext = inputToolContext as ToolContext;
|
|
283
306
|
const singleFunctionToCall =
|
|
284
307
|
typeof tool_choice !== 'string' && tool_choice.type === 'function' && tool_choice?.function?.name;
|
|
285
308
|
const { maxChatCompletions = DEFAULT_MAX_CHAT_COMPLETIONS, afterCompletion } = options || {};
|
|
@@ -307,7 +330,7 @@ export class AbstractChatCompletionRunner<
|
|
|
307
330
|
return tool as any as RunnableToolFunction<any>;
|
|
308
331
|
});
|
|
309
332
|
|
|
310
|
-
const functionsByName: Record<string, RunnableFunction<any>> = {};
|
|
333
|
+
const functionsByName: Record<string, RunnableFunction<any, ToolContext>> = {};
|
|
311
334
|
for (const f of inputTools) {
|
|
312
335
|
if (f.type === 'function') {
|
|
313
336
|
functionsByName[f.function.name || f.function.function.name] = f.function;
|
|
@@ -374,9 +397,9 @@ export class AbstractChatCompletionRunner<
|
|
|
374
397
|
const content = error instanceof Error ? error.message : String(error);
|
|
375
398
|
return { message: { role, tool_call_id, content }, functionCalled: false };
|
|
376
399
|
}
|
|
377
|
-
rawContent = await fn.function(parsed, runner);
|
|
400
|
+
rawContent = await fn.function(parsed, runner, toolContext);
|
|
378
401
|
} else {
|
|
379
|
-
rawContent = await fn.function(args, runner);
|
|
402
|
+
rawContent = await fn.function(args, runner, toolContext);
|
|
380
403
|
}
|
|
381
404
|
|
|
382
405
|
const content = this.#stringifyFunctionCallResult(rawContent);
|
|
@@ -103,11 +103,12 @@ export class AssistantStream
|
|
|
103
103
|
|
|
104
104
|
//Catch all for passing along all events
|
|
105
105
|
this.on('event', (event) => {
|
|
106
|
+
const eventCopy = structuredClone(event);
|
|
106
107
|
const reader = readQueue.shift();
|
|
107
108
|
if (reader) {
|
|
108
|
-
reader.resolve(
|
|
109
|
+
reader.resolve(eventCopy);
|
|
109
110
|
} else {
|
|
110
|
-
pushQueue.push(
|
|
111
|
+
pushQueue.push(eventCopy);
|
|
111
112
|
}
|
|
112
113
|
});
|
|
113
114
|
|
|
@@ -686,7 +687,7 @@ export class AssistantStream
|
|
|
686
687
|
|
|
687
688
|
const accEntry = accValue[index];
|
|
688
689
|
if (accEntry == null) {
|
|
689
|
-
accValue
|
|
690
|
+
accValue[index] = deltaEntry;
|
|
690
691
|
} else {
|
|
691
692
|
accValue[index] = this.accumulateDelta(accEntry, deltaEntry);
|
|
692
693
|
}
|
|
@@ -16,20 +16,60 @@ export interface ChatCompletionRunnerEvents extends AbstractChatCompletionRunner
|
|
|
16
16
|
content: (content: string) => void;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
type ChatCompletionToolRunnerParamsBase = Omit<ChatCompletionCreateParamsNonStreaming, 'tools'>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parameters for tools that do not require a context value.
|
|
23
|
+
*/
|
|
24
|
+
export type ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs extends BaseFunctionsArgs> =
|
|
25
|
+
ChatCompletionToolRunnerParamsBase & {
|
|
26
|
+
tools: RunnableTools<FunctionsArgs> | AutoParseableTool<any, true>[];
|
|
27
|
+
toolContext?: never;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parameters for tools that require a context value.
|
|
32
|
+
*/
|
|
33
|
+
export type ChatCompletionToolRunnerParamsWithContext<
|
|
34
|
+
FunctionsArgs extends BaseFunctionsArgs,
|
|
35
|
+
ToolContext,
|
|
36
|
+
> = ChatCompletionToolRunnerParamsBase & {
|
|
37
|
+
tools: RunnableTools<FunctionsArgs, ToolContext> | AutoParseableTool<any, true>[];
|
|
38
|
+
/**
|
|
39
|
+
* Context to pass to each tool callback during this run.
|
|
40
|
+
*/
|
|
41
|
+
toolContext: ToolContext;
|
|
24
42
|
};
|
|
25
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Parameters for running tools. Supplying a context type makes `toolContext`
|
|
46
|
+
* required; omitting it preserves the existing no-context form.
|
|
47
|
+
*/
|
|
48
|
+
export type ChatCompletionToolRunnerParams<FunctionsArgs extends BaseFunctionsArgs, ToolContext = never> = [
|
|
49
|
+
ToolContext,
|
|
50
|
+
] extends [never] ?
|
|
51
|
+
ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs>
|
|
52
|
+
: ChatCompletionToolRunnerParamsWithContext<FunctionsArgs, ToolContext>;
|
|
53
|
+
|
|
26
54
|
export class ChatCompletionRunner<ParsedT = null> extends AbstractChatCompletionRunner<
|
|
27
55
|
ChatCompletionRunnerEvents,
|
|
28
56
|
ParsedT
|
|
29
57
|
> {
|
|
58
|
+
static runTools<ParsedT, ToolContext = unknown>(
|
|
59
|
+
client: OpenAI,
|
|
60
|
+
params: ChatCompletionToolRunnerParamsWithContext<any[], ToolContext>,
|
|
61
|
+
options?: RunnerOptions,
|
|
62
|
+
): ChatCompletionRunner<ParsedT>;
|
|
30
63
|
static runTools<ParsedT>(
|
|
31
64
|
client: OpenAI,
|
|
32
|
-
params:
|
|
65
|
+
params: ChatCompletionToolRunnerParamsWithoutContext<any[]>,
|
|
66
|
+
options?: RunnerOptions,
|
|
67
|
+
): ChatCompletionRunner<ParsedT>;
|
|
68
|
+
static runTools<ParsedT, ToolContext = unknown>(
|
|
69
|
+
client: OpenAI,
|
|
70
|
+
params:
|
|
71
|
+
| ChatCompletionToolRunnerParamsWithContext<any[], ToolContext>
|
|
72
|
+
| ChatCompletionToolRunnerParamsWithoutContext<any[]>,
|
|
33
73
|
options?: RunnerOptions,
|
|
34
74
|
): ChatCompletionRunner<ParsedT> {
|
|
35
75
|
const runner = new ChatCompletionRunner<ParsedT>();
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import OpenAI from '../index';
|
|
9
9
|
import { RequestOptions } from '../internal/request-options';
|
|
10
10
|
import { type ReadableStream } from '../internal/shim-types';
|
|
11
|
+
import { uuid4 } from '../internal/utils/uuid';
|
|
11
12
|
import {
|
|
12
13
|
AutoParseableResponseFormat,
|
|
13
14
|
hasAutoParseableInput,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
type ChatCompletionCreateParams,
|
|
26
27
|
type ChatCompletionCreateParamsBase,
|
|
27
28
|
type ChatCompletionCreateParamsStreaming,
|
|
29
|
+
type ChatCompletionMessageParam,
|
|
28
30
|
type ChatCompletionRole,
|
|
29
31
|
} from '../resources/chat/completions/completions';
|
|
30
32
|
import { Stream } from '../streaming';
|
|
@@ -117,6 +119,69 @@ export type ChatCompletionStreamParams = Omit<ChatCompletionCreateParamsBase, 's
|
|
|
117
119
|
stream?: true;
|
|
118
120
|
};
|
|
119
121
|
|
|
122
|
+
type ChatCompletionReadableStreamMessage = {
|
|
123
|
+
type: 'message';
|
|
124
|
+
message: ChatCompletionMessageParam;
|
|
125
|
+
tool_call_ids?: string[];
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Keep message records readable as empty chunks by older SDKs. Their finalizer
|
|
129
|
+
// overwrites `object`, so the encoded payload does not leak into completions.
|
|
130
|
+
const CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX = 'chat.completion.chunk.message:';
|
|
131
|
+
|
|
132
|
+
type ChatCompletionReadableStreamMessageChunk = Pick<ChatCompletionChunk, 'id' | 'created' | 'model'> & {
|
|
133
|
+
choices: [];
|
|
134
|
+
object: `${typeof CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX}${string}`;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type ChatCompletionReadableStreamItem =
|
|
138
|
+
| ChatCompletionChunk
|
|
139
|
+
| ChatCompletionReadableStreamMessage
|
|
140
|
+
| ChatCompletionReadableStreamMessageChunk;
|
|
141
|
+
|
|
142
|
+
export function makeChatCompletionReadableStreamMessageChunk(
|
|
143
|
+
chunk: ChatCompletionChunk,
|
|
144
|
+
message: ChatCompletionMessageParam,
|
|
145
|
+
toolCallIds?: string[],
|
|
146
|
+
): ChatCompletionReadableStreamMessageChunk {
|
|
147
|
+
const payload: ChatCompletionReadableStreamMessage = {
|
|
148
|
+
type: 'message',
|
|
149
|
+
message,
|
|
150
|
+
...(toolCallIds ? { tool_call_ids: toolCallIds } : {}),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
id: chunk.id,
|
|
155
|
+
choices: [],
|
|
156
|
+
created: chunk.created,
|
|
157
|
+
model: chunk.model,
|
|
158
|
+
object: `${CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX}${JSON.stringify(payload)}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isChatCompletionReadableStreamMessage(
|
|
163
|
+
item: ChatCompletionReadableStreamItem,
|
|
164
|
+
): item is ChatCompletionReadableStreamMessage | ChatCompletionReadableStreamMessageChunk {
|
|
165
|
+
return (
|
|
166
|
+
('type' in item && item.type === 'message' && 'message' in item) ||
|
|
167
|
+
('object' in item &&
|
|
168
|
+
typeof item.object === 'string' &&
|
|
169
|
+
item.object.startsWith(CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX))
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getChatCompletionReadableStreamMessage(
|
|
174
|
+
item: ChatCompletionReadableStreamMessage | ChatCompletionReadableStreamMessageChunk,
|
|
175
|
+
): ChatCompletionReadableStreamMessage {
|
|
176
|
+
if ('type' in item) {
|
|
177
|
+
return item;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return JSON.parse(
|
|
181
|
+
item.object.slice(CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX.length),
|
|
182
|
+
) as ChatCompletionReadableStreamMessage;
|
|
183
|
+
}
|
|
184
|
+
|
|
120
185
|
interface ChoiceEventState {
|
|
121
186
|
content_done: boolean;
|
|
122
187
|
refusal_done: boolean;
|
|
@@ -204,27 +269,28 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
204
269
|
|
|
205
270
|
for (const choice of chunk.choices) {
|
|
206
271
|
const choiceSnapshot = completion.choices[choice.index]!;
|
|
272
|
+
const { delta } = choice;
|
|
207
273
|
|
|
208
274
|
if (
|
|
209
|
-
|
|
275
|
+
delta?.content != null &&
|
|
210
276
|
choiceSnapshot.message?.role === 'assistant' &&
|
|
211
277
|
choiceSnapshot.message?.content
|
|
212
278
|
) {
|
|
213
|
-
this._emit('content',
|
|
279
|
+
this._emit('content', delta.content, choiceSnapshot.message.content);
|
|
214
280
|
this._emit('content.delta', {
|
|
215
|
-
delta:
|
|
281
|
+
delta: delta.content,
|
|
216
282
|
snapshot: choiceSnapshot.message.content,
|
|
217
283
|
parsed: choiceSnapshot.message.parsed,
|
|
218
284
|
});
|
|
219
285
|
}
|
|
220
286
|
|
|
221
287
|
if (
|
|
222
|
-
|
|
288
|
+
delta?.refusal != null &&
|
|
223
289
|
choiceSnapshot.message?.role === 'assistant' &&
|
|
224
290
|
choiceSnapshot.message?.refusal
|
|
225
291
|
) {
|
|
226
292
|
this._emit('refusal.delta', {
|
|
227
|
-
delta:
|
|
293
|
+
delta: delta.refusal,
|
|
228
294
|
snapshot: choiceSnapshot.message.refusal,
|
|
229
295
|
});
|
|
230
296
|
}
|
|
@@ -253,7 +319,7 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
253
319
|
}
|
|
254
320
|
}
|
|
255
321
|
|
|
256
|
-
for (const toolCall of
|
|
322
|
+
for (const toolCall of delta?.tool_calls ?? []) {
|
|
257
323
|
if (state.current_tool_call_index !== toolCall.index) {
|
|
258
324
|
this.#emitContentDoneEvents(choiceSnapshot);
|
|
259
325
|
|
|
@@ -266,7 +332,7 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
266
332
|
state.current_tool_call_index = toolCall.index;
|
|
267
333
|
}
|
|
268
334
|
|
|
269
|
-
for (const toolCallDelta of
|
|
335
|
+
for (const toolCallDelta of delta?.tool_calls ?? []) {
|
|
270
336
|
const toolCallSnapshot = choiceSnapshot.message.tool_calls?.[toolCallDelta.index];
|
|
271
337
|
if (!toolCallSnapshot?.type) {
|
|
272
338
|
continue;
|
|
@@ -397,21 +463,51 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
397
463
|
this._listenForAbort(options?.signal);
|
|
398
464
|
this.#beginRequest();
|
|
399
465
|
this._connected();
|
|
400
|
-
const stream = Stream.fromReadableStream<
|
|
466
|
+
const stream = Stream.fromReadableStream<ChatCompletionReadableStreamItem>(
|
|
467
|
+
readableStream,
|
|
468
|
+
this.controller,
|
|
469
|
+
);
|
|
401
470
|
let chatId;
|
|
402
|
-
for await (const
|
|
403
|
-
if (
|
|
471
|
+
for await (const item of stream) {
|
|
472
|
+
if (isChatCompletionReadableStreamMessage(item)) {
|
|
473
|
+
const message = getChatCompletionReadableStreamMessage(item);
|
|
474
|
+
if (this.#currentChatCompletionSnapshot) {
|
|
475
|
+
const toolCalls = this.#currentChatCompletionSnapshot.choices[0]?.message.tool_calls;
|
|
476
|
+
for (const [index, id] of message.tool_call_ids?.entries() ?? []) {
|
|
477
|
+
const toolCall = toolCalls?.[index];
|
|
478
|
+
if (toolCall && id) {
|
|
479
|
+
toolCall.id = id;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this._addChatCompletion(this.#endRequest());
|
|
484
|
+
chatId = undefined;
|
|
485
|
+
}
|
|
486
|
+
this._addMessage(message.message);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const chunk = item;
|
|
491
|
+
|
|
492
|
+
if (chatId && chunk.id && chatId !== chunk.id) {
|
|
404
493
|
// A new request has been made.
|
|
405
494
|
this._addChatCompletion(this.#endRequest());
|
|
406
495
|
}
|
|
407
496
|
|
|
408
497
|
this.#addChunk(chunk);
|
|
409
|
-
chatId = chunk.id;
|
|
498
|
+
if (chunk.id) chatId = chunk.id;
|
|
410
499
|
}
|
|
411
500
|
if (stream.controller.signal?.aborted) {
|
|
412
501
|
throw new APIUserAbortError();
|
|
413
502
|
}
|
|
414
|
-
|
|
503
|
+
if (this.#currentChatCompletionSnapshot) {
|
|
504
|
+
return this._addChatCompletion(this.#endRequest());
|
|
505
|
+
}
|
|
506
|
+
const lastChatCompletion = this._chatCompletions[this._chatCompletions.length - 1];
|
|
507
|
+
if (lastChatCompletion) {
|
|
508
|
+
return lastChatCompletion;
|
|
509
|
+
}
|
|
510
|
+
throw new OpenAIError(`request ended without sending any chunks`);
|
|
415
511
|
}
|
|
416
512
|
|
|
417
513
|
#getAutoParseableResponseFormat(): AutoParseableResponseFormat<ParsedT> | null {
|
|
@@ -431,7 +527,7 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
431
527
|
...rest,
|
|
432
528
|
choices: [],
|
|
433
529
|
};
|
|
434
|
-
} else {
|
|
530
|
+
} else if (chunk.id) {
|
|
435
531
|
Object.assign(snapshot, rest);
|
|
436
532
|
}
|
|
437
533
|
|
|
@@ -657,9 +753,6 @@ function finalizeChatCompletion<ParsedT>(
|
|
|
657
753
|
tool_calls: tool_calls.map((tool_call, i) => {
|
|
658
754
|
const { function: fn, type, id, ...toolRest } = tool_call;
|
|
659
755
|
const { arguments: args, name, ...fnRest } = fn || {};
|
|
660
|
-
if (id == null) {
|
|
661
|
-
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].id\n${str(snapshot)}`);
|
|
662
|
-
}
|
|
663
756
|
if (type == null) {
|
|
664
757
|
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].type\n${str(snapshot)}`);
|
|
665
758
|
}
|
|
@@ -674,7 +767,12 @@ function finalizeChatCompletion<ParsedT>(
|
|
|
674
767
|
);
|
|
675
768
|
}
|
|
676
769
|
|
|
677
|
-
return {
|
|
770
|
+
return {
|
|
771
|
+
...toolRest,
|
|
772
|
+
id: id || `call_${uuid4()}`,
|
|
773
|
+
type,
|
|
774
|
+
function: { ...fnRest, name, arguments: args },
|
|
775
|
+
};
|
|
678
776
|
}),
|
|
679
777
|
},
|
|
680
778
|
};
|