openai 7.5.0 → 7.6.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 +105 -0
- package/README.md +46 -2
- package/auth/index.d.mts +1 -1
- package/auth/index.d.mts.map +1 -1
- package/auth/index.d.ts +1 -1
- package/auth/index.d.ts.map +1 -1
- package/auth/index.js.map +1 -1
- package/auth/index.mjs.map +1 -1
- package/auth/subject-token-providers.d.mts.map +1 -1
- package/auth/subject-token-providers.d.ts.map +1 -1
- package/auth/subject-token-providers.js +176 -4
- package/auth/subject-token-providers.js.map +1 -1
- package/auth/subject-token-providers.mjs +176 -4
- package/auth/subject-token-providers.mjs.map +1 -1
- package/auth/types.d.mts +17 -0
- package/auth/types.d.mts.map +1 -1
- package/auth/types.d.ts +17 -0
- package/auth/types.d.ts.map +1 -1
- package/auth/workload-identity-auth.d.mts.map +1 -1
- package/auth/workload-identity-auth.d.ts.map +1 -1
- package/auth/workload-identity-auth.js +80 -7
- package/auth/workload-identity-auth.js.map +1 -1
- package/auth/workload-identity-auth.mjs +80 -7
- package/auth/workload-identity-auth.mjs.map +1 -1
- package/auth/x509-transport.d.mts +5 -0
- package/auth/x509-transport.d.mts.map +1 -0
- package/auth/x509-transport.d.ts +5 -0
- package/auth/x509-transport.d.ts.map +1 -0
- package/auth/x509-transport.js +39 -0
- package/auth/x509-transport.js.map +1 -0
- package/auth/x509-transport.mjs +36 -0
- package/auth/x509-transport.mjs.map +1 -0
- package/azure.d.mts +11 -2
- package/azure.d.mts.map +1 -1
- package/azure.d.ts +11 -2
- package/azure.d.ts.map +1 -1
- package/azure.js +7 -1
- package/azure.js.map +1 -1
- package/azure.mjs +7 -1
- package/azure.mjs.map +1 -1
- package/bedrock.d.mts +6 -2
- package/bedrock.d.mts.map +1 -1
- package/bedrock.d.ts +6 -2
- package/bedrock.d.ts.map +1 -1
- package/bedrock.js +34 -4
- package/bedrock.js.map +1 -1
- package/bedrock.mjs +35 -5
- package/bedrock.mjs.map +1 -1
- package/beta/realtime/internal-base.d.mts +2 -0
- package/beta/realtime/internal-base.d.mts.map +1 -1
- package/beta/realtime/internal-base.d.ts +2 -0
- package/beta/realtime/internal-base.d.ts.map +1 -1
- package/beta/realtime/internal-base.js +15 -0
- package/beta/realtime/internal-base.js.map +1 -1
- package/beta/realtime/internal-base.mjs +14 -0
- package/beta/realtime/internal-base.mjs.map +1 -1
- package/beta/realtime/websocket.d.mts +11 -5
- package/beta/realtime/websocket.d.mts.map +1 -1
- package/beta/realtime/websocket.d.ts +11 -5
- package/beta/realtime/websocket.d.ts.map +1 -1
- package/beta/realtime/websocket.js +106 -31
- package/beta/realtime/websocket.js.map +1 -1
- package/beta/realtime/websocket.mjs +107 -32
- package/beta/realtime/websocket.mjs.map +1 -1
- package/beta/realtime/ws.d.mts +0 -2
- package/beta/realtime/ws.d.mts.map +1 -1
- package/beta/realtime/ws.d.ts +0 -2
- package/beta/realtime/ws.d.ts.map +1 -1
- package/beta/realtime/ws.js +24 -5
- package/beta/realtime/ws.js.map +1 -1
- package/beta/realtime/ws.mjs +25 -6
- package/beta/realtime/ws.mjs.map +1 -1
- package/client.d.mts +17 -2
- package/client.d.mts.map +1 -1
- package/client.d.ts +17 -2
- package/client.d.ts.map +1 -1
- package/client.js +422 -70
- package/client.js.map +1 -1
- package/client.mjs +423 -71
- package/client.mjs.map +1 -1
- package/core/EventEmitter.d.mts.map +1 -1
- package/core/EventEmitter.d.ts.map +1 -1
- package/core/EventEmitter.js +67 -9
- package/core/EventEmitter.js.map +1 -1
- package/core/EventEmitter.mjs +68 -10
- package/core/EventEmitter.mjs.map +1 -1
- package/core/streaming.d.mts +1 -0
- package/core/streaming.d.mts.map +1 -1
- package/core/streaming.d.ts +1 -0
- package/core/streaming.d.ts.map +1 -1
- package/core/streaming.js +58 -19
- package/core/streaming.js.map +1 -1
- package/core/streaming.mjs +58 -19
- package/core/streaming.mjs.map +1 -1
- package/helpers/audio.d.mts +5 -5
- package/helpers/audio.d.mts.map +1 -1
- package/helpers/audio.d.ts +5 -5
- package/helpers/audio.d.ts.map +1 -1
- package/helpers/audio.js +93 -22
- package/helpers/audio.js.map +1 -1
- package/helpers/audio.mjs +93 -22
- package/helpers/audio.mjs.map +1 -1
- package/helpers/standard-schema.d.mts.map +1 -1
- package/helpers/standard-schema.d.ts.map +1 -1
- package/helpers/standard-schema.js +34 -14
- package/helpers/standard-schema.js.map +1 -1
- package/helpers/standard-schema.mjs +35 -15
- package/helpers/standard-schema.mjs.map +1 -1
- package/helpers/zod-v3-strict-schema.d.mts +7 -0
- package/helpers/zod-v3-strict-schema.d.mts.map +1 -0
- package/helpers/zod-v3-strict-schema.d.ts +7 -0
- package/helpers/zod-v3-strict-schema.d.ts.map +1 -0
- package/helpers/zod-v3-strict-schema.js +403 -0
- package/helpers/zod-v3-strict-schema.js.map +1 -0
- package/helpers/zod-v3-strict-schema.mjs +399 -0
- package/helpers/zod-v3-strict-schema.mjs.map +1 -0
- package/helpers/zod.d.mts.map +1 -1
- package/helpers/zod.d.ts.map +1 -1
- package/helpers/zod.js +11 -3
- package/helpers/zod.js.map +1 -1
- package/helpers/zod.mjs +12 -4
- 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.map +1 -1
- package/index.mjs.map +1 -1
- package/internal/assistant-stream-delta.d.mts.map +1 -1
- package/internal/assistant-stream-delta.d.ts.map +1 -1
- package/internal/assistant-stream-delta.js +28 -8
- package/internal/assistant-stream-delta.js.map +1 -1
- package/internal/assistant-stream-delta.mjs +28 -8
- package/internal/assistant-stream-delta.mjs.map +1 -1
- package/internal/auth/x509-token-exchange.d.mts +16 -0
- package/internal/auth/x509-token-exchange.d.mts.map +1 -0
- package/internal/auth/x509-token-exchange.d.ts +16 -0
- package/internal/auth/x509-token-exchange.d.ts.map +1 -0
- package/internal/auth/x509-token-exchange.js +246 -0
- package/internal/auth/x509-token-exchange.js.map +1 -0
- package/internal/auth/x509-token-exchange.mjs +243 -0
- package/internal/auth/x509-token-exchange.mjs.map +1 -0
- package/internal/auth/x509-transport-capability.d.mts +37 -0
- package/internal/auth/x509-transport-capability.d.mts.map +1 -0
- package/internal/auth/x509-transport-capability.d.ts +37 -0
- package/internal/auth/x509-transport-capability.d.ts.map +1 -0
- package/internal/auth/x509-transport-capability.js +169 -0
- package/internal/auth/x509-transport-capability.js.map +1 -0
- package/internal/auth/x509-transport-capability.mjs +164 -0
- package/internal/auth/x509-transport-capability.mjs.map +1 -0
- package/internal/auth/x509-transport-registry.d.mts +63 -0
- package/internal/auth/x509-transport-registry.d.mts.map +1 -0
- package/internal/auth/x509-transport-registry.d.ts +63 -0
- package/internal/auth/x509-transport-registry.d.ts.map +1 -0
- package/internal/auth/x509-transport-registry.js +53 -0
- package/internal/auth/x509-transport-registry.js.map +1 -0
- package/internal/auth/x509-transport-registry.mjs +48 -0
- package/internal/auth/x509-transport-registry.mjs.map +1 -0
- package/internal/auth/x509-transport-state-browser.d.mts +21 -0
- package/internal/auth/x509-transport-state-browser.d.mts.map +1 -0
- package/internal/auth/x509-transport-state-browser.d.ts +21 -0
- package/internal/auth/x509-transport-state-browser.d.ts.map +1 -0
- package/internal/auth/x509-transport-state-browser.js +30 -0
- package/internal/auth/x509-transport-state-browser.js.map +1 -0
- package/internal/auth/x509-transport-state-browser.mjs +27 -0
- package/internal/auth/x509-transport-state-browser.mjs.map +1 -0
- package/internal/auth/x509-transport-state.cjs +30 -0
- package/internal/auth/x509-transport-state.cjs.map +1 -0
- package/internal/auth/x509-transport-state.d.cts +21 -0
- package/internal/auth/x509-transport-state.d.cts.map +1 -0
- package/internal/auth/x509-workload-identity-auth.d.mts +128 -0
- package/internal/auth/x509-workload-identity-auth.d.mts.map +1 -0
- package/internal/auth/x509-workload-identity-auth.d.ts +128 -0
- package/internal/auth/x509-workload-identity-auth.d.ts.map +1 -0
- package/internal/auth/x509-workload-identity-auth.js +767 -0
- package/internal/auth/x509-workload-identity-auth.js.map +1 -0
- package/internal/auth/x509-workload-identity-auth.mjs +757 -0
- package/internal/auth/x509-workload-identity-auth.mjs.map +1 -0
- package/internal/bedrock.d.mts +2 -0
- package/internal/bedrock.d.mts.map +1 -1
- package/internal/bedrock.d.ts +2 -0
- package/internal/bedrock.d.ts.map +1 -1
- package/internal/bedrock.js +169 -5
- package/internal/bedrock.js.map +1 -1
- package/internal/bedrock.mjs +168 -5
- package/internal/bedrock.mjs.map +1 -1
- package/internal/data-residency.d.mts +12 -0
- package/internal/data-residency.d.mts.map +1 -0
- package/internal/data-residency.d.ts +12 -0
- package/internal/data-residency.d.ts.map +1 -0
- package/internal/data-residency.js +34 -0
- package/internal/data-residency.js.map +1 -0
- package/internal/data-residency.mjs +30 -0
- package/internal/data-residency.mjs.map +1 -0
- package/internal/multipart-encoding.d.mts +8 -0
- package/internal/multipart-encoding.d.mts.map +1 -0
- package/internal/multipart-encoding.d.ts +8 -0
- package/internal/multipart-encoding.d.ts.map +1 -0
- package/internal/multipart-encoding.js +51 -0
- package/internal/multipart-encoding.js.map +1 -0
- package/internal/multipart-encoding.mjs +48 -0
- package/internal/multipart-encoding.mjs.map +1 -0
- package/internal/qs/utils.d.mts.map +1 -1
- package/internal/qs/utils.d.ts.map +1 -1
- package/internal/qs/utils.js +307 -10
- package/internal/qs/utils.js.map +1 -1
- package/internal/qs/utils.mjs +307 -10
- package/internal/qs/utils.mjs.map +1 -1
- package/internal/request-options.d.mts +7 -0
- package/internal/request-options.d.mts.map +1 -1
- package/internal/request-options.d.ts +7 -0
- package/internal/request-options.d.ts.map +1 -1
- package/internal/request-options.js +43 -1
- package/internal/request-options.js.map +1 -1
- package/internal/request-options.mjs +42 -1
- package/internal/request-options.mjs.map +1 -1
- package/internal/responses/canonical-output-text.js +3 -3
- package/internal/responses/canonical-output-text.js.map +1 -1
- package/internal/responses/canonical-output-text.mjs +3 -3
- package/internal/responses/canonical-output-text.mjs.map +1 -1
- package/internal/responses/response-accumulator.d.mts +1 -1
- package/internal/responses/response-accumulator.d.mts.map +1 -1
- package/internal/responses/response-accumulator.d.ts +1 -1
- package/internal/responses/response-accumulator.d.ts.map +1 -1
- package/internal/responses/response-accumulator.js +331 -25
- package/internal/responses/response-accumulator.js.map +1 -1
- package/internal/responses/response-accumulator.mjs +331 -25
- package/internal/responses/response-accumulator.mjs.map +1 -1
- package/internal/tslib.js +6 -0
- package/internal/tslib.mjs +6 -1
- package/internal/uploads.d.mts +10 -6
- package/internal/uploads.d.mts.map +1 -1
- package/internal/uploads.d.ts +10 -6
- package/internal/uploads.d.ts.map +1 -1
- package/internal/uploads.js +65 -24
- package/internal/uploads.js.map +1 -1
- package/internal/uploads.mjs +65 -24
- package/internal/uploads.mjs.map +1 -1
- package/internal/utils/env.d.mts.map +1 -1
- package/internal/utils/env.d.ts.map +1 -1
- package/internal/utils/env.js +9 -4
- package/internal/utils/env.js.map +1 -1
- package/internal/utils/env.mjs +9 -4
- package/internal/utils/env.mjs.map +1 -1
- package/internal/ws.d.mts.map +1 -1
- package/internal/ws.d.ts.map +1 -1
- package/internal/ws.js +23 -1
- package/internal/ws.js.map +1 -1
- package/internal/ws.mjs +23 -1
- package/internal/ws.mjs.map +1 -1
- package/lib/AbstractChatCompletionRunner.d.mts.map +1 -1
- package/lib/AbstractChatCompletionRunner.d.ts.map +1 -1
- package/lib/AbstractChatCompletionRunner.js +32 -7
- package/lib/AbstractChatCompletionRunner.js.map +1 -1
- package/lib/AbstractChatCompletionRunner.mjs +34 -9
- 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 +211 -23
- package/lib/AssistantStream.js.map +1 -1
- package/lib/AssistantStream.mjs +211 -23
- package/lib/AssistantStream.mjs.map +1 -1
- package/lib/ChatCompletionStream.d.mts +1 -1
- package/lib/ChatCompletionStream.d.mts.map +1 -1
- package/lib/ChatCompletionStream.d.ts +1 -1
- package/lib/ChatCompletionStream.d.ts.map +1 -1
- package/lib/ChatCompletionStream.js +1118 -67
- package/lib/ChatCompletionStream.js.map +1 -1
- package/lib/ChatCompletionStream.mjs +1119 -68
- package/lib/ChatCompletionStream.mjs.map +1 -1
- package/lib/EventEmitter.d.mts.map +1 -1
- package/lib/EventEmitter.d.ts.map +1 -1
- package/lib/EventEmitter.js +67 -9
- package/lib/EventEmitter.js.map +1 -1
- package/lib/EventEmitter.mjs +68 -10
- package/lib/EventEmitter.mjs.map +1 -1
- package/lib/EventStream.d.mts +4 -0
- package/lib/EventStream.d.mts.map +1 -1
- package/lib/EventStream.d.ts +4 -0
- package/lib/EventStream.d.ts.map +1 -1
- package/lib/EventStream.js +1096 -11
- package/lib/EventStream.js.map +1 -1
- package/lib/EventStream.mjs +1097 -12
- package/lib/EventStream.mjs.map +1 -1
- package/lib/ResponsesParser.d.mts.map +1 -1
- package/lib/ResponsesParser.d.ts.map +1 -1
- package/lib/ResponsesParser.js +1 -1
- package/lib/ResponsesParser.js.map +1 -1
- package/lib/ResponsesParser.mjs +1 -1
- package/lib/ResponsesParser.mjs.map +1 -1
- package/lib/Util.d.mts +2 -0
- package/lib/Util.d.mts.map +1 -1
- package/lib/Util.d.ts +2 -0
- package/lib/Util.d.ts.map +1 -1
- package/lib/Util.js +7 -4
- package/lib/Util.js.map +1 -1
- package/lib/Util.mjs +7 -4
- package/lib/Util.mjs.map +1 -1
- package/lib/assistant-run-polling.d.mts +12 -0
- package/lib/assistant-run-polling.d.mts.map +1 -0
- package/lib/assistant-run-polling.d.ts +12 -0
- package/lib/assistant-run-polling.d.ts.map +1 -0
- package/lib/assistant-run-polling.js +17 -0
- package/lib/assistant-run-polling.js.map +1 -0
- package/lib/assistant-run-polling.mjs +14 -0
- package/lib/assistant-run-polling.mjs.map +1 -0
- package/lib/embeddings.d.mts +18 -0
- package/lib/embeddings.d.mts.map +1 -0
- package/lib/embeddings.d.ts +18 -0
- package/lib/embeddings.d.ts.map +1 -0
- package/lib/embeddings.js +50 -0
- package/lib/embeddings.js.map +1 -0
- package/lib/embeddings.mjs +47 -0
- package/lib/embeddings.mjs.map +1 -0
- package/lib/file-processing.d.mts +10 -0
- package/lib/file-processing.d.mts.map +1 -0
- package/lib/file-processing.d.ts +10 -0
- package/lib/file-processing.d.ts.map +1 -0
- package/lib/file-processing.js +30 -0
- package/lib/file-processing.js.map +1 -0
- package/lib/file-processing.mjs +27 -0
- package/lib/file-processing.mjs.map +1 -0
- package/lib/parser.d.mts.map +1 -1
- package/lib/parser.d.ts.map +1 -1
- package/lib/parser.js +16 -4
- package/lib/parser.js.map +1 -1
- package/lib/parser.mjs +16 -4
- package/lib/parser.mjs.map +1 -1
- package/lib/polling.d.mts +20 -0
- package/lib/polling.d.mts.map +1 -0
- package/lib/polling.d.ts +20 -0
- package/lib/polling.d.ts.map +1 -0
- package/lib/polling.js +130 -0
- package/lib/polling.js.map +1 -0
- package/lib/polling.mjs +127 -0
- package/lib/polling.mjs.map +1 -0
- package/lib/responses/ResponseStream.d.mts.map +1 -1
- package/lib/responses/ResponseStream.d.ts.map +1 -1
- package/lib/responses/ResponseStream.js +21 -11
- package/lib/responses/ResponseStream.js.map +1 -1
- package/lib/responses/ResponseStream.mjs +21 -11
- package/lib/responses/ResponseStream.mjs.map +1 -1
- package/lib/vector-store-polling.d.mts +22 -0
- package/lib/vector-store-polling.d.mts.map +1 -0
- package/lib/vector-store-polling.d.ts +22 -0
- package/lib/vector-store-polling.d.ts.map +1 -0
- package/lib/vector-store-polling.js +24 -0
- package/lib/vector-store-polling.js.map +1 -0
- package/lib/vector-store-polling.mjs +20 -0
- package/lib/vector-store-polling.mjs.map +1 -0
- package/lib/vector-store-upload.d.mts +17 -0
- package/lib/vector-store-upload.d.mts.map +1 -0
- package/lib/vector-store-upload.d.ts +17 -0
- package/lib/vector-store-upload.d.ts.map +1 -0
- package/lib/vector-store-upload.js +37 -0
- package/lib/vector-store-upload.js.map +1 -0
- package/lib/vector-store-upload.mjs +34 -0
- package/lib/vector-store-upload.mjs.map +1 -0
- package/lib/webhook-signature.d.mts +15 -0
- package/lib/webhook-signature.d.mts.map +1 -0
- package/lib/webhook-signature.d.ts +15 -0
- package/lib/webhook-signature.d.ts.map +1 -0
- package/lib/webhook-signature.js +125 -0
- package/lib/webhook-signature.js.map +1 -0
- package/lib/webhook-signature.mjs +121 -0
- package/lib/webhook-signature.mjs.map +1 -0
- package/package.json +14 -1
- package/realtime/internal-base.d.mts +2 -0
- package/realtime/internal-base.d.mts.map +1 -1
- package/realtime/internal-base.d.ts +2 -0
- package/realtime/internal-base.d.ts.map +1 -1
- package/realtime/internal-base.js +15 -0
- package/realtime/internal-base.js.map +1 -1
- package/realtime/internal-base.mjs +14 -0
- package/realtime/internal-base.mjs.map +1 -1
- package/realtime/websocket.d.mts +3 -2
- package/realtime/websocket.d.mts.map +1 -1
- package/realtime/websocket.d.ts +3 -2
- package/realtime/websocket.d.ts.map +1 -1
- package/realtime/websocket.js +90 -26
- package/realtime/websocket.js.map +1 -1
- package/realtime/websocket.mjs +91 -27
- package/realtime/websocket.mjs.map +1 -1
- package/realtime/ws.d.mts.map +1 -1
- package/realtime/ws.d.ts.map +1 -1
- package/realtime/ws.js +9 -1
- package/realtime/ws.js.map +1 -1
- package/realtime/ws.mjs +10 -2
- package/realtime/ws.mjs.map +1 -1
- package/resources/admin/organization/index.d.mts +2 -2
- package/resources/admin/organization/index.d.mts.map +1 -1
- package/resources/admin/organization/index.d.ts +2 -2
- package/resources/admin/organization/index.d.ts.map +1 -1
- package/resources/admin/organization/index.js.map +1 -1
- package/resources/admin/organization/index.mjs.map +1 -1
- package/resources/admin/organization/organization.d.mts +4 -4
- package/resources/admin/organization/organization.d.mts.map +1 -1
- package/resources/admin/organization/organization.d.ts +4 -4
- package/resources/admin/organization/organization.d.ts.map +1 -1
- package/resources/admin/organization/organization.js.map +1 -1
- package/resources/admin/organization/organization.mjs.map +1 -1
- package/resources/admin/organization/projects/index.d.mts +1 -1
- package/resources/admin/organization/projects/index.d.mts.map +1 -1
- package/resources/admin/organization/projects/index.d.ts +1 -1
- package/resources/admin/organization/projects/index.d.ts.map +1 -1
- package/resources/admin/organization/projects/index.js.map +1 -1
- package/resources/admin/organization/projects/index.mjs.map +1 -1
- package/resources/admin/organization/projects/projects.d.mts +22 -5
- package/resources/admin/organization/projects/projects.d.mts.map +1 -1
- package/resources/admin/organization/projects/projects.d.ts +22 -5
- package/resources/admin/organization/projects/projects.d.ts.map +1 -1
- package/resources/admin/organization/projects/projects.js.map +1 -1
- package/resources/admin/organization/projects/projects.mjs.map +1 -1
- package/resources/admin/organization/usage.d.mts +62 -1
- package/resources/admin/organization/usage.d.mts.map +1 -1
- package/resources/admin/organization/usage.d.ts +62 -1
- package/resources/admin/organization/usage.d.ts.map +1 -1
- package/resources/admin/organization/usage.js.map +1 -1
- package/resources/admin/organization/usage.mjs.map +1 -1
- package/resources/beta/responses/internal-base.d.mts.map +1 -1
- package/resources/beta/responses/internal-base.d.ts.map +1 -1
- package/resources/beta/responses/internal-base.js +2 -0
- package/resources/beta/responses/internal-base.js.map +1 -1
- package/resources/beta/responses/internal-base.mjs +2 -0
- package/resources/beta/responses/internal-base.mjs.map +1 -1
- package/resources/beta/responses/responses.d.mts +9 -12
- package/resources/beta/responses/responses.d.mts.map +1 -1
- package/resources/beta/responses/responses.d.ts +9 -12
- package/resources/beta/responses/responses.d.ts.map +1 -1
- package/resources/beta/responses/responses.js.map +1 -1
- package/resources/beta/responses/responses.mjs.map +1 -1
- package/resources/beta/threads/runs/index.d.mts +1 -1
- package/resources/beta/threads/runs/index.d.mts.map +1 -1
- package/resources/beta/threads/runs/index.d.ts +1 -1
- package/resources/beta/threads/runs/index.d.ts.map +1 -1
- package/resources/beta/threads/runs/runs.d.mts.map +1 -1
- package/resources/beta/threads/runs/runs.d.ts.map +1 -1
- package/resources/beta/threads/runs/runs.js +2 -43
- package/resources/beta/threads/runs/runs.js.map +1 -1
- package/resources/beta/threads/runs/runs.mjs +2 -43
- package/resources/beta/threads/runs/runs.mjs.map +1 -1
- package/resources/chat/completions/completions.d.mts +6 -0
- package/resources/chat/completions/completions.d.mts.map +1 -1
- package/resources/chat/completions/completions.d.ts +6 -0
- 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/embeddings.d.mts.map +1 -1
- package/resources/embeddings.d.ts.map +1 -1
- package/resources/embeddings.js +2 -36
- package/resources/embeddings.js.map +1 -1
- package/resources/embeddings.mjs +2 -36
- package/resources/embeddings.mjs.map +1 -1
- package/resources/files.d.mts.map +1 -1
- package/resources/files.d.ts.map +1 -1
- package/resources/files.js +2 -15
- package/resources/files.js.map +1 -1
- package/resources/files.mjs +2 -15
- package/resources/files.mjs.map +1 -1
- package/resources/images.d.mts +12 -22
- package/resources/images.d.mts.map +1 -1
- package/resources/images.d.ts +12 -22
- package/resources/images.d.ts.map +1 -1
- package/resources/realtime/calls.d.mts +23 -1
- package/resources/realtime/calls.d.mts.map +1 -1
- package/resources/realtime/calls.d.ts +23 -1
- package/resources/realtime/calls.d.ts.map +1 -1
- package/resources/realtime/calls.js +24 -0
- package/resources/realtime/calls.js.map +1 -1
- package/resources/realtime/calls.mjs +24 -0
- package/resources/realtime/calls.mjs.map +1 -1
- package/resources/realtime/index.d.mts +1 -1
- package/resources/realtime/index.d.mts.map +1 -1
- package/resources/realtime/index.d.ts +1 -1
- package/resources/realtime/index.d.ts.map +1 -1
- package/resources/realtime/index.js.map +1 -1
- package/resources/realtime/index.mjs +1 -1
- package/resources/realtime/index.mjs.map +1 -1
- package/resources/realtime/realtime.d.mts +5 -4
- package/resources/realtime/realtime.d.mts.map +1 -1
- package/resources/realtime/realtime.d.ts +5 -4
- 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/internal-base.d.mts.map +1 -1
- package/resources/responses/internal-base.d.ts.map +1 -1
- package/resources/responses/internal-base.js +2 -0
- package/resources/responses/internal-base.js.map +1 -1
- package/resources/responses/internal-base.mjs +2 -0
- package/resources/responses/internal-base.mjs.map +1 -1
- package/resources/responses/responses.d.mts +9 -12
- package/resources/responses/responses.d.mts.map +1 -1
- package/resources/responses/responses.d.ts +9 -12
- package/resources/responses/responses.d.ts.map +1 -1
- package/resources/responses/responses.js.map +1 -1
- package/resources/responses/responses.mjs.map +1 -1
- package/resources/vector-stores/file-batches.d.mts.map +1 -1
- package/resources/vector-stores/file-batches.d.ts.map +1 -1
- package/resources/vector-stores/file-batches.js +4 -61
- package/resources/vector-stores/file-batches.js.map +1 -1
- package/resources/vector-stores/file-batches.mjs +4 -61
- package/resources/vector-stores/file-batches.mjs.map +1 -1
- package/resources/vector-stores/files.d.mts +1 -1
- package/resources/vector-stores/files.d.mts.map +1 -1
- package/resources/vector-stores/files.d.ts +1 -1
- package/resources/vector-stores/files.d.ts.map +1 -1
- package/resources/vector-stores/files.js +2 -35
- package/resources/vector-stores/files.js.map +1 -1
- package/resources/vector-stores/files.mjs +2 -35
- package/resources/vector-stores/files.mjs.map +1 -1
- package/resources/webhooks/webhooks.d.mts.map +1 -1
- package/resources/webhooks/webhooks.d.ts.map +1 -1
- package/resources/webhooks/webhooks.js +5 -43
- package/resources/webhooks/webhooks.js.map +1 -1
- package/resources/webhooks/webhooks.mjs +5 -43
- package/resources/webhooks/webhooks.mjs.map +1 -1
- package/src/auth/index.ts +6 -1
- package/src/auth/subject-token-providers.ts +196 -4
- package/src/auth/types.ts +24 -0
- package/src/auth/workload-identity-auth.ts +104 -7
- package/src/auth/x509-transport.ts +49 -0
- package/src/azure.ts +22 -1
- package/src/bedrock.ts +49 -5
- package/src/beta/realtime/internal-base.ts +15 -0
- package/src/beta/realtime/websocket.ts +148 -40
- package/src/beta/realtime/ws.ts +31 -13
- package/src/client.ts +536 -92
- package/src/core/EventEmitter.ts +91 -7
- package/src/core/streaming.ts +71 -20
- package/src/helpers/audio.ts +93 -22
- package/src/helpers/standard-schema.ts +50 -16
- package/src/helpers/zod-v3-strict-schema.ts +514 -0
- package/src/helpers/zod.ts +17 -4
- package/src/index.ts +1 -1
- package/src/internal/assistant-stream-delta.ts +33 -10
- package/src/internal/auth/x509-token-exchange.ts +310 -0
- package/src/internal/auth/x509-transport-capability.ts +219 -0
- package/src/internal/auth/x509-transport-registry.ts +118 -0
- package/src/internal/auth/x509-transport-state-browser.ts +36 -0
- package/src/internal/auth/x509-transport-state.cts +36 -0
- package/src/internal/auth/x509-workload-identity-auth.ts +958 -0
- package/src/internal/bedrock.ts +182 -5
- package/src/internal/data-residency.ts +44 -0
- package/src/internal/multipart-encoding.ts +53 -0
- package/src/internal/qs/utils.ts +375 -13
- package/src/internal/request-options.ts +52 -1
- package/src/internal/responses/canonical-output-text.ts +3 -3
- package/src/internal/responses/response-accumulator.ts +411 -24
- package/src/internal/uploads.ts +81 -26
- package/src/internal/utils/env.ts +9 -5
- package/src/internal/ws.ts +24 -3
- package/src/lib/AbstractChatCompletionRunner.ts +33 -6
- package/src/lib/AssistantStream.ts +262 -22
- package/src/lib/ChatCompletionStream.ts +1467 -103
- package/src/lib/EventEmitter.ts +91 -7
- package/src/lib/EventStream.ts +1483 -10
- package/src/lib/ResponsesParser.ts +4 -1
- package/src/lib/Util.ts +7 -5
- package/src/lib/assistant-run-polling.ts +27 -0
- package/src/lib/embeddings.ts +65 -0
- package/src/lib/file-processing.ts +36 -0
- package/src/lib/parser.ts +19 -4
- package/src/lib/polling.ts +144 -0
- package/src/lib/responses/ResponseStream.ts +22 -10
- package/src/lib/vector-store-polling.ts +46 -0
- package/src/lib/vector-store-upload.ts +53 -0
- package/src/lib/webhook-signature.ts +164 -0
- package/src/realtime/internal-base.ts +15 -0
- package/src/realtime/websocket.ts +130 -27
- package/src/realtime/ws.ts +15 -2
- package/src/resources/admin/organization/index.ts +2 -0
- package/src/resources/admin/organization/organization.ts +4 -0
- package/src/resources/admin/organization/projects/index.ts +1 -0
- package/src/resources/admin/organization/projects/projects.ts +37 -4
- package/src/resources/admin/organization/usage.ts +83 -0
- package/src/resources/beta/responses/internal-base.ts +2 -0
- package/src/resources/beta/responses/responses.ts +9 -12
- package/src/resources/beta/threads/runs/index.ts +1 -1
- package/src/resources/beta/threads/runs/runs.ts +2 -45
- package/src/resources/chat/completions/completions.ts +7 -0
- package/src/resources/embeddings.ts +2 -42
- package/src/resources/files.ts +2 -19
- package/src/resources/images.ts +12 -22
- package/src/resources/realtime/api.md +1 -0
- package/src/resources/realtime/calls.ts +46 -0
- package/src/resources/realtime/index.ts +7 -1
- package/src/resources/realtime/realtime.ts +5 -3
- package/src/resources/responses/internal-base.ts +2 -0
- package/src/resources/responses/responses.ts +9 -12
- package/src/resources/vector-stores/file-batches.ts +8 -77
- package/src/resources/vector-stores/files.ts +3 -43
- package/src/resources/webhooks/webhooks.ts +5 -61
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { partialParse } from '../_vendor/partial-json-parser/parser';
|
|
1
|
+
import { MalformedJSON, partialParse } from '../_vendor/partial-json-parser/parser';
|
|
2
2
|
import {
|
|
3
3
|
APIUserAbortError,
|
|
4
4
|
ContentFilterFinishReasonError,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
OpenAIError,
|
|
7
7
|
} from '../error';
|
|
8
8
|
import type OpenAI from '../index';
|
|
9
|
-
import type
|
|
9
|
+
import { observeJSONRequestBody, type RequestOptions } from '../internal/request-options';
|
|
10
10
|
import type { ReadableStream } from '../internal/shim-types';
|
|
11
11
|
import { uuid4 } from '../internal/utils/uuid';
|
|
12
12
|
import { hasOwn } from '../internal/utils/values';
|
|
@@ -36,6 +36,18 @@ import { Stream } from '../streaming';
|
|
|
36
36
|
import { AbstractChatCompletionRunner } from './AbstractChatCompletionRunner';
|
|
37
37
|
import type { AbstractChatCompletionRunnerEvents } from './AbstractChatCompletionRunner';
|
|
38
38
|
|
|
39
|
+
function parseStructuredStreamingJSON(content: string): unknown {
|
|
40
|
+
try {
|
|
41
|
+
return partialParse(content);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof MalformedJSON || error instanceof SyntaxError) {
|
|
44
|
+
return parseResponseFormatContent({ type: 'json_schema', $parseRaw: undefined }, content);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
/** An incremental assistant-text event and its accumulated state. */
|
|
40
52
|
export interface ContentDeltaEvent {
|
|
41
53
|
/** The new text received in this chunk. */
|
|
@@ -256,11 +268,64 @@ type PartialToolCallSnapshot = {
|
|
|
256
268
|
|
|
257
269
|
interface ChoiceEventState {
|
|
258
270
|
content_done: boolean;
|
|
271
|
+
content_parse_state: PartialJSONParseState | undefined;
|
|
259
272
|
refusal_done: boolean;
|
|
260
273
|
logprobs_content_done: boolean;
|
|
261
274
|
logprobs_refusal_done: boolean;
|
|
262
275
|
current_tool_call_index: number | null;
|
|
263
276
|
done_tool_calls: Set<number>;
|
|
277
|
+
tool_call_parse_states: Map<number, PartialJSONParseState>;
|
|
278
|
+
tool_call_identities: Map<number, ToolCallIdentity>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
interface CapturedToolCallDeltaFrame {
|
|
282
|
+
readonly index: number;
|
|
283
|
+
readonly arguments_delta: string;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
interface CapturedChoiceToolCallFrames {
|
|
287
|
+
readonly index: number;
|
|
288
|
+
readonly tool_calls: readonly CapturedToolCallDeltaFrame[];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
interface ValidatedChoiceSnapshot {
|
|
292
|
+
message: ChatCompletionSnapshot.Choice.Message;
|
|
293
|
+
content: string | null | undefined;
|
|
294
|
+
refusal: string | null | undefined;
|
|
295
|
+
toolCallCollection: ChatCompletionSnapshot.Choice.Message.ToolCall[] | undefined;
|
|
296
|
+
toolCalls: ReadonlyMap<number, ValidatedToolCallSnapshot>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
interface ValidatedToolCallSnapshot {
|
|
300
|
+
tool: ChatCompletionSnapshot.Choice.Message.ToolCall;
|
|
301
|
+
function: ChatCompletionSnapshot.Choice.Message.ToolCall.Function;
|
|
302
|
+
type: 'function';
|
|
303
|
+
name: string;
|
|
304
|
+
arguments: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface ToolCallIdentity {
|
|
308
|
+
type: 'function';
|
|
309
|
+
name: string;
|
|
310
|
+
parseable: boolean;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
interface PartialJSONParseState {
|
|
314
|
+
bytes: number;
|
|
315
|
+
depth: number;
|
|
316
|
+
fragments: number;
|
|
317
|
+
work: number;
|
|
318
|
+
escaped: boolean;
|
|
319
|
+
has_non_whitespace: boolean;
|
|
320
|
+
in_string: boolean;
|
|
321
|
+
last_parsed_bytes: number;
|
|
322
|
+
pending_high_surrogate: boolean;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
interface PartialJSONParseBudget {
|
|
326
|
+
bytes: number;
|
|
327
|
+
fragments: number;
|
|
328
|
+
work: number;
|
|
264
329
|
}
|
|
265
330
|
|
|
266
331
|
// The Chat Completions schema limits n to 128. Replayed streams do not retain
|
|
@@ -268,6 +333,287 @@ interface ChoiceEventState {
|
|
|
268
333
|
// conservative ceiling to prevent sparse tool-call arrays from growing unbounded.
|
|
269
334
|
const MAX_STREAM_CHOICES = 128;
|
|
270
335
|
const MAX_STREAM_TOOL_CALLS = 128;
|
|
336
|
+
const MAX_PARTIAL_JSON_BYTES = 16 * 1024 * 1024;
|
|
337
|
+
const MAX_PARTIAL_JSON_FRAGMENTS = 65_536;
|
|
338
|
+
const MAX_PARTIAL_JSON_DEPTH = 128;
|
|
339
|
+
const MAX_PARTIAL_JSON_PARSE_WORK = 64 * 1024 * 1024;
|
|
340
|
+
const EAGER_PARTIAL_JSON_BYTES = 1024;
|
|
341
|
+
|
|
342
|
+
function createPartialJSONParseState(): PartialJSONParseState {
|
|
343
|
+
return {
|
|
344
|
+
bytes: 0,
|
|
345
|
+
depth: 0,
|
|
346
|
+
fragments: 0,
|
|
347
|
+
work: 0,
|
|
348
|
+
escaped: false,
|
|
349
|
+
has_non_whitespace: false,
|
|
350
|
+
in_string: false,
|
|
351
|
+
last_parsed_bytes: 0,
|
|
352
|
+
pending_high_surrogate: false,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function recordPartialJSONFragment(
|
|
357
|
+
state: PartialJSONParseState,
|
|
358
|
+
budget: PartialJSONParseBudget,
|
|
359
|
+
fragment: string,
|
|
360
|
+
validationWorkBudget?: PartialJSONParseBudget,
|
|
361
|
+
): boolean {
|
|
362
|
+
if (budget.fragments >= MAX_PARTIAL_JSON_FRAGMENTS) {
|
|
363
|
+
throw new OpenAIError('Chat completion stream exceeded its structured JSON fragment limit');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let bytes = 0;
|
|
367
|
+
let { depth, escaped, has_non_whitespace: hasNonWhitespace, in_string: inString } = state;
|
|
368
|
+
let completed = false;
|
|
369
|
+
let firstCharacter = true;
|
|
370
|
+
|
|
371
|
+
for (const character of fragment) {
|
|
372
|
+
const previousBytes = bytes;
|
|
373
|
+
const codePoint = character.codePointAt(0)!;
|
|
374
|
+
if (firstCharacter && state.pending_high_surrogate && codePoint >= 0xdc_00 && codePoint <= 0xdf_ff) {
|
|
375
|
+
bytes += 1;
|
|
376
|
+
} else if (codePoint <= 0x7f) {
|
|
377
|
+
bytes += 1;
|
|
378
|
+
} else if (codePoint <= 0x7_ff) {
|
|
379
|
+
bytes += 2;
|
|
380
|
+
} else if (codePoint <= 0xff_ff) {
|
|
381
|
+
bytes += 3;
|
|
382
|
+
} else {
|
|
383
|
+
bytes += 4;
|
|
384
|
+
}
|
|
385
|
+
firstCharacter = false;
|
|
386
|
+
if (budget.bytes + bytes > MAX_PARTIAL_JSON_BYTES) {
|
|
387
|
+
throw new OpenAIError('Chat completion stream exceeded its structured JSON byte limit');
|
|
388
|
+
}
|
|
389
|
+
if (validationWorkBudget && validationWorkBudget.work + bytes > MAX_PARTIAL_JSON_PARSE_WORK) {
|
|
390
|
+
validationWorkBudget.work += previousBytes;
|
|
391
|
+
throw new OpenAIError('Chat completion stream exceeded its structured JSON parse-work limit');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (character !== ' ' && character !== '\n' && character !== '\r' && character !== '\t') {
|
|
395
|
+
hasNonWhitespace = true;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (inString) {
|
|
399
|
+
if (escaped) {
|
|
400
|
+
escaped = false;
|
|
401
|
+
} else if (character === '\\') {
|
|
402
|
+
escaped = true;
|
|
403
|
+
} else if (character === '"') {
|
|
404
|
+
inString = false;
|
|
405
|
+
completed ||= depth === 0;
|
|
406
|
+
}
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (character === '"') {
|
|
411
|
+
inString = true;
|
|
412
|
+
} else if (character === '{' || character === '[') {
|
|
413
|
+
depth += 1;
|
|
414
|
+
if (depth > MAX_PARTIAL_JSON_DEPTH) {
|
|
415
|
+
throw new OpenAIError('Chat completion stream exceeded its structured JSON nesting depth limit');
|
|
416
|
+
}
|
|
417
|
+
} else if ((character === '}' || character === ']') && depth > 0) {
|
|
418
|
+
depth -= 1;
|
|
419
|
+
completed ||= depth === 0;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
state.bytes += bytes;
|
|
424
|
+
state.fragments += 1;
|
|
425
|
+
state.depth = depth;
|
|
426
|
+
state.escaped = escaped;
|
|
427
|
+
state.has_non_whitespace = hasNonWhitespace;
|
|
428
|
+
state.in_string = inString;
|
|
429
|
+
if (fragment.length > 0) {
|
|
430
|
+
const finalCodeUnit = fragment.codePointAt(fragment.length - 1) ?? 0;
|
|
431
|
+
state.pending_high_surrogate = finalCodeUnit >= 0xd8_00 && finalCodeUnit <= 0xdb_ff;
|
|
432
|
+
}
|
|
433
|
+
budget.bytes += bytes;
|
|
434
|
+
budget.fragments += 1;
|
|
435
|
+
if (validationWorkBudget) {
|
|
436
|
+
validationWorkBudget.work += bytes;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!hasNonWhitespace || bytes === 0) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const minimumGrowth = Math.max(EAGER_PARTIAL_JSON_BYTES, Math.floor(state.last_parsed_bytes / 2));
|
|
444
|
+
if (
|
|
445
|
+
state.bytes > EAGER_PARTIAL_JSON_BYTES &&
|
|
446
|
+
!completed &&
|
|
447
|
+
state.bytes - state.last_parsed_bytes < minimumGrowth
|
|
448
|
+
) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function reservePartialJSONParse(state: PartialJSONParseState, budget: PartialJSONParseBudget): boolean {
|
|
456
|
+
if (budget.work + state.bytes > MAX_PARTIAL_JSON_PARSE_WORK) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
budget.work += state.bytes;
|
|
461
|
+
state.work += state.bytes;
|
|
462
|
+
state.last_parsed_bytes = state.bytes;
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function captureStructuredJSONSnapshot(
|
|
467
|
+
snapshot: object,
|
|
468
|
+
property: 'content' | 'arguments' | 'refusal',
|
|
469
|
+
): string | null | undefined {
|
|
470
|
+
const descriptor = Object.getOwnPropertyDescriptor(snapshot, property);
|
|
471
|
+
if (!descriptor) {
|
|
472
|
+
let prototype = Object.getPrototypeOf(snapshot) as object | null;
|
|
473
|
+
for (let depth = 0; prototype !== null; depth += 1) {
|
|
474
|
+
if (depth >= MAX_PARTIAL_JSON_DEPTH || Object.getOwnPropertyDescriptor(prototype, property)) {
|
|
475
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
476
|
+
}
|
|
477
|
+
prototype = Object.getPrototypeOf(prototype) as object | null;
|
|
478
|
+
}
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
if (
|
|
482
|
+
!('value' in descriptor) ||
|
|
483
|
+
(typeof descriptor.value !== 'string' && descriptor.value !== null && descriptor.value !== undefined)
|
|
484
|
+
) {
|
|
485
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return descriptor.value as string | null | undefined;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function captureStructuredMessageSnapshot(
|
|
492
|
+
choice: ChatCompletionSnapshot.Choice,
|
|
493
|
+
): ChatCompletionSnapshot.Choice.Message {
|
|
494
|
+
const descriptor = Object.getOwnPropertyDescriptor(choice, 'message');
|
|
495
|
+
if (
|
|
496
|
+
!descriptor ||
|
|
497
|
+
!('value' in descriptor) ||
|
|
498
|
+
typeof descriptor.value !== 'object' ||
|
|
499
|
+
descriptor.value === null
|
|
500
|
+
) {
|
|
501
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return descriptor.value as ChatCompletionSnapshot.Choice.Message;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function captureSnapshotArray<Item>(
|
|
508
|
+
snapshot: object,
|
|
509
|
+
property: 'choices' | 'tool_calls',
|
|
510
|
+
maximum: number,
|
|
511
|
+
kind: 'choice' | 'tool-call',
|
|
512
|
+
): Item[] | undefined {
|
|
513
|
+
const descriptor = Object.getOwnPropertyDescriptor(snapshot, property);
|
|
514
|
+
if (!descriptor) {
|
|
515
|
+
let prototype = Object.getPrototypeOf(snapshot) as object | null;
|
|
516
|
+
for (let depth = 0; prototype !== null; depth += 1) {
|
|
517
|
+
if (depth >= MAX_PARTIAL_JSON_DEPTH || Object.getOwnPropertyDescriptor(prototype, property)) {
|
|
518
|
+
throw new OpenAIError(`Chat completion stream contains an unsafe snapshot ${kind} collection`);
|
|
519
|
+
}
|
|
520
|
+
prototype = Object.getPrototypeOf(prototype) as object | null;
|
|
521
|
+
}
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
if (!('value' in descriptor) || !Array.isArray(descriptor.value)) {
|
|
525
|
+
throw new OpenAIError(`Chat completion stream contains an unsafe snapshot ${kind} collection`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const length = Object.getOwnPropertyDescriptor(descriptor.value, 'length');
|
|
529
|
+
if (!length || !('value' in length) || !Number.isSafeInteger(length.value) || length.value > maximum) {
|
|
530
|
+
throw new OpenAIError(`Chat completion stream exceeded its snapshot ${kind} limit`);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return descriptor.value as Item[];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function captureSnapshotArrayItem<Item>(array: Item[], index: number): Item | undefined {
|
|
537
|
+
const descriptor = Object.getOwnPropertyDescriptor(array, index);
|
|
538
|
+
if (!descriptor) {
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
if (!('value' in descriptor)) {
|
|
542
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return descriptor.value as Item;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function mapCapturedSnapshotArray<Item, Mapped>(
|
|
549
|
+
array: Item[],
|
|
550
|
+
maximum: number,
|
|
551
|
+
kind: 'choice' | 'tool-call',
|
|
552
|
+
map: (item: Item, index: number) => Mapped,
|
|
553
|
+
): Mapped[] {
|
|
554
|
+
const descriptor = Object.getOwnPropertyDescriptor(array, 'length');
|
|
555
|
+
const length: unknown = descriptor && 'value' in descriptor ? descriptor.value : undefined;
|
|
556
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0 || length > maximum) {
|
|
557
|
+
throw new OpenAIError(`Chat completion stream exceeded its snapshot ${kind} limit`);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const mapped: Mapped[] = [];
|
|
561
|
+
mapped.length = length;
|
|
562
|
+
for (let index = 0; index < length; index += 1) {
|
|
563
|
+
const item = Object.getOwnPropertyDescriptor(array, index);
|
|
564
|
+
if (!item) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (!('value' in item)) {
|
|
568
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
569
|
+
}
|
|
570
|
+
mapped[index] = map(item.value as Item, index);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return mapped;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function validateStructuredJSONSnapshot(
|
|
577
|
+
value: string,
|
|
578
|
+
budget?: PartialJSONParseBudget,
|
|
579
|
+
validationWorkBudget?: PartialJSONParseBudget,
|
|
580
|
+
): string {
|
|
581
|
+
const state = createPartialJSONParseState();
|
|
582
|
+
const parseBudget = budget ?? { bytes: 0, fragments: 0, work: 0 };
|
|
583
|
+
recordPartialJSONFragment(state, parseBudget, value, validationWorkBudget);
|
|
584
|
+
if (!reservePartialJSONParse(state, parseBudget)) {
|
|
585
|
+
throw new OpenAIError('Chat completion stream exceeded its structured JSON parse-work limit');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return value;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function ownFunctionToolIdentity(
|
|
592
|
+
toolCall: PartialToolCallSnapshot,
|
|
593
|
+
): Pick<ToolCallIdentity, 'type' | 'name'> | undefined {
|
|
594
|
+
const type = Object.getOwnPropertyDescriptor(toolCall, 'type');
|
|
595
|
+
const fn = Object.getOwnPropertyDescriptor(toolCall, 'function');
|
|
596
|
+
if (!type || !('value' in type) || type.value !== 'function' || !fn || !('value' in fn)) {
|
|
597
|
+
return undefined;
|
|
598
|
+
}
|
|
599
|
+
if (typeof fn.value !== 'object' || fn.value === null) {
|
|
600
|
+
return undefined;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const name = Object.getOwnPropertyDescriptor(fn.value, 'name');
|
|
604
|
+
if (!name || !('value' in name) || typeof name.value !== 'string' || name.value.length === 0) {
|
|
605
|
+
return undefined;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return { type: 'function', name: name.value };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function assertBoundToolCallIdentity(toolCall: PartialToolCallSnapshot, identity: ToolCallIdentity): void {
|
|
612
|
+
const current = ownFunctionToolIdentity(toolCall);
|
|
613
|
+
if (!current || current.name !== identity.name || current.type !== identity.type) {
|
|
614
|
+
throw new OpenAIError('Chat completion stream contains a changed tool call identity');
|
|
615
|
+
}
|
|
616
|
+
}
|
|
271
617
|
|
|
272
618
|
function assignOwnProperties<T extends object>(target: T, source: object): T {
|
|
273
619
|
if (Object.prototype.propertyIsEnumerable.call(source, '__proto__') && !hasOwn(target, '__proto__')) {
|
|
@@ -282,6 +628,625 @@ function assignOwnProperties<T extends object>(target: T, source: object): T {
|
|
|
282
628
|
return Object.assign(target, source);
|
|
283
629
|
}
|
|
284
630
|
|
|
631
|
+
function cloneParserConfigObject<Value extends object>(
|
|
632
|
+
value: Value,
|
|
633
|
+
stableFields: readonly PropertyKey[] = [],
|
|
634
|
+
): Value {
|
|
635
|
+
const descriptors: PropertyDescriptorMap = Object.getOwnPropertyDescriptors(value);
|
|
636
|
+
for (const field of stableFields) {
|
|
637
|
+
const descriptor = descriptors[field];
|
|
638
|
+
if (!descriptor && !(field in value)) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
descriptors[field] = {
|
|
643
|
+
value: descriptor && 'value' in descriptor ? descriptor.value : Reflect.get(value, field, value),
|
|
644
|
+
enumerable: descriptor?.enumerable ?? false,
|
|
645
|
+
configurable: descriptor?.configurable ?? true,
|
|
646
|
+
writable: descriptor && 'writable' in descriptor ? descriptor.writable : false,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return Object.create(Object.getPrototypeOf(value), descriptors) as Value;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function snapshotChatCompletionParserParams(params: ChatCompletionCreateParams): ChatCompletionCreateParams {
|
|
654
|
+
const snapshot = cloneParserConfigObject(params);
|
|
655
|
+
|
|
656
|
+
if (params.tools) {
|
|
657
|
+
const stableTools: NonNullable<ChatCompletionCreateParams['tools']> = [];
|
|
658
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(params.tools, 'length');
|
|
659
|
+
const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
|
|
660
|
+
const toolCount =
|
|
661
|
+
typeof length === 'number' && Number.isSafeInteger(length) && length >= 0
|
|
662
|
+
? Math.min(length, MAX_STREAM_TOOL_CALLS)
|
|
663
|
+
: 0;
|
|
664
|
+
|
|
665
|
+
for (let index = 0; index < toolCount; index += 1) {
|
|
666
|
+
const item = Object.getOwnPropertyDescriptor(params.tools, String(index));
|
|
667
|
+
if (!item || !('value' in item)) {
|
|
668
|
+
stableTools.length = index + 1;
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const tool = item.value as NonNullable<ChatCompletionCreateParams['tools']>[number];
|
|
673
|
+
const stableTool = cloneParserConfigObject(tool, [
|
|
674
|
+
'type',
|
|
675
|
+
'$brand',
|
|
676
|
+
'$parseRaw',
|
|
677
|
+
'$callback',
|
|
678
|
+
'function',
|
|
679
|
+
]);
|
|
680
|
+
const descriptors = Object.getOwnPropertyDescriptors(stableTool);
|
|
681
|
+
|
|
682
|
+
if (isChatCompletionFunctionTool(stableTool)) {
|
|
683
|
+
const descriptor = descriptors.function;
|
|
684
|
+
descriptors.function = {
|
|
685
|
+
...(descriptor && 'value' in descriptor
|
|
686
|
+
? descriptor
|
|
687
|
+
: { configurable: true, enumerable: true, writable: true }),
|
|
688
|
+
value: cloneParserConfigObject(stableTool.function, ['name', 'strict']),
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
stableTools[index] = Object.create(Object.getPrototypeOf(tool), descriptors) as typeof tool;
|
|
693
|
+
}
|
|
694
|
+
snapshot.tools = stableTools;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (params.response_format) {
|
|
698
|
+
snapshot.response_format = cloneParserConfigObject(params.response_format, [
|
|
699
|
+
'type',
|
|
700
|
+
'$brand',
|
|
701
|
+
'$parseRaw',
|
|
702
|
+
]);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return snapshot;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
type ChatCompletionInputTool = NonNullable<ChatCompletionCreateParams['tools']>[number];
|
|
709
|
+
type ChatCompletionResponseFormat = NonNullable<ChatCompletionCreateParams['response_format']>;
|
|
710
|
+
|
|
711
|
+
interface SerializedFunctionParserConfig {
|
|
712
|
+
source: ChatCompletionInputTool | undefined;
|
|
713
|
+
schemaMatches: boolean;
|
|
714
|
+
name?: string;
|
|
715
|
+
strict?: boolean;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
interface SerializedToolParserConfig {
|
|
719
|
+
source: ChatCompletionInputTool | undefined;
|
|
720
|
+
type?: string;
|
|
721
|
+
function?: SerializedFunctionParserConfig;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
interface SerializedResponseParserConfig {
|
|
725
|
+
source: ChatCompletionResponseFormat | undefined;
|
|
726
|
+
schemaMatches: boolean;
|
|
727
|
+
type?: string;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
interface SerializedParserSchemaBudget {
|
|
731
|
+
nodes: number;
|
|
732
|
+
bytes: number;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const MAX_SERIALIZED_PARSER_SCHEMA_NODES = 4096;
|
|
736
|
+
const stringifyParserSchemaValue = JSON.stringify;
|
|
737
|
+
const MAX_SERIALIZED_PARSER_SCHEMA_BYTES = 1024 * 1024;
|
|
738
|
+
const MAX_SERIALIZED_PARSER_SCHEMA_DEPTH = 64;
|
|
739
|
+
const OMITTED_SERIALIZED_PARSER_VALUE = Symbol('omitted serialized parser value');
|
|
740
|
+
const UNSAFE_SERIALIZED_PARSER_VALUE = Symbol('unsafe serialized parser value');
|
|
741
|
+
|
|
742
|
+
type CanonicalSerializedParserValue =
|
|
743
|
+
| string
|
|
744
|
+
| typeof OMITTED_SERIALIZED_PARSER_VALUE
|
|
745
|
+
| typeof UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
746
|
+
|
|
747
|
+
function canonicalSerializedParserSchema(
|
|
748
|
+
value: unknown,
|
|
749
|
+
budget: SerializedParserSchemaBudget,
|
|
750
|
+
): string | undefined {
|
|
751
|
+
const ancestors = new WeakSet<object>();
|
|
752
|
+
|
|
753
|
+
const charge = (bytes: number): boolean => {
|
|
754
|
+
if (
|
|
755
|
+
!Number.isSafeInteger(bytes) ||
|
|
756
|
+
bytes < 0 ||
|
|
757
|
+
budget.bytes + bytes > MAX_SERIALIZED_PARSER_SCHEMA_BYTES
|
|
758
|
+
) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
budget.bytes += bytes;
|
|
762
|
+
return true;
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
const visit = (current: unknown, depth: number): CanonicalSerializedParserValue => {
|
|
766
|
+
if (depth > MAX_SERIALIZED_PARSER_SCHEMA_DEPTH || budget.nodes >= MAX_SERIALIZED_PARSER_SCHEMA_NODES) {
|
|
767
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
768
|
+
}
|
|
769
|
+
budget.nodes += 1;
|
|
770
|
+
|
|
771
|
+
if (current === undefined || typeof current === 'function' || typeof current === 'symbol') {
|
|
772
|
+
return OMITTED_SERIALIZED_PARSER_VALUE;
|
|
773
|
+
}
|
|
774
|
+
if (typeof current === 'bigint') {
|
|
775
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
776
|
+
}
|
|
777
|
+
if (current === null || typeof current === 'boolean' || typeof current === 'number') {
|
|
778
|
+
const serialized = stringifyParserSchemaValue(current);
|
|
779
|
+
return typeof serialized === 'string' && charge(serialized.length)
|
|
780
|
+
? serialized
|
|
781
|
+
: UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
782
|
+
}
|
|
783
|
+
if (typeof current === 'string') {
|
|
784
|
+
if (!charge(current.length * 6 + 2)) {
|
|
785
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
786
|
+
}
|
|
787
|
+
return stringifyParserSchemaValue(current);
|
|
788
|
+
}
|
|
789
|
+
if (typeof current !== 'object' || ancestors.has(current)) {
|
|
790
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const array = Array.isArray(current);
|
|
794
|
+
const prototype = Object.getPrototypeOf(current) as object | null;
|
|
795
|
+
if (
|
|
796
|
+
(array && prototype !== Array.prototype) ||
|
|
797
|
+
(!array && prototype !== null && prototype !== Object.prototype)
|
|
798
|
+
) {
|
|
799
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
for (
|
|
803
|
+
let owner: object | null = current;
|
|
804
|
+
owner !== null;
|
|
805
|
+
owner = Object.getPrototypeOf(owner) as object | null
|
|
806
|
+
) {
|
|
807
|
+
const serializer = Object.getOwnPropertyDescriptor(owner, 'toJSON');
|
|
808
|
+
if (!serializer) {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (!('value' in serializer) || typeof serializer.value === 'function') {
|
|
812
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
813
|
+
}
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
ancestors.add(current);
|
|
818
|
+
try {
|
|
819
|
+
if (!charge(2)) {
|
|
820
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (array) {
|
|
824
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(current, 'length');
|
|
825
|
+
const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
|
|
826
|
+
if (
|
|
827
|
+
typeof length !== 'number' ||
|
|
828
|
+
!Number.isSafeInteger(length) ||
|
|
829
|
+
length < 0 ||
|
|
830
|
+
length > MAX_SERIALIZED_PARSER_SCHEMA_NODES - budget.nodes
|
|
831
|
+
) {
|
|
832
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const items: string[] = [];
|
|
836
|
+
for (let index = 0; index < length; index += 1) {
|
|
837
|
+
const key = String(index);
|
|
838
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
839
|
+
if (!descriptor) {
|
|
840
|
+
if (
|
|
841
|
+
Object.getOwnPropertyDescriptor(Array.prototype, key) ||
|
|
842
|
+
Object.getOwnPropertyDescriptor(Object.prototype, key)
|
|
843
|
+
) {
|
|
844
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
845
|
+
}
|
|
846
|
+
budget.nodes += 1;
|
|
847
|
+
if (!charge(4)) {
|
|
848
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
849
|
+
}
|
|
850
|
+
items.push('null');
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if (!('value' in descriptor)) {
|
|
854
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const item = visit(descriptor.value, depth + 1);
|
|
858
|
+
if (item === UNSAFE_SERIALIZED_PARSER_VALUE) {
|
|
859
|
+
return item;
|
|
860
|
+
}
|
|
861
|
+
items.push(item === OMITTED_SERIALIZED_PARSER_VALUE ? 'null' : item);
|
|
862
|
+
}
|
|
863
|
+
return `[${items.join(',')}]`;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const keys = Reflect.ownKeys(current);
|
|
867
|
+
if (keys.length > MAX_SERIALIZED_PARSER_SCHEMA_NODES - budget.nodes) {
|
|
868
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const entries: [string, unknown][] = [];
|
|
872
|
+
for (const key of keys) {
|
|
873
|
+
if (typeof key !== 'string') {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
877
|
+
if (!descriptor) {
|
|
878
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
879
|
+
}
|
|
880
|
+
if (!descriptor.enumerable) {
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
if (!('value' in descriptor)) {
|
|
884
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
885
|
+
}
|
|
886
|
+
entries.push([key, descriptor.value]);
|
|
887
|
+
}
|
|
888
|
+
entries.sort(([left], [right]) => {
|
|
889
|
+
if (left === right) {
|
|
890
|
+
return 0;
|
|
891
|
+
}
|
|
892
|
+
return left < right ? -1 : 1;
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
const fields: string[] = [];
|
|
896
|
+
for (const [key, entry] of entries) {
|
|
897
|
+
const normalized = visit(entry, depth + 1);
|
|
898
|
+
if (normalized === UNSAFE_SERIALIZED_PARSER_VALUE) {
|
|
899
|
+
return normalized;
|
|
900
|
+
}
|
|
901
|
+
if (normalized === OMITTED_SERIALIZED_PARSER_VALUE) {
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (!charge(key.length * 6 + 3)) {
|
|
905
|
+
return UNSAFE_SERIALIZED_PARSER_VALUE;
|
|
906
|
+
}
|
|
907
|
+
fields.push(`${stringifyParserSchemaValue(key)}:${normalized}`);
|
|
908
|
+
}
|
|
909
|
+
return `{${fields.join(',')}}`;
|
|
910
|
+
} finally {
|
|
911
|
+
ancestors.delete(current);
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
try {
|
|
916
|
+
const normalized = visit(value, 0);
|
|
917
|
+
return typeof normalized === 'string' ? normalized : undefined;
|
|
918
|
+
} catch {
|
|
919
|
+
return undefined;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function rememberSerializedParserSchema(
|
|
924
|
+
signatures: WeakMap<object, string>,
|
|
925
|
+
source: object,
|
|
926
|
+
holder: object,
|
|
927
|
+
key: string,
|
|
928
|
+
): void {
|
|
929
|
+
const parser = Object.getOwnPropertyDescriptor(source, '$parseRaw');
|
|
930
|
+
const schema = Object.getOwnPropertyDescriptor(holder, key);
|
|
931
|
+
if (
|
|
932
|
+
!parser ||
|
|
933
|
+
!('value' in parser) ||
|
|
934
|
+
typeof parser.value !== 'function' ||
|
|
935
|
+
!schema ||
|
|
936
|
+
!('value' in schema)
|
|
937
|
+
) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const normalized = canonicalSerializedParserSchema(schema.value, { nodes: 0, bytes: 0 });
|
|
942
|
+
if (normalized !== undefined) {
|
|
943
|
+
signatures.set(source, normalized);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function hasMatchingSerializedParserSchema(
|
|
948
|
+
signatures: WeakMap<object, string>,
|
|
949
|
+
source: object | undefined,
|
|
950
|
+
holder: object,
|
|
951
|
+
key: string,
|
|
952
|
+
value: unknown,
|
|
953
|
+
): boolean {
|
|
954
|
+
const expected = source && signatures.get(source);
|
|
955
|
+
const descriptor = Object.getOwnPropertyDescriptor(holder, key);
|
|
956
|
+
return (
|
|
957
|
+
expected !== undefined &&
|
|
958
|
+
descriptor !== undefined &&
|
|
959
|
+
'value' in descriptor &&
|
|
960
|
+
canonicalSerializedParserSchema(value, { nodes: 0, bytes: 0 }) === expected
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function serializedParserDescriptor(
|
|
965
|
+
descriptor: PropertyDescriptor | undefined,
|
|
966
|
+
value: unknown,
|
|
967
|
+
): PropertyDescriptor {
|
|
968
|
+
return descriptor && 'value' in descriptor
|
|
969
|
+
? { ...descriptor, value }
|
|
970
|
+
: { configurable: true, enumerable: true, writable: true, value };
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function shadowSerializedParserMetadata(
|
|
974
|
+
descriptors: PropertyDescriptorMap,
|
|
975
|
+
source: object,
|
|
976
|
+
fields: readonly string[],
|
|
977
|
+
): void {
|
|
978
|
+
for (const field of fields) {
|
|
979
|
+
const descriptor = descriptors[field];
|
|
980
|
+
if (!descriptor && !(field in source)) {
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
descriptors[field] =
|
|
984
|
+
descriptor && 'value' in descriptor
|
|
985
|
+
? { ...descriptor, value: undefined }
|
|
986
|
+
: {
|
|
987
|
+
configurable: descriptor?.configurable ?? true,
|
|
988
|
+
enumerable: descriptor?.enumerable ?? false,
|
|
989
|
+
writable: false,
|
|
990
|
+
value: undefined,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function snapshotSerializedParserTool(serialized: SerializedToolParserConfig): ChatCompletionInputTool {
|
|
996
|
+
const source =
|
|
997
|
+
serialized.source ??
|
|
998
|
+
({
|
|
999
|
+
type: serialized.type,
|
|
1000
|
+
...(serialized.type === 'function' ? { function: {} } : {}),
|
|
1001
|
+
} as ChatCompletionInputTool);
|
|
1002
|
+
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
1003
|
+
descriptors.type = serializedParserDescriptor(descriptors.type, serialized.type);
|
|
1004
|
+
|
|
1005
|
+
if (serialized.type !== 'function' || !serialized.function) {
|
|
1006
|
+
if (descriptors.function) {
|
|
1007
|
+
descriptors.function = serializedParserDescriptor(descriptors.function, undefined);
|
|
1008
|
+
}
|
|
1009
|
+
shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw', '$callback']);
|
|
1010
|
+
return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionInputTool;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const descriptor = descriptors.function;
|
|
1014
|
+
const original =
|
|
1015
|
+
descriptor && 'value' in descriptor && typeof descriptor.value === 'object' && descriptor.value !== null
|
|
1016
|
+
? (descriptor.value as object)
|
|
1017
|
+
: {};
|
|
1018
|
+
const functionDescriptors = Object.getOwnPropertyDescriptors(original);
|
|
1019
|
+
functionDescriptors['name'] = serializedParserDescriptor(
|
|
1020
|
+
functionDescriptors['name'],
|
|
1021
|
+
serialized.function.name,
|
|
1022
|
+
);
|
|
1023
|
+
functionDescriptors['strict'] = serializedParserDescriptor(
|
|
1024
|
+
functionDescriptors['strict'],
|
|
1025
|
+
serialized.function.strict,
|
|
1026
|
+
);
|
|
1027
|
+
descriptors.function = serializedParserDescriptor(
|
|
1028
|
+
descriptor,
|
|
1029
|
+
Object.create(Object.getPrototypeOf(original), functionDescriptors),
|
|
1030
|
+
);
|
|
1031
|
+
if (!serialized.function.schemaMatches) {
|
|
1032
|
+
shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw', '$callback']);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionInputTool;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
function snapshotSerializedResponseFormat(
|
|
1039
|
+
serialized: SerializedResponseParserConfig,
|
|
1040
|
+
): ChatCompletionResponseFormat {
|
|
1041
|
+
const source = serialized.source ?? ({ type: serialized.type } as ChatCompletionResponseFormat);
|
|
1042
|
+
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
1043
|
+
descriptors.type = serializedParserDescriptor(descriptors.type, serialized.type);
|
|
1044
|
+
if (serialized.type !== 'json_schema' || !serialized.source || !serialized.schemaMatches) {
|
|
1045
|
+
shadowSerializedParserMetadata(descriptors, source, ['$brand', '$parseRaw']);
|
|
1046
|
+
}
|
|
1047
|
+
return Object.create(Object.getPrototypeOf(source), descriptors) as ChatCompletionResponseFormat;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function ownSerializedParserObject(holder: object, key: string): object | undefined {
|
|
1051
|
+
const descriptor = Object.getOwnPropertyDescriptor(holder, key);
|
|
1052
|
+
if (!descriptor || !('value' in descriptor)) {
|
|
1053
|
+
return undefined;
|
|
1054
|
+
}
|
|
1055
|
+
const { value } = descriptor;
|
|
1056
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function observeSerializedChatCompletionParserParams(
|
|
1060
|
+
body: ChatCompletionCreateParams,
|
|
1061
|
+
initial: ChatCompletionCreateParams,
|
|
1062
|
+
update: (params: ChatCompletionCreateParams) => void,
|
|
1063
|
+
): () => void {
|
|
1064
|
+
const originalToolOwners = new WeakMap<object, ChatCompletionInputTool>();
|
|
1065
|
+
const originalSchemaSignatures = new WeakMap<object, string>();
|
|
1066
|
+
// At most 128 tools plus one response format each receive an independent
|
|
1067
|
+
// 4,096-node/1 MiB source and wire allowance (129 MiB maximum per pass).
|
|
1068
|
+
if (body.tools) {
|
|
1069
|
+
for (let index = 0; index < body.tools.length && index < MAX_STREAM_TOOL_CALLS; index += 1) {
|
|
1070
|
+
const owner = ownSerializedParserObject(body.tools, String(index));
|
|
1071
|
+
const source = initial.tools?.[index];
|
|
1072
|
+
if (owner && source) {
|
|
1073
|
+
originalToolOwners.set(owner, source);
|
|
1074
|
+
const originalFunction = ownSerializedParserObject(source, 'function');
|
|
1075
|
+
if (originalFunction) {
|
|
1076
|
+
rememberSerializedParserSchema(originalSchemaSignatures, source, originalFunction, 'parameters');
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (initial.response_format) {
|
|
1082
|
+
rememberSerializedParserSchema(
|
|
1083
|
+
originalSchemaSignatures,
|
|
1084
|
+
initial.response_format,
|
|
1085
|
+
initial.response_format,
|
|
1086
|
+
'json_schema',
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
let root: object | undefined;
|
|
1090
|
+
let tools: object | undefined;
|
|
1091
|
+
let responseFormat: object | undefined;
|
|
1092
|
+
let responseFrame: SerializedResponseParserConfig | undefined;
|
|
1093
|
+
let frames: (SerializedToolParserConfig | undefined)[] = [];
|
|
1094
|
+
let toolFrames = new WeakMap<object, SerializedToolParserConfig>();
|
|
1095
|
+
let actualToolOwners = new Map<number, object | undefined>();
|
|
1096
|
+
let functionFrames = new WeakMap<object, SerializedFunctionParserConfig>();
|
|
1097
|
+
|
|
1098
|
+
return observeJSONRequestBody(body, {
|
|
1099
|
+
value(holder, key, value) {
|
|
1100
|
+
if (!root && key === '' && typeof value === 'object' && value !== null) {
|
|
1101
|
+
root = value;
|
|
1102
|
+
tools = undefined;
|
|
1103
|
+
responseFormat = undefined;
|
|
1104
|
+
responseFrame = undefined;
|
|
1105
|
+
frames = [];
|
|
1106
|
+
toolFrames = new WeakMap();
|
|
1107
|
+
actualToolOwners = new Map();
|
|
1108
|
+
functionFrames = new WeakMap();
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (holder === root && key === 'response_format') {
|
|
1113
|
+
if (typeof value === 'object' && value !== null) {
|
|
1114
|
+
responseFormat = value;
|
|
1115
|
+
const owner = ownSerializedParserObject(holder, key);
|
|
1116
|
+
responseFrame = {
|
|
1117
|
+
source: owner === body.response_format ? initial.response_format : undefined,
|
|
1118
|
+
schemaMatches: false,
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (holder === root && key === 'tools') {
|
|
1125
|
+
if (Array.isArray(value)) {
|
|
1126
|
+
tools = new Proxy(value, {
|
|
1127
|
+
get(target, property) {
|
|
1128
|
+
const actual = Reflect.get(target, property, target) as unknown;
|
|
1129
|
+
if (typeof property === 'string') {
|
|
1130
|
+
const index = Number(property);
|
|
1131
|
+
if (
|
|
1132
|
+
Number.isSafeInteger(index) &&
|
|
1133
|
+
index >= 0 &&
|
|
1134
|
+
index < MAX_STREAM_TOOL_CALLS &&
|
|
1135
|
+
String(index) === property
|
|
1136
|
+
) {
|
|
1137
|
+
actualToolOwners.set(
|
|
1138
|
+
index,
|
|
1139
|
+
typeof actual === 'object' && actual !== null ? actual : undefined,
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return actual;
|
|
1144
|
+
},
|
|
1145
|
+
});
|
|
1146
|
+
return tools;
|
|
1147
|
+
}
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (holder === tools) {
|
|
1152
|
+
const index = Number(key);
|
|
1153
|
+
if (
|
|
1154
|
+
!Number.isSafeInteger(index) ||
|
|
1155
|
+
index < 0 ||
|
|
1156
|
+
index >= MAX_STREAM_TOOL_CALLS ||
|
|
1157
|
+
typeof value !== 'object' ||
|
|
1158
|
+
value === null
|
|
1159
|
+
) {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
const owner = actualToolOwners.get(index);
|
|
1163
|
+
const source = owner ? originalToolOwners.get(owner) : undefined;
|
|
1164
|
+
const frame: SerializedToolParserConfig = { source };
|
|
1165
|
+
frames[index] = frame;
|
|
1166
|
+
toolFrames.set(value, frame);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const tool = toolFrames.get(holder);
|
|
1171
|
+
if (tool) {
|
|
1172
|
+
if (key === 'type' && typeof value === 'string') {
|
|
1173
|
+
tool.type = value;
|
|
1174
|
+
} else if (key === 'function' && typeof value === 'object' && value !== null) {
|
|
1175
|
+
const fn: SerializedFunctionParserConfig = { source: tool.source, schemaMatches: false };
|
|
1176
|
+
tool.function = fn;
|
|
1177
|
+
functionFrames.set(value, fn);
|
|
1178
|
+
}
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (holder === responseFormat && responseFrame) {
|
|
1183
|
+
if (key === 'type' && typeof value === 'string') {
|
|
1184
|
+
responseFrame.type = value;
|
|
1185
|
+
} else if (key === 'json_schema') {
|
|
1186
|
+
responseFrame.schemaMatches = hasMatchingSerializedParserSchema(
|
|
1187
|
+
originalSchemaSignatures,
|
|
1188
|
+
responseFrame.source,
|
|
1189
|
+
holder,
|
|
1190
|
+
key,
|
|
1191
|
+
value,
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const fn = functionFrames.get(holder);
|
|
1198
|
+
if (fn) {
|
|
1199
|
+
if (key === 'name' && typeof value === 'string') {
|
|
1200
|
+
fn.name = value;
|
|
1201
|
+
} else if (key === 'strict' && typeof value === 'boolean') {
|
|
1202
|
+
fn.strict = value;
|
|
1203
|
+
} else if (key === 'parameters') {
|
|
1204
|
+
fn.schemaMatches = hasMatchingSerializedParserSchema(
|
|
1205
|
+
originalSchemaSignatures,
|
|
1206
|
+
fn.source,
|
|
1207
|
+
holder,
|
|
1208
|
+
key,
|
|
1209
|
+
value,
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return undefined;
|
|
1214
|
+
},
|
|
1215
|
+
complete() {
|
|
1216
|
+
if (!root) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const snapshot = cloneParserConfigObject(initial);
|
|
1220
|
+
if (tools) {
|
|
1221
|
+
const serializedTools: ChatCompletionInputTool[] = [];
|
|
1222
|
+
for (let index = 0; index < frames.length; index += 1) {
|
|
1223
|
+
const frame = frames[index];
|
|
1224
|
+
if (frame) {
|
|
1225
|
+
serializedTools[index] = snapshotSerializedParserTool(frame);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
snapshot.tools = serializedTools;
|
|
1229
|
+
} else {
|
|
1230
|
+
delete snapshot.tools;
|
|
1231
|
+
}
|
|
1232
|
+
if (responseFrame) {
|
|
1233
|
+
snapshot.response_format = snapshotSerializedResponseFormat(responseFrame);
|
|
1234
|
+
} else {
|
|
1235
|
+
delete snapshot.response_format;
|
|
1236
|
+
}
|
|
1237
|
+
update(snapshot);
|
|
1238
|
+
root = undefined;
|
|
1239
|
+
tools = undefined;
|
|
1240
|
+
responseFormat = undefined;
|
|
1241
|
+
responseFrame = undefined;
|
|
1242
|
+
frames = [];
|
|
1243
|
+
toolFrames = new WeakMap();
|
|
1244
|
+
actualToolOwners = new Map();
|
|
1245
|
+
functionFrames = new WeakMap();
|
|
1246
|
+
},
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
|
|
285
1250
|
/** Streams chat completion chunks while accumulating snapshots, parsed output, and events. */
|
|
286
1251
|
export class ChatCompletionStream<ParsedT = null>
|
|
287
1252
|
extends AbstractChatCompletionRunner<ChatCompletionStreamEvents<ParsedT>, ParsedT>
|
|
@@ -291,6 +1256,8 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
291
1256
|
#audioDoneChoiceIndexes: Set<number>;
|
|
292
1257
|
#choiceEventStates: ChoiceEventState[];
|
|
293
1258
|
#currentChatCompletionSnapshot: ChatCompletionSnapshot | undefined;
|
|
1259
|
+
#hasAutoParseableTool: boolean;
|
|
1260
|
+
#partialJSONParseBudget: PartialJSONParseBudget;
|
|
294
1261
|
|
|
295
1262
|
/** Creates an unstarted stream, retaining request parameters for structured-output parsing. */
|
|
296
1263
|
constructor(params: ChatCompletionCreateParams | null) {
|
|
@@ -298,6 +1265,27 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
298
1265
|
this.#params = params;
|
|
299
1266
|
this.#audioDoneChoiceIndexes = new Set();
|
|
300
1267
|
this.#choiceEventStates = [];
|
|
1268
|
+
this.#hasAutoParseableTool = false;
|
|
1269
|
+
const tools = params?.tools;
|
|
1270
|
+
const lengthDescriptor = tools && Object.getOwnPropertyDescriptor(tools, 'length');
|
|
1271
|
+
const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;
|
|
1272
|
+
if (tools && typeof length === 'number' && Number.isSafeInteger(length) && length >= 0) {
|
|
1273
|
+
for (let index = 0; index < Math.min(length, MAX_STREAM_TOOL_CALLS); index += 1) {
|
|
1274
|
+
const descriptor = Object.getOwnPropertyDescriptor(tools, String(index));
|
|
1275
|
+
if (!descriptor || !('value' in descriptor)) {
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const tool = descriptor.value as ChatCompletionInputTool;
|
|
1279
|
+
if (
|
|
1280
|
+
isChatCompletionFunctionTool(tool) &&
|
|
1281
|
+
(isAutoParsableTool(tool) || tool.function.strict === true)
|
|
1282
|
+
) {
|
|
1283
|
+
this.#hasAutoParseableTool = true;
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
this.#partialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
|
|
301
1289
|
}
|
|
302
1290
|
|
|
303
1291
|
/** The latest accumulated completion, or `undefined` before a chunk arrives or after finalization. */
|
|
@@ -341,6 +1329,7 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
341
1329
|
}
|
|
342
1330
|
this.#audioDoneChoiceIndexes = new Set();
|
|
343
1331
|
this.#currentChatCompletionSnapshot = undefined;
|
|
1332
|
+
this.#partialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
|
|
344
1333
|
}
|
|
345
1334
|
|
|
346
1335
|
#getChoiceEventState(choice: ChatCompletionSnapshot.Choice): ChoiceEventState {
|
|
@@ -351,11 +1340,14 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
351
1340
|
|
|
352
1341
|
state = {
|
|
353
1342
|
content_done: false,
|
|
1343
|
+
content_parse_state: undefined,
|
|
354
1344
|
refusal_done: false,
|
|
355
1345
|
logprobs_content_done: false,
|
|
356
1346
|
logprobs_refusal_done: false,
|
|
357
1347
|
done_tool_calls: new Set(),
|
|
358
1348
|
current_tool_call_index: null,
|
|
1349
|
+
tool_call_parse_states: new Map(),
|
|
1350
|
+
tool_call_identities: new Map(),
|
|
359
1351
|
};
|
|
360
1352
|
this.#choiceEventStates[choice.index] = state;
|
|
361
1353
|
return state;
|
|
@@ -366,45 +1358,52 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
366
1358
|
return;
|
|
367
1359
|
}
|
|
368
1360
|
|
|
369
|
-
const
|
|
1361
|
+
const capturedChoiceFrames = new WeakMap<object, CapturedChoiceToolCallFrames>();
|
|
1362
|
+
const completion = this.#accumulateChatCompletion(chunk, capturedChoiceFrames);
|
|
370
1363
|
this._emit('chunk', chunk, completion);
|
|
371
1364
|
|
|
372
1365
|
for (const choice of chunk.choices) {
|
|
373
|
-
const
|
|
1366
|
+
const capturedChoice = capturedChoiceFrames.get(choice);
|
|
1367
|
+
const choiceSnapshot = completion.choices[capturedChoice?.index ?? choice.index]!;
|
|
1368
|
+
const capturedToolCalls = capturedChoice?.tool_calls ?? [];
|
|
374
1369
|
const { delta } = choice;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
choiceSnapshot
|
|
379
|
-
choiceSnapshot.message
|
|
380
|
-
|
|
381
|
-
|
|
1370
|
+
const structuredResponse = isParseableResponseFormat(this.#params?.response_format);
|
|
1371
|
+
const boundedSnapshot = structuredResponse || this.#hasAutoParseableTool;
|
|
1372
|
+
const messageSnapshot = boundedSnapshot
|
|
1373
|
+
? captureStructuredMessageSnapshot(choiceSnapshot)
|
|
1374
|
+
: choiceSnapshot.message;
|
|
1375
|
+
const refusal = boundedSnapshot
|
|
1376
|
+
? captureStructuredJSONSnapshot(messageSnapshot, 'refusal')
|
|
1377
|
+
: messageSnapshot.refusal;
|
|
1378
|
+
const parseableContent = !refusal && structuredResponse;
|
|
1379
|
+
const messageContent = parseableContent
|
|
1380
|
+
? captureStructuredJSONSnapshot(messageSnapshot, 'content')
|
|
1381
|
+
: messageSnapshot.content;
|
|
1382
|
+
|
|
1383
|
+
if (delta?.content != null && messageSnapshot.role === 'assistant' && messageContent) {
|
|
1384
|
+
this._emit('content', delta.content, messageContent);
|
|
382
1385
|
this._emit('content.delta', {
|
|
383
1386
|
delta: delta.content,
|
|
384
|
-
snapshot:
|
|
385
|
-
parsed:
|
|
1387
|
+
snapshot: messageContent,
|
|
1388
|
+
parsed: messageSnapshot.parsed,
|
|
386
1389
|
});
|
|
387
1390
|
}
|
|
388
1391
|
|
|
389
|
-
if (
|
|
390
|
-
delta?.refusal != null &&
|
|
391
|
-
choiceSnapshot.message?.role === 'assistant' &&
|
|
392
|
-
choiceSnapshot.message?.refusal
|
|
393
|
-
) {
|
|
1392
|
+
if (delta?.refusal != null && messageSnapshot.role === 'assistant' && refusal) {
|
|
394
1393
|
this._emit('refusal.delta', {
|
|
395
1394
|
delta: delta.refusal,
|
|
396
|
-
snapshot:
|
|
1395
|
+
snapshot: refusal,
|
|
397
1396
|
});
|
|
398
1397
|
}
|
|
399
1398
|
|
|
400
|
-
if (choice.logprobs?.content != null &&
|
|
1399
|
+
if (choice.logprobs?.content != null && messageSnapshot.role === 'assistant') {
|
|
401
1400
|
this._emit('logprobs.content.delta', {
|
|
402
1401
|
content: choice.logprobs?.content,
|
|
403
1402
|
snapshot: choiceSnapshot.logprobs?.content ?? [],
|
|
404
1403
|
});
|
|
405
1404
|
}
|
|
406
1405
|
|
|
407
|
-
if (choice.logprobs?.refusal != null &&
|
|
1406
|
+
if (choice.logprobs?.refusal != null && messageSnapshot.role === 'assistant') {
|
|
408
1407
|
this._emit('logprobs.refusal.delta', {
|
|
409
1408
|
refusal: choice.logprobs?.refusal,
|
|
410
1409
|
snapshot: choiceSnapshot.logprobs?.refusal ?? [],
|
|
@@ -421,7 +1420,7 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
421
1420
|
}
|
|
422
1421
|
}
|
|
423
1422
|
|
|
424
|
-
for (const toolCall of
|
|
1423
|
+
for (const toolCall of capturedToolCalls) {
|
|
425
1424
|
if (state.current_tool_call_index !== toolCall.index) {
|
|
426
1425
|
this.#emitContentDoneEvents(choiceSnapshot);
|
|
427
1426
|
|
|
@@ -434,19 +1433,30 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
434
1433
|
state.current_tool_call_index = toolCall.index;
|
|
435
1434
|
}
|
|
436
1435
|
|
|
437
|
-
for (const toolCallDelta of
|
|
438
|
-
const toolCallSnapshot =
|
|
1436
|
+
for (const toolCallDelta of capturedToolCalls) {
|
|
1437
|
+
const toolCallSnapshot = messageSnapshot.tool_calls?.[toolCallDelta.index];
|
|
439
1438
|
if (!toolCallSnapshot?.type) {
|
|
440
1439
|
continue;
|
|
441
1440
|
}
|
|
442
1441
|
|
|
443
1442
|
if (toolCallSnapshot.type === 'function') {
|
|
1443
|
+
const boundIdentity = state.tool_call_identities.get(toolCallDelta.index);
|
|
1444
|
+
let argumentsSnapshot: string;
|
|
1445
|
+
if (boundIdentity?.parseable) {
|
|
1446
|
+
const capturedArguments = captureStructuredJSONSnapshot(toolCallSnapshot.function, 'arguments');
|
|
1447
|
+
if (typeof capturedArguments !== 'string') {
|
|
1448
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1449
|
+
}
|
|
1450
|
+
argumentsSnapshot = capturedArguments;
|
|
1451
|
+
} else {
|
|
1452
|
+
argumentsSnapshot = toolCallSnapshot.function.arguments;
|
|
1453
|
+
}
|
|
444
1454
|
this._emit('tool_calls.function.arguments.delta', {
|
|
445
1455
|
name: toolCallSnapshot.function.name,
|
|
446
1456
|
index: toolCallDelta.index,
|
|
447
|
-
arguments:
|
|
1457
|
+
arguments: argumentsSnapshot,
|
|
448
1458
|
parsed_arguments: toolCallSnapshot.function.parsed_arguments,
|
|
449
|
-
arguments_delta: toolCallDelta.
|
|
1459
|
+
arguments_delta: toolCallDelta.arguments_delta,
|
|
450
1460
|
});
|
|
451
1461
|
} else if (toolCallSnapshot.type !== 'custom') {
|
|
452
1462
|
assertNever(toolCallSnapshot);
|
|
@@ -462,10 +1472,18 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
462
1472
|
return;
|
|
463
1473
|
}
|
|
464
1474
|
|
|
465
|
-
const
|
|
1475
|
+
const messageSnapshot = this.#hasAutoParseableTool
|
|
1476
|
+
? captureStructuredMessageSnapshot(choiceSnapshot)
|
|
1477
|
+
: choiceSnapshot.message;
|
|
1478
|
+
const toolCallSnapshot = messageSnapshot.tool_calls?.[toolCallIndex];
|
|
466
1479
|
if (!toolCallSnapshot) {
|
|
467
1480
|
throw new Error('no tool call snapshot');
|
|
468
1481
|
}
|
|
1482
|
+
const boundIdentity = state.tool_call_identities.get(toolCallIndex);
|
|
1483
|
+
if (boundIdentity) {
|
|
1484
|
+
assertBoundToolCallIdentity(toolCallSnapshot, boundIdentity);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
469
1487
|
if (!toolCallSnapshot.type) {
|
|
470
1488
|
throw new Error('tool call snapshot missing `type`');
|
|
471
1489
|
}
|
|
@@ -476,16 +1494,33 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
476
1494
|
) as ChatCompletionFunctionTool | undefined; // TS doesn't narrow based on isChatCompletionTool
|
|
477
1495
|
|
|
478
1496
|
let parsedArguments: unknown = null;
|
|
1497
|
+
const parseable = isAutoParsableTool(inputTool) || inputTool?.function.strict === true;
|
|
1498
|
+
let argumentsSnapshot: string;
|
|
1499
|
+
if (parseable) {
|
|
1500
|
+
if (this.#currentChatCompletionSnapshot) {
|
|
1501
|
+
this.#validateStructuredSnapshots(this.#currentChatCompletionSnapshot);
|
|
1502
|
+
}
|
|
1503
|
+
const capturedArguments = captureStructuredJSONSnapshot(toolCallSnapshot.function, 'arguments');
|
|
1504
|
+
if (typeof capturedArguments !== 'string') {
|
|
1505
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1506
|
+
}
|
|
1507
|
+
argumentsSnapshot = capturedArguments;
|
|
1508
|
+
} else {
|
|
1509
|
+
argumentsSnapshot = toolCallSnapshot.function.arguments;
|
|
1510
|
+
}
|
|
479
1511
|
if (isAutoParsableTool(inputTool)) {
|
|
480
|
-
parsedArguments = inputTool.$parseRaw(
|
|
1512
|
+
parsedArguments = inputTool.$parseRaw(validateStructuredJSONSnapshot(argumentsSnapshot));
|
|
481
1513
|
} else if (inputTool?.function.strict) {
|
|
482
|
-
parsedArguments =
|
|
1514
|
+
parsedArguments = parseResponseFormatContent(
|
|
1515
|
+
{ type: 'json_schema', $parseRaw: undefined },
|
|
1516
|
+
validateStructuredJSONSnapshot(argumentsSnapshot),
|
|
1517
|
+
);
|
|
483
1518
|
}
|
|
484
1519
|
|
|
485
1520
|
this._emit('tool_calls.function.arguments.done', {
|
|
486
1521
|
name: toolCallSnapshot.function.name,
|
|
487
1522
|
index: toolCallIndex,
|
|
488
|
-
arguments:
|
|
1523
|
+
arguments: argumentsSnapshot,
|
|
489
1524
|
parsed_arguments: parsedArguments,
|
|
490
1525
|
});
|
|
491
1526
|
} else if (toolCallSnapshot.type !== 'custom') {
|
|
@@ -495,32 +1530,45 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
495
1530
|
|
|
496
1531
|
#emitContentDoneEvents(choiceSnapshot: ChatCompletionSnapshot.Choice) {
|
|
497
1532
|
const state = this.#getChoiceEventState(choiceSnapshot);
|
|
1533
|
+
const structuredResponse = isParseableResponseFormat(this.#params?.response_format);
|
|
1534
|
+
const boundedSnapshot = structuredResponse || this.#hasAutoParseableTool;
|
|
1535
|
+
const messageSnapshot = boundedSnapshot
|
|
1536
|
+
? captureStructuredMessageSnapshot(choiceSnapshot)
|
|
1537
|
+
: choiceSnapshot.message;
|
|
1538
|
+
const refusal = boundedSnapshot
|
|
1539
|
+
? captureStructuredJSONSnapshot(messageSnapshot, 'refusal')
|
|
1540
|
+
: messageSnapshot.refusal;
|
|
1541
|
+
const parseableContent = !refusal && structuredResponse;
|
|
1542
|
+
const content = parseableContent
|
|
1543
|
+
? captureStructuredJSONSnapshot(messageSnapshot, 'content')
|
|
1544
|
+
: messageSnapshot.content;
|
|
498
1545
|
|
|
499
1546
|
if (
|
|
500
|
-
|
|
501
|
-
(
|
|
502
|
-
(!
|
|
503
|
-
!choiceSnapshot.message.tool_calls?.length &&
|
|
504
|
-
!choiceSnapshot.message.function_call)) &&
|
|
1547
|
+
content != null &&
|
|
1548
|
+
(content !== '' ||
|
|
1549
|
+
(!refusal && !messageSnapshot.tool_calls?.length && !messageSnapshot.function_call)) &&
|
|
505
1550
|
!state.content_done
|
|
506
1551
|
) {
|
|
1552
|
+
if (parseableContent && this.#currentChatCompletionSnapshot) {
|
|
1553
|
+
this.#validateStructuredSnapshots(this.#currentChatCompletionSnapshot);
|
|
1554
|
+
}
|
|
507
1555
|
state.content_done = true;
|
|
508
1556
|
|
|
509
1557
|
this._emit('content.done', {
|
|
510
|
-
content
|
|
511
|
-
parsed:
|
|
1558
|
+
content,
|
|
1559
|
+
parsed: refusal
|
|
512
1560
|
? null
|
|
513
1561
|
: parseResponseFormatContent<ParsedT>(
|
|
514
1562
|
this.#params?.response_format,
|
|
515
|
-
|
|
1563
|
+
parseableContent ? validateStructuredJSONSnapshot(content) : content,
|
|
516
1564
|
),
|
|
517
1565
|
});
|
|
518
1566
|
}
|
|
519
1567
|
|
|
520
|
-
if (
|
|
1568
|
+
if (refusal && !state.refusal_done) {
|
|
521
1569
|
state.refusal_done = true;
|
|
522
1570
|
|
|
523
|
-
this._emit('refusal.done', { refusal
|
|
1571
|
+
this._emit('refusal.done', { refusal });
|
|
524
1572
|
}
|
|
525
1573
|
|
|
526
1574
|
if (choiceSnapshot.logprobs?.content && !state.logprobs_content_done) {
|
|
@@ -536,6 +1584,119 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
536
1584
|
}
|
|
537
1585
|
}
|
|
538
1586
|
|
|
1587
|
+
#validateStructuredSnapshots(
|
|
1588
|
+
snapshot: ChatCompletionSnapshot,
|
|
1589
|
+
): WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot> {
|
|
1590
|
+
const finalJSONBudget: PartialJSONParseBudget = { bytes: 0, fragments: 0, work: 0 };
|
|
1591
|
+
const parseableContent = isParseableResponseFormat(this.#params?.response_format);
|
|
1592
|
+
const validatedMessages = new WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot>();
|
|
1593
|
+
const choices = captureSnapshotArray<ChatCompletionSnapshot.Choice>(
|
|
1594
|
+
snapshot,
|
|
1595
|
+
'choices',
|
|
1596
|
+
MAX_STREAM_CHOICES,
|
|
1597
|
+
'choice',
|
|
1598
|
+
);
|
|
1599
|
+
if (!choices) {
|
|
1600
|
+
throw new OpenAIError('Chat completion stream contains an unsafe snapshot choice collection');
|
|
1601
|
+
}
|
|
1602
|
+
for (let choiceIndex = 0; choiceIndex < choices.length; choiceIndex += 1) {
|
|
1603
|
+
const choice = captureSnapshotArrayItem(choices, choiceIndex);
|
|
1604
|
+
if (!choice) {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
const message = captureStructuredMessageSnapshot(choice);
|
|
1608
|
+
const refusal = captureStructuredJSONSnapshot(message, 'refusal');
|
|
1609
|
+
const content = captureStructuredJSONSnapshot(message, 'content');
|
|
1610
|
+
const validatedTools = new Map<number, ValidatedToolCallSnapshot>();
|
|
1611
|
+
const toolCalls = captureSnapshotArray<ChatCompletionSnapshot.Choice.Message.ToolCall>(
|
|
1612
|
+
message,
|
|
1613
|
+
'tool_calls',
|
|
1614
|
+
MAX_STREAM_TOOL_CALLS,
|
|
1615
|
+
'tool-call',
|
|
1616
|
+
);
|
|
1617
|
+
validatedMessages.set(
|
|
1618
|
+
choice,
|
|
1619
|
+
Object.freeze({
|
|
1620
|
+
message,
|
|
1621
|
+
content,
|
|
1622
|
+
refusal,
|
|
1623
|
+
toolCallCollection: toolCalls,
|
|
1624
|
+
toolCalls: validatedTools,
|
|
1625
|
+
}),
|
|
1626
|
+
);
|
|
1627
|
+
const state = this.#choiceEventStates[choice.index];
|
|
1628
|
+
if (parseableContent && !refusal && typeof content === 'string') {
|
|
1629
|
+
validateStructuredJSONSnapshot(content, finalJSONBudget, this.#partialJSONParseBudget);
|
|
1630
|
+
}
|
|
1631
|
+
for (const [index, identity] of state?.tool_call_identities ?? []) {
|
|
1632
|
+
const toolCall = toolCalls && captureSnapshotArrayItem(toolCalls, index);
|
|
1633
|
+
if (!toolCall) {
|
|
1634
|
+
throw new OpenAIError('Chat completion stream contains a changed tool call identity');
|
|
1635
|
+
}
|
|
1636
|
+
assertBoundToolCallIdentity(toolCall, identity);
|
|
1637
|
+
}
|
|
1638
|
+
if (!this.#hasAutoParseableTool) {
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
for (let toolCallIndex = 0; toolCallIndex < (toolCalls?.length ?? 0); toolCallIndex += 1) {
|
|
1642
|
+
const toolCall = captureSnapshotArrayItem(toolCalls!, toolCallIndex);
|
|
1643
|
+
if (!toolCall) {
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
const identity = ownFunctionToolIdentity(toolCall);
|
|
1647
|
+
if (!identity) {
|
|
1648
|
+
const type = Object.getOwnPropertyDescriptor(toolCall, 'type');
|
|
1649
|
+
if (type && !('value' in type)) {
|
|
1650
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1651
|
+
}
|
|
1652
|
+
if (type?.value !== 'function') {
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
const fn = Object.getOwnPropertyDescriptor(toolCall, 'function');
|
|
1656
|
+
if (fn && !('value' in fn)) {
|
|
1657
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1658
|
+
}
|
|
1659
|
+
if (fn && typeof fn.value === 'object' && fn.value !== null) {
|
|
1660
|
+
const name = Object.getOwnPropertyDescriptor(fn.value, 'name');
|
|
1661
|
+
if (name && !('value' in name)) {
|
|
1662
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
if (
|
|
1668
|
+
!shouldParseToolCall(this.#params, {
|
|
1669
|
+
type: identity.type,
|
|
1670
|
+
function: { name: identity.name },
|
|
1671
|
+
})
|
|
1672
|
+
) {
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
const descriptor = Object.getOwnPropertyDescriptor(toolCall, 'function');
|
|
1676
|
+
if (!descriptor || !('value' in descriptor)) {
|
|
1677
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1678
|
+
}
|
|
1679
|
+
const fn = descriptor.value as ChatCompletionSnapshot.Choice.Message.ToolCall.Function;
|
|
1680
|
+
const argumentsSnapshot = captureStructuredJSONSnapshot(fn, 'arguments');
|
|
1681
|
+
if (typeof argumentsSnapshot !== 'string') {
|
|
1682
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
1683
|
+
}
|
|
1684
|
+
validateStructuredJSONSnapshot(argumentsSnapshot, finalJSONBudget, this.#partialJSONParseBudget);
|
|
1685
|
+
validatedTools.set(
|
|
1686
|
+
toolCallIndex,
|
|
1687
|
+
Object.freeze({
|
|
1688
|
+
tool: toolCall,
|
|
1689
|
+
function: fn,
|
|
1690
|
+
type: identity.type,
|
|
1691
|
+
name: identity.name,
|
|
1692
|
+
arguments: argumentsSnapshot,
|
|
1693
|
+
}),
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return validatedMessages;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
539
1700
|
#endRequest(): ParsedChatCompletion<ParsedT> {
|
|
540
1701
|
if (this.ended) {
|
|
541
1702
|
throw new OpenAIError(`stream has ended, this shouldn't happen`);
|
|
@@ -544,11 +1705,12 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
544
1705
|
if (!snapshot) {
|
|
545
1706
|
throw new OpenAIError(`request ended without sending any chunks`);
|
|
546
1707
|
}
|
|
1708
|
+
const validatedMessages = this.#validateStructuredSnapshots(snapshot);
|
|
547
1709
|
const audioDoneChoiceIndexes = this.#audioDoneChoiceIndexes;
|
|
548
1710
|
this.#audioDoneChoiceIndexes = new Set();
|
|
549
1711
|
this.#currentChatCompletionSnapshot = undefined;
|
|
550
1712
|
this.#choiceEventStates = [];
|
|
551
|
-
return finalizeChatCompletion(snapshot, this.#params, audioDoneChoiceIndexes);
|
|
1713
|
+
return finalizeChatCompletion(snapshot, this.#params, audioDoneChoiceIndexes, validatedMessages);
|
|
552
1714
|
}
|
|
553
1715
|
|
|
554
1716
|
protected override async _createChatCompletion(
|
|
@@ -557,12 +1719,35 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
557
1719
|
options?: RequestOptions,
|
|
558
1720
|
): Promise<ParsedChatCompletion<ParsedT>> {
|
|
559
1721
|
this._listenForAbort(options?.signal);
|
|
1722
|
+
const requestParams = { ...params, stream: true as const };
|
|
1723
|
+
this.#params = requestParams;
|
|
560
1724
|
this.#beginRequest();
|
|
561
1725
|
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
1726
|
+
const parserParams = snapshotChatCompletionParserParams(requestParams);
|
|
1727
|
+
this.#params = parserParams;
|
|
1728
|
+
this.#hasAutoParseableTool =
|
|
1729
|
+
parserParams.tools?.some(
|
|
1730
|
+
(tool) =>
|
|
1731
|
+
isChatCompletionFunctionTool(tool) && (isAutoParsableTool(tool) || tool.function.strict === true),
|
|
1732
|
+
) ?? false;
|
|
1733
|
+
const stopObserving =
|
|
1734
|
+
requestParams.tools || requestParams.response_format
|
|
1735
|
+
? observeSerializedChatCompletionParserParams(requestParams, parserParams, (serialized) => {
|
|
1736
|
+
this.#params = serialized;
|
|
1737
|
+
this.#hasAutoParseableTool =
|
|
1738
|
+
serialized.tools?.some(
|
|
1739
|
+
(tool) =>
|
|
1740
|
+
isChatCompletionFunctionTool(tool) &&
|
|
1741
|
+
(isAutoParsableTool(tool) || tool.function.strict === true),
|
|
1742
|
+
) ?? false;
|
|
1743
|
+
})
|
|
1744
|
+
: undefined;
|
|
1745
|
+
const stream = await client.chat.completions
|
|
1746
|
+
.create(requestParams, {
|
|
1747
|
+
...options,
|
|
1748
|
+
signal: this.controller.signal,
|
|
1749
|
+
})
|
|
1750
|
+
.finally(stopObserving);
|
|
566
1751
|
this._connected();
|
|
567
1752
|
for await (const chunk of stream) {
|
|
568
1753
|
this.#addChunk(chunk);
|
|
@@ -629,9 +1814,12 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
629
1814
|
throw new OpenAIError(`request ended without sending any chunks`);
|
|
630
1815
|
}
|
|
631
1816
|
|
|
632
|
-
#accumulateChatCompletion(
|
|
1817
|
+
#accumulateChatCompletion(
|
|
1818
|
+
chunk: ChatCompletionChunk,
|
|
1819
|
+
capturedChoiceFrames: WeakMap<object, CapturedChoiceToolCallFrames>,
|
|
1820
|
+
): ChatCompletionSnapshot {
|
|
633
1821
|
let snapshot = this.#currentChatCompletionSnapshot;
|
|
634
|
-
const { choices, ...rest } = chunk;
|
|
1822
|
+
const { choices, obfuscation: _obfuscation, ...rest } = chunk;
|
|
635
1823
|
if (!snapshot) {
|
|
636
1824
|
const newSnapshot: ChatCompletionSnapshot = {
|
|
637
1825
|
...rest,
|
|
@@ -651,17 +1839,23 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
651
1839
|
? Math.min(requestedChoiceCount, MAX_STREAM_CHOICES)
|
|
652
1840
|
: MAX_STREAM_CHOICES;
|
|
653
1841
|
|
|
654
|
-
for (const
|
|
1842
|
+
for (const chunkChoice of chunk.choices) {
|
|
1843
|
+
const { delta, finish_reason, index, logprobs = null, ...other } = chunkChoice;
|
|
1844
|
+
const capturedToolCalls: CapturedToolCallDeltaFrame[] = [];
|
|
1845
|
+
capturedChoiceFrames.set(chunkChoice, Object.freeze({ index, tool_calls: capturedToolCalls }));
|
|
655
1846
|
if (!Number.isSafeInteger(index) || index < 0 || index >= maxChoices) {
|
|
656
1847
|
throw new OpenAIError(`Chat completion stream contains an invalid choice index: ${index}`);
|
|
657
1848
|
}
|
|
658
1849
|
|
|
659
1850
|
let choice = snapshot.choices[index];
|
|
660
1851
|
if (!choice) {
|
|
661
|
-
const newChoice = { finish_reason, index, message: {}, logprobs, ...other };
|
|
1852
|
+
const newChoice = { finish_reason, index, message: {}, logprobs: null, ...other };
|
|
662
1853
|
snapshot.choices[index] = newChoice;
|
|
663
1854
|
choice = newChoice;
|
|
664
1855
|
}
|
|
1856
|
+
if (isParseableResponseFormat(this.#params?.response_format) || this.#hasAutoParseableTool) {
|
|
1857
|
+
captureStructuredJSONSnapshot(captureStructuredMessageSnapshot(choice), 'refusal');
|
|
1858
|
+
}
|
|
665
1859
|
|
|
666
1860
|
if (logprobs) {
|
|
667
1861
|
if (choice.logprobs) {
|
|
@@ -680,6 +1874,14 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
680
1874
|
}
|
|
681
1875
|
} else {
|
|
682
1876
|
choice.logprobs = { ...logprobs };
|
|
1877
|
+
|
|
1878
|
+
if (logprobs.content) {
|
|
1879
|
+
choice.logprobs.content = [...logprobs.content];
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (logprobs.refusal) {
|
|
1883
|
+
choice.logprobs.refusal = [...logprobs.refusal];
|
|
1884
|
+
}
|
|
683
1885
|
}
|
|
684
1886
|
}
|
|
685
1887
|
|
|
@@ -700,13 +1902,17 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
700
1902
|
assignOwnProperties(choice, other);
|
|
701
1903
|
|
|
702
1904
|
if (!delta) {
|
|
1905
|
+
Object.freeze(capturedToolCalls);
|
|
703
1906
|
continue;
|
|
704
1907
|
} // Shouldn't happen; just in case.
|
|
705
1908
|
|
|
706
1909
|
this.#audioDoneChoiceIndexes.delete(index);
|
|
707
|
-
const { audio, content, refusal, function_call, role,
|
|
708
|
-
|
|
709
|
-
|
|
1910
|
+
const { audio, content, refusal, function_call, role, ...capturedDeltaFields } =
|
|
1911
|
+
delta as typeof delta & {
|
|
1912
|
+
audio?: Partial<ChatCompletionAudio> | null;
|
|
1913
|
+
};
|
|
1914
|
+
const { tool_calls: capturedToolCallDelta, ...rest } = capturedDeltaFields;
|
|
1915
|
+
const tool_calls = hasOwn(capturedDeltaFields, 'tool_calls') ? capturedToolCallDelta : delta.tool_calls;
|
|
710
1916
|
assertIsEmpty(rest);
|
|
711
1917
|
assignOwnProperties(choice.message, rest);
|
|
712
1918
|
if (
|
|
@@ -760,11 +1966,26 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
760
1966
|
}
|
|
761
1967
|
}
|
|
762
1968
|
if (content != null) {
|
|
763
|
-
choice.message.content = (choice.message.content || '') + content;
|
|
764
|
-
|
|
765
1969
|
if (!choice.message.refusal && isParseableResponseFormat(this.#params?.response_format)) {
|
|
766
|
-
|
|
767
|
-
|
|
1970
|
+
const eventState = this.#getChoiceEventState(choice);
|
|
1971
|
+
const parseState = (eventState.content_parse_state ??= createPartialJSONParseState());
|
|
1972
|
+
const shouldParse = recordPartialJSONFragment(parseState, this.#partialJSONParseBudget, content);
|
|
1973
|
+
choice.message.content = (captureStructuredJSONSnapshot(choice.message, 'content') || '') + content;
|
|
1974
|
+
|
|
1975
|
+
// The partial parser does not accept whitespace-only input. Once output
|
|
1976
|
+
// grows, coalesce prefix reparses while preserving every raw snapshot.
|
|
1977
|
+
if (!parseState.has_non_whitespace) {
|
|
1978
|
+
choice.message.parsed = null;
|
|
1979
|
+
} else if (shouldParse && reservePartialJSONParse(parseState, this.#partialJSONParseBudget)) {
|
|
1980
|
+
this.#validateStructuredSnapshots(snapshot);
|
|
1981
|
+
choice.message.parsed = parseStructuredStreamingJSON(
|
|
1982
|
+
validateStructuredJSONSnapshot(choice.message.content),
|
|
1983
|
+
);
|
|
1984
|
+
} else if (content.length > 0) {
|
|
1985
|
+
choice.message.parsed = null;
|
|
1986
|
+
}
|
|
1987
|
+
} else {
|
|
1988
|
+
choice.message.content = (choice.message.content || '') + content;
|
|
768
1989
|
}
|
|
769
1990
|
}
|
|
770
1991
|
|
|
@@ -774,12 +1995,27 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
774
1995
|
// once every delta for them has been accumulated.
|
|
775
1996
|
const toolCallSnapshots = (choice.message.tool_calls ??= []) as PartialToolCallSnapshot[];
|
|
776
1997
|
|
|
777
|
-
for (const
|
|
1998
|
+
for (const toolCallDelta of tool_calls) {
|
|
1999
|
+
const { index, id, type, function: fn, custom, ...rest } = toolCallDelta;
|
|
778
2000
|
if (!Number.isSafeInteger(index) || index < 0 || index >= MAX_STREAM_TOOL_CALLS) {
|
|
779
2001
|
throw new OpenAIError(`Chat completion stream contains an invalid tool call index: ${index}`);
|
|
780
2002
|
}
|
|
2003
|
+
let argumentsDelta = '';
|
|
781
2004
|
|
|
782
2005
|
const tool_call = (toolCallSnapshots[index] ??= {});
|
|
2006
|
+
const functionName = fn?.name;
|
|
2007
|
+
const eventState = this.#hasAutoParseableTool ? this.#getChoiceEventState(choice) : undefined;
|
|
2008
|
+
let boundIdentity = eventState?.tool_call_identities.get(index);
|
|
2009
|
+
if (boundIdentity) {
|
|
2010
|
+
assertBoundToolCallIdentity(tool_call, boundIdentity);
|
|
2011
|
+
if (
|
|
2012
|
+
(type !== undefined && type !== boundIdentity.type) ||
|
|
2013
|
+
(functionName !== undefined && functionName !== boundIdentity.name)
|
|
2014
|
+
) {
|
|
2015
|
+
throw new OpenAIError('Chat completion stream contains a changed tool call identity');
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
783
2019
|
assignOwnProperties(tool_call, rest);
|
|
784
2020
|
if (id) {
|
|
785
2021
|
tool_call.id = id;
|
|
@@ -797,20 +2033,80 @@ export class ChatCompletionStream<ParsedT = null>
|
|
|
797
2033
|
}
|
|
798
2034
|
}
|
|
799
2035
|
if (fn) {
|
|
800
|
-
const functionSnapshot = (tool_call.function ??= { name:
|
|
801
|
-
if (
|
|
802
|
-
functionSnapshot.name =
|
|
2036
|
+
const functionSnapshot = (tool_call.function ??= { name: functionName ?? '', arguments: '' });
|
|
2037
|
+
if (functionName) {
|
|
2038
|
+
functionSnapshot.name = functionName;
|
|
803
2039
|
}
|
|
804
|
-
if (
|
|
805
|
-
|
|
2040
|
+
if (eventState && !boundIdentity) {
|
|
2041
|
+
const identity = ownFunctionToolIdentity(tool_call);
|
|
2042
|
+
const configuredTool =
|
|
2043
|
+
identity &&
|
|
2044
|
+
this.#params?.tools?.find(
|
|
2045
|
+
(tool) => isChatCompletionFunctionTool(tool) && tool.function.name === identity.name,
|
|
2046
|
+
);
|
|
2047
|
+
if (identity) {
|
|
2048
|
+
boundIdentity = {
|
|
2049
|
+
...identity,
|
|
2050
|
+
parseable:
|
|
2051
|
+
configuredTool !== undefined &&
|
|
2052
|
+
shouldParseToolCall(this.#params, {
|
|
2053
|
+
type: identity.type,
|
|
2054
|
+
function: { name: identity.name },
|
|
2055
|
+
}),
|
|
2056
|
+
};
|
|
2057
|
+
eventState.tool_call_identities.set(index, boundIdentity);
|
|
2058
|
+
if (!boundIdentity.parseable) {
|
|
2059
|
+
const provisionalState = eventState.tool_call_parse_states.get(index);
|
|
2060
|
+
if (provisionalState) {
|
|
2061
|
+
this.#partialJSONParseBudget.bytes -= provisionalState.bytes;
|
|
2062
|
+
this.#partialJSONParseBudget.fragments -= provisionalState.fragments;
|
|
2063
|
+
this.#partialJSONParseBudget.work -= provisionalState.work;
|
|
2064
|
+
eventState.tool_call_parse_states.delete(index);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
const argumentFragment = fn.arguments;
|
|
2070
|
+
if (argumentFragment != null) {
|
|
2071
|
+
argumentsDelta = argumentFragment;
|
|
2072
|
+
if (eventState && boundIdentity?.parseable !== false) {
|
|
2073
|
+
let parseState = eventState.tool_call_parse_states.get(index);
|
|
2074
|
+
if (!parseState) {
|
|
2075
|
+
parseState = createPartialJSONParseState();
|
|
2076
|
+
eventState.tool_call_parse_states.set(index, parseState);
|
|
2077
|
+
}
|
|
806
2078
|
|
|
807
|
-
|
|
808
|
-
|
|
2079
|
+
const shouldParse = recordPartialJSONFragment(
|
|
2080
|
+
parseState,
|
|
2081
|
+
this.#partialJSONParseBudget,
|
|
2082
|
+
argumentFragment,
|
|
2083
|
+
);
|
|
2084
|
+
const previousArguments = captureStructuredJSONSnapshot(functionSnapshot, 'arguments');
|
|
2085
|
+
if (typeof previousArguments !== 'string') {
|
|
2086
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
2087
|
+
}
|
|
2088
|
+
functionSnapshot.arguments = previousArguments + argumentFragment;
|
|
2089
|
+
if (
|
|
2090
|
+
shouldParse &&
|
|
2091
|
+
boundIdentity?.parseable === true &&
|
|
2092
|
+
reservePartialJSONParse(parseState, this.#partialJSONParseBudget)
|
|
2093
|
+
) {
|
|
2094
|
+
this.#validateStructuredSnapshots(snapshot);
|
|
2095
|
+
functionSnapshot.parsed_arguments = parseStructuredStreamingJSON(
|
|
2096
|
+
validateStructuredJSONSnapshot(functionSnapshot.arguments),
|
|
2097
|
+
);
|
|
2098
|
+
} else if (argumentFragment.length > 0 && hasOwn(functionSnapshot, 'parsed_arguments')) {
|
|
2099
|
+
functionSnapshot.parsed_arguments = undefined;
|
|
2100
|
+
}
|
|
2101
|
+
} else {
|
|
2102
|
+
functionSnapshot.arguments += argumentFragment;
|
|
809
2103
|
}
|
|
810
2104
|
}
|
|
811
2105
|
}
|
|
2106
|
+
capturedToolCalls.push(Object.freeze({ index, arguments_delta: argumentsDelta }));
|
|
812
2107
|
}
|
|
813
2108
|
}
|
|
2109
|
+
Object.freeze(capturedToolCalls);
|
|
814
2110
|
}
|
|
815
2111
|
return snapshot;
|
|
816
2112
|
}
|
|
@@ -838,13 +2134,41 @@ function finalizeChatCompletion<ParsedT>(
|
|
|
838
2134
|
snapshot: ChatCompletionSnapshot,
|
|
839
2135
|
params: ChatCompletionCreateParams | null,
|
|
840
2136
|
audioDoneChoiceIndexes: ReadonlySet<number>,
|
|
2137
|
+
validatedMessages: WeakMap<ChatCompletionSnapshot.Choice, ValidatedChoiceSnapshot>,
|
|
841
2138
|
): ParsedChatCompletion<ParsedT> {
|
|
842
2139
|
const { id, choices, created, model, system_fingerprint, ...rest } = snapshot;
|
|
843
2140
|
const completion: ChatCompletion = {
|
|
844
2141
|
...rest,
|
|
845
2142
|
id,
|
|
846
|
-
choices:
|
|
847
|
-
|
|
2143
|
+
choices: mapCapturedSnapshotArray(
|
|
2144
|
+
choices,
|
|
2145
|
+
MAX_STREAM_CHOICES,
|
|
2146
|
+
'choice',
|
|
2147
|
+
(choice): ChatCompletion.Choice => {
|
|
2148
|
+
const validated = validatedMessages.get(choice);
|
|
2149
|
+
if (!validated) {
|
|
2150
|
+
throw new OpenAIError('Chat completion stream contains an unsafe structured JSON snapshot');
|
|
2151
|
+
}
|
|
2152
|
+
const stableChoice = new Proxy(choice, {
|
|
2153
|
+
get(target, property, receiver) {
|
|
2154
|
+
return property === 'message' ? validated.message : Reflect.get(target, property, receiver);
|
|
2155
|
+
},
|
|
2156
|
+
});
|
|
2157
|
+
const { message: sourceMessage, finish_reason, index, logprobs, ...choiceRest } = stableChoice;
|
|
2158
|
+
const message = new Proxy(sourceMessage, {
|
|
2159
|
+
get(target, property, receiver) {
|
|
2160
|
+
if (property === 'content') {
|
|
2161
|
+
return validated.content;
|
|
2162
|
+
}
|
|
2163
|
+
if (property === 'refusal') {
|
|
2164
|
+
return validated.refusal;
|
|
2165
|
+
}
|
|
2166
|
+
if (property === 'tool_calls') {
|
|
2167
|
+
return validated.toolCallCollection;
|
|
2168
|
+
}
|
|
2169
|
+
return Reflect.get(target, property, receiver);
|
|
2170
|
+
},
|
|
2171
|
+
});
|
|
848
2172
|
const { content = null, function_call, tool_calls, audio, ...messageRest } = message;
|
|
849
2173
|
// Audio streams can end with an expires_at-only chunk after the
|
|
850
2174
|
// generated audio, without a separate finish_reason.
|
|
@@ -897,47 +2221,91 @@ function finalizeChatCompletion<ParsedT>(
|
|
|
897
2221
|
role,
|
|
898
2222
|
content,
|
|
899
2223
|
refusal: message.refusal ?? null,
|
|
900
|
-
tool_calls:
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
2224
|
+
tool_calls: mapCapturedSnapshotArray(
|
|
2225
|
+
tool_calls,
|
|
2226
|
+
MAX_STREAM_TOOL_CALLS,
|
|
2227
|
+
'tool-call',
|
|
2228
|
+
(tool_call, i): ChatCompletionMessageToolCall => {
|
|
2229
|
+
const captured = validated.toolCalls.get(i);
|
|
2230
|
+
if (!captured) {
|
|
2231
|
+
const identity = ownFunctionToolIdentity(tool_call);
|
|
2232
|
+
if (
|
|
2233
|
+
identity &&
|
|
2234
|
+
shouldParseToolCall(params, {
|
|
2235
|
+
type: identity.type,
|
|
2236
|
+
function: { name: identity.name },
|
|
2237
|
+
})
|
|
2238
|
+
) {
|
|
2239
|
+
throw new OpenAIError(
|
|
2240
|
+
'Chat completion stream contains an unsafe structured JSON snapshot',
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
if (captured && captured.tool !== tool_call) {
|
|
2245
|
+
throw new OpenAIError('Chat completion stream contains a changed tool call identity');
|
|
2246
|
+
}
|
|
2247
|
+
const stableFunction =
|
|
2248
|
+
captured &&
|
|
2249
|
+
new Proxy(captured.function, {
|
|
2250
|
+
get(target, property, receiver) {
|
|
2251
|
+
if (property === 'arguments') {
|
|
2252
|
+
return captured.arguments;
|
|
2253
|
+
}
|
|
2254
|
+
if (property === 'name') {
|
|
2255
|
+
return captured.name;
|
|
2256
|
+
}
|
|
2257
|
+
return Reflect.get(target, property, receiver);
|
|
2258
|
+
},
|
|
2259
|
+
});
|
|
2260
|
+
const stableTool =
|
|
2261
|
+
captured && stableFunction
|
|
2262
|
+
? new Proxy(tool_call, {
|
|
2263
|
+
get(target, property, receiver) {
|
|
2264
|
+
if (property === 'type') {
|
|
2265
|
+
return captured.type;
|
|
2266
|
+
}
|
|
2267
|
+
if (property === 'function') {
|
|
2268
|
+
return stableFunction;
|
|
2269
|
+
}
|
|
2270
|
+
return Reflect.get(target, property, receiver);
|
|
2271
|
+
},
|
|
2272
|
+
})
|
|
2273
|
+
: tool_call;
|
|
2274
|
+
if (stableTool.type == null) {
|
|
2275
|
+
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].type`);
|
|
2276
|
+
}
|
|
904
2277
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
2278
|
+
if (stableTool.type === 'custom') {
|
|
2279
|
+
const { custom, type, id, ...toolRest } = stableTool;
|
|
2280
|
+
const { input = '', name, ...customRest } = custom || {};
|
|
2281
|
+
if (name == null) {
|
|
2282
|
+
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].custom.name`);
|
|
2283
|
+
}
|
|
2284
|
+
return {
|
|
2285
|
+
...toolRest,
|
|
2286
|
+
id: id || `call_${uuid4()}`,
|
|
2287
|
+
type,
|
|
2288
|
+
custom: { ...customRest, name, input },
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
const { function: fn, type, id, ...toolRest } = stableTool;
|
|
2293
|
+
const { arguments: args, name, ...fnRest } = fn || {};
|
|
908
2294
|
if (name == null) {
|
|
909
|
-
throw new OpenAIError(
|
|
910
|
-
|
|
911
|
-
|
|
2295
|
+
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].function.name`);
|
|
2296
|
+
}
|
|
2297
|
+
if (args == null) {
|
|
2298
|
+
throw new OpenAIError(`missing choices[${index}].tool_calls[${i}].function.arguments`);
|
|
912
2299
|
}
|
|
2300
|
+
|
|
913
2301
|
return {
|
|
914
2302
|
...toolRest,
|
|
915
2303
|
id: id || `call_${uuid4()}`,
|
|
916
2304
|
type,
|
|
917
|
-
|
|
2305
|
+
function: { ...fnRest, name, arguments: args },
|
|
918
2306
|
};
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
const { function: fn, type, id, ...toolRest } = tool_call;
|
|
922
|
-
const { arguments: args, name, ...fnRest } = fn || {};
|
|
923
|
-
if (name == null) {
|
|
924
|
-
throw new OpenAIError(
|
|
925
|
-
`missing choices[${index}].tool_calls[${i}].function.name\n${str(snapshot)}`,
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
if (args == null) {
|
|
929
|
-
throw new OpenAIError(
|
|
930
|
-
`missing choices[${index}].tool_calls[${i}].function.arguments\n${str(snapshot)}`,
|
|
931
|
-
);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
return {
|
|
935
|
-
...toolRest,
|
|
936
|
-
id: id || `call_${uuid4()}`,
|
|
937
|
-
type,
|
|
938
|
-
function: { ...fnRest, name, arguments: args },
|
|
939
|
-
};
|
|
940
|
-
}),
|
|
2307
|
+
},
|
|
2308
|
+
),
|
|
941
2309
|
},
|
|
942
2310
|
};
|
|
943
2311
|
}
|
|
@@ -965,10 +2333,6 @@ function isCompleteAudio(
|
|
|
965
2333
|
return audio?.id != null && audio.data != null && audio.transcript != null && audio.expires_at != null;
|
|
966
2334
|
}
|
|
967
2335
|
|
|
968
|
-
function str(x: unknown) {
|
|
969
|
-
return JSON.stringify(x);
|
|
970
|
-
}
|
|
971
|
-
|
|
972
2336
|
/**
|
|
973
2337
|
* The chat completion accumulated from every streamed chunk received so far.
|
|
974
2338
|
* Fields within each choice can remain incomplete until generation finishes.
|