ai 6.0.32 → 6.0.34
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 +16 -0
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -2
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/docs/02-foundations/03-prompts.mdx +2 -2
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/28-output.mdx +1 -1
- package/package.json +6 -4
- package/src/agent/agent.ts +116 -0
- package/src/agent/create-agent-ui-stream-response.test.ts +258 -0
- package/src/agent/create-agent-ui-stream-response.ts +50 -0
- package/src/agent/create-agent-ui-stream.ts +73 -0
- package/src/agent/index.ts +33 -0
- package/src/agent/infer-agent-tools.ts +7 -0
- package/src/agent/infer-agent-ui-message.test-d.ts +54 -0
- package/src/agent/infer-agent-ui-message.ts +11 -0
- package/src/agent/pipe-agent-ui-stream-to-response.ts +52 -0
- package/src/agent/tool-loop-agent-on-finish-callback.ts +31 -0
- package/src/agent/tool-loop-agent-on-step-finish-callback.ts +11 -0
- package/src/agent/tool-loop-agent-settings.ts +182 -0
- package/src/agent/tool-loop-agent.test-d.ts +114 -0
- package/src/agent/tool-loop-agent.test.ts +442 -0
- package/src/agent/tool-loop-agent.ts +114 -0
- package/src/embed/__snapshots__/embed-many.test.ts.snap +191 -0
- package/src/embed/__snapshots__/embed.test.ts.snap +81 -0
- package/src/embed/embed-many-result.ts +53 -0
- package/src/embed/embed-many.test.ts +653 -0
- package/src/embed/embed-many.ts +378 -0
- package/src/embed/embed-result.ts +50 -0
- package/src/embed/embed.test.ts +298 -0
- package/src/embed/embed.ts +211 -0
- package/src/embed/index.ts +4 -0
- package/src/error/index.ts +34 -0
- package/src/error/invalid-argument-error.ts +34 -0
- package/src/error/invalid-stream-part-error.ts +28 -0
- package/src/error/invalid-tool-approval-error.ts +26 -0
- package/src/error/invalid-tool-input-error.ts +33 -0
- package/src/error/no-image-generated-error.ts +39 -0
- package/src/error/no-object-generated-error.ts +70 -0
- package/src/error/no-output-generated-error.ts +26 -0
- package/src/error/no-speech-generated-error.ts +18 -0
- package/src/error/no-such-tool-error.ts +35 -0
- package/src/error/no-transcript-generated-error.ts +20 -0
- package/src/error/tool-call-not-found-for-approval-error.ts +32 -0
- package/src/error/tool-call-repair-error.ts +30 -0
- package/src/error/unsupported-model-version-error.ts +23 -0
- package/src/error/verify-no-object-generated-error.ts +27 -0
- package/src/generate-image/generate-image-result.ts +42 -0
- package/src/generate-image/generate-image.test.ts +1420 -0
- package/src/generate-image/generate-image.ts +360 -0
- package/src/generate-image/index.ts +18 -0
- package/src/generate-object/__snapshots__/generate-object.test.ts.snap +133 -0
- package/src/generate-object/__snapshots__/stream-object.test.ts.snap +297 -0
- package/src/generate-object/generate-object-result.ts +67 -0
- package/src/generate-object/generate-object.test-d.ts +49 -0
- package/src/generate-object/generate-object.test.ts +1191 -0
- package/src/generate-object/generate-object.ts +518 -0
- package/src/generate-object/index.ts +9 -0
- package/src/generate-object/inject-json-instruction.test.ts +181 -0
- package/src/generate-object/inject-json-instruction.ts +30 -0
- package/src/generate-object/output-strategy.ts +415 -0
- package/src/generate-object/parse-and-validate-object-result.ts +111 -0
- package/src/generate-object/repair-text.ts +12 -0
- package/src/generate-object/stream-object-result.ts +120 -0
- package/src/generate-object/stream-object.test-d.ts +74 -0
- package/src/generate-object/stream-object.test.ts +1950 -0
- package/src/generate-object/stream-object.ts +986 -0
- package/src/generate-object/validate-object-generation-input.ts +144 -0
- package/src/generate-speech/generate-speech-result.ts +30 -0
- package/src/generate-speech/generate-speech.test.ts +300 -0
- package/src/generate-speech/generate-speech.ts +190 -0
- package/src/generate-speech/generated-audio-file.ts +65 -0
- package/src/generate-speech/index.ts +3 -0
- package/src/generate-text/__snapshots__/generate-text.test.ts.snap +1872 -0
- package/src/generate-text/__snapshots__/stream-text.test.ts.snap +1255 -0
- package/src/generate-text/collect-tool-approvals.test.ts +553 -0
- package/src/generate-text/collect-tool-approvals.ts +116 -0
- package/src/generate-text/content-part.ts +25 -0
- package/src/generate-text/execute-tool-call.ts +129 -0
- package/src/generate-text/extract-reasoning-content.ts +17 -0
- package/src/generate-text/extract-text-content.ts +15 -0
- package/src/generate-text/generate-text-result.ts +168 -0
- package/src/generate-text/generate-text.test-d.ts +68 -0
- package/src/generate-text/generate-text.test.ts +7011 -0
- package/src/generate-text/generate-text.ts +1223 -0
- package/src/generate-text/generated-file.ts +70 -0
- package/src/generate-text/index.ts +57 -0
- package/src/generate-text/is-approval-needed.ts +29 -0
- package/src/generate-text/output-utils.ts +23 -0
- package/src/generate-text/output.test.ts +698 -0
- package/src/generate-text/output.ts +590 -0
- package/src/generate-text/parse-tool-call.test.ts +570 -0
- package/src/generate-text/parse-tool-call.ts +188 -0
- package/src/generate-text/prepare-step.ts +103 -0
- package/src/generate-text/prune-messages.test.ts +720 -0
- package/src/generate-text/prune-messages.ts +167 -0
- package/src/generate-text/reasoning-output.ts +20 -0
- package/src/generate-text/reasoning.ts +8 -0
- package/src/generate-text/response-message.ts +10 -0
- package/src/generate-text/run-tools-transformation.test.ts +1143 -0
- package/src/generate-text/run-tools-transformation.ts +420 -0
- package/src/generate-text/smooth-stream.test.ts +2101 -0
- package/src/generate-text/smooth-stream.ts +162 -0
- package/src/generate-text/step-result.ts +238 -0
- package/src/generate-text/stop-condition.ts +29 -0
- package/src/generate-text/stream-text-result.ts +463 -0
- package/src/generate-text/stream-text.test-d.ts +200 -0
- package/src/generate-text/stream-text.test.ts +19979 -0
- package/src/generate-text/stream-text.ts +2505 -0
- package/src/generate-text/to-response-messages.test.ts +922 -0
- package/src/generate-text/to-response-messages.ts +163 -0
- package/src/generate-text/tool-approval-request-output.ts +21 -0
- package/src/generate-text/tool-call-repair-function.ts +27 -0
- package/src/generate-text/tool-call.ts +47 -0
- package/src/generate-text/tool-error.ts +34 -0
- package/src/generate-text/tool-output-denied.ts +21 -0
- package/src/generate-text/tool-output.ts +7 -0
- package/src/generate-text/tool-result.ts +36 -0
- package/src/generate-text/tool-set.ts +14 -0
- package/src/global.ts +24 -0
- package/src/index.ts +50 -0
- package/src/logger/index.ts +6 -0
- package/src/logger/log-warnings.test.ts +351 -0
- package/src/logger/log-warnings.ts +119 -0
- package/src/middleware/__snapshots__/simulate-streaming-middleware.test.ts.snap +64 -0
- package/src/middleware/add-tool-input-examples-middleware.test.ts +476 -0
- package/src/middleware/add-tool-input-examples-middleware.ts +90 -0
- package/src/middleware/default-embedding-settings-middleware.test.ts +126 -0
- package/src/middleware/default-embedding-settings-middleware.ts +22 -0
- package/src/middleware/default-settings-middleware.test.ts +388 -0
- package/src/middleware/default-settings-middleware.ts +33 -0
- package/src/middleware/extract-json-middleware.test.ts +827 -0
- package/src/middleware/extract-json-middleware.ts +197 -0
- package/src/middleware/extract-reasoning-middleware.test.ts +1028 -0
- package/src/middleware/extract-reasoning-middleware.ts +238 -0
- package/src/middleware/index.ts +10 -0
- package/src/middleware/simulate-streaming-middleware.test.ts +911 -0
- package/src/middleware/simulate-streaming-middleware.ts +79 -0
- package/src/middleware/wrap-embedding-model.test.ts +358 -0
- package/src/middleware/wrap-embedding-model.ts +86 -0
- package/src/middleware/wrap-image-model.test.ts +423 -0
- package/src/middleware/wrap-image-model.ts +85 -0
- package/src/middleware/wrap-language-model.test.ts +518 -0
- package/src/middleware/wrap-language-model.ts +104 -0
- package/src/middleware/wrap-provider.test.ts +120 -0
- package/src/middleware/wrap-provider.ts +51 -0
- package/src/model/as-embedding-model-v3.test.ts +319 -0
- package/src/model/as-embedding-model-v3.ts +24 -0
- package/src/model/as-image-model-v3.test.ts +409 -0
- package/src/model/as-image-model-v3.ts +24 -0
- package/src/model/as-language-model-v3.test.ts +508 -0
- package/src/model/as-language-model-v3.ts +103 -0
- package/src/model/as-provider-v3.ts +36 -0
- package/src/model/as-speech-model-v3.test.ts +356 -0
- package/src/model/as-speech-model-v3.ts +24 -0
- package/src/model/as-transcription-model-v3.test.ts +529 -0
- package/src/model/as-transcription-model-v3.ts +24 -0
- package/src/model/resolve-model.test.ts +244 -0
- package/src/model/resolve-model.ts +126 -0
- package/src/prompt/call-settings.ts +148 -0
- package/src/prompt/content-part.ts +209 -0
- package/src/prompt/convert-to-language-model-prompt.test.ts +2018 -0
- package/src/prompt/convert-to-language-model-prompt.ts +442 -0
- package/src/prompt/create-tool-model-output.test.ts +508 -0
- package/src/prompt/create-tool-model-output.ts +34 -0
- package/src/prompt/data-content.test.ts +15 -0
- package/src/prompt/data-content.ts +134 -0
- package/src/prompt/index.ts +27 -0
- package/src/prompt/invalid-data-content-error.ts +29 -0
- package/src/prompt/invalid-message-role-error.ts +27 -0
- package/src/prompt/message-conversion-error.ts +28 -0
- package/src/prompt/message.ts +68 -0
- package/src/prompt/prepare-call-settings.test.ts +159 -0
- package/src/prompt/prepare-call-settings.ts +108 -0
- package/src/prompt/prepare-tools-and-tool-choice.test.ts +461 -0
- package/src/prompt/prepare-tools-and-tool-choice.ts +86 -0
- package/src/prompt/prompt.ts +43 -0
- package/src/prompt/split-data-url.ts +17 -0
- package/src/prompt/standardize-prompt.test.ts +82 -0
- package/src/prompt/standardize-prompt.ts +99 -0
- package/src/prompt/wrap-gateway-error.ts +29 -0
- package/src/registry/custom-provider.test.ts +211 -0
- package/src/registry/custom-provider.ts +155 -0
- package/src/registry/index.ts +7 -0
- package/src/registry/no-such-provider-error.ts +41 -0
- package/src/registry/provider-registry.test.ts +691 -0
- package/src/registry/provider-registry.ts +328 -0
- package/src/rerank/index.ts +2 -0
- package/src/rerank/rerank-result.ts +70 -0
- package/src/rerank/rerank.test.ts +516 -0
- package/src/rerank/rerank.ts +237 -0
- package/src/telemetry/assemble-operation-name.ts +21 -0
- package/src/telemetry/get-base-telemetry-attributes.ts +53 -0
- package/src/telemetry/get-tracer.ts +20 -0
- package/src/telemetry/noop-tracer.ts +69 -0
- package/src/telemetry/record-span.ts +63 -0
- package/src/telemetry/select-telemetry-attributes.ts +78 -0
- package/src/telemetry/select-temetry-attributes.test.ts +114 -0
- package/src/telemetry/stringify-for-telemetry.test.ts +114 -0
- package/src/telemetry/stringify-for-telemetry.ts +33 -0
- package/src/telemetry/telemetry-settings.ts +44 -0
- package/src/test/mock-embedding-model-v2.ts +35 -0
- package/src/test/mock-embedding-model-v3.ts +48 -0
- package/src/test/mock-image-model-v2.ts +28 -0
- package/src/test/mock-image-model-v3.ts +28 -0
- package/src/test/mock-language-model-v2.ts +72 -0
- package/src/test/mock-language-model-v3.ts +77 -0
- package/src/test/mock-provider-v2.ts +68 -0
- package/src/test/mock-provider-v3.ts +80 -0
- package/src/test/mock-reranking-model-v3.ts +25 -0
- package/src/test/mock-server-response.ts +69 -0
- package/src/test/mock-speech-model-v2.ts +24 -0
- package/src/test/mock-speech-model-v3.ts +24 -0
- package/src/test/mock-tracer.ts +156 -0
- package/src/test/mock-transcription-model-v2.ts +24 -0
- package/src/test/mock-transcription-model-v3.ts +24 -0
- package/src/test/mock-values.ts +4 -0
- package/src/test/not-implemented.ts +3 -0
- package/src/text-stream/create-text-stream-response.test.ts +38 -0
- package/src/text-stream/create-text-stream-response.ts +18 -0
- package/src/text-stream/index.ts +2 -0
- package/src/text-stream/pipe-text-stream-to-response.test.ts +38 -0
- package/src/text-stream/pipe-text-stream-to-response.ts +26 -0
- package/src/transcribe/index.ts +2 -0
- package/src/transcribe/transcribe-result.ts +60 -0
- package/src/transcribe/transcribe.test.ts +313 -0
- package/src/transcribe/transcribe.ts +173 -0
- package/src/types/embedding-model-middleware.ts +3 -0
- package/src/types/embedding-model.ts +18 -0
- package/src/types/image-model-middleware.ts +3 -0
- package/src/types/image-model-response-metadata.ts +16 -0
- package/src/types/image-model.ts +19 -0
- package/src/types/index.ts +29 -0
- package/src/types/json-value.ts +15 -0
- package/src/types/language-model-middleware.ts +3 -0
- package/src/types/language-model-request-metadata.ts +6 -0
- package/src/types/language-model-response-metadata.ts +21 -0
- package/src/types/language-model.ts +104 -0
- package/src/types/provider-metadata.ts +16 -0
- package/src/types/provider.ts +55 -0
- package/src/types/reranking-model.ts +6 -0
- package/src/types/speech-model-response-metadata.ts +21 -0
- package/src/types/speech-model.ts +6 -0
- package/src/types/transcription-model-response-metadata.ts +16 -0
- package/src/types/transcription-model.ts +9 -0
- package/src/types/usage.ts +200 -0
- package/src/types/warning.ts +7 -0
- package/src/ui/__snapshots__/append-response-messages.test.ts.snap +416 -0
- package/src/ui/__snapshots__/convert-to-model-messages.test.ts.snap +419 -0
- package/src/ui/__snapshots__/process-chat-text-response.test.ts.snap +142 -0
- package/src/ui/call-completion-api.ts +157 -0
- package/src/ui/chat-transport.ts +83 -0
- package/src/ui/chat.test-d.ts +233 -0
- package/src/ui/chat.test.ts +2695 -0
- package/src/ui/chat.ts +716 -0
- package/src/ui/convert-file-list-to-file-ui-parts.ts +36 -0
- package/src/ui/convert-to-model-messages.test.ts +2775 -0
- package/src/ui/convert-to-model-messages.ts +373 -0
- package/src/ui/default-chat-transport.ts +36 -0
- package/src/ui/direct-chat-transport.test.ts +446 -0
- package/src/ui/direct-chat-transport.ts +118 -0
- package/src/ui/http-chat-transport.test.ts +185 -0
- package/src/ui/http-chat-transport.ts +292 -0
- package/src/ui/index.ts +71 -0
- package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +44 -0
- package/src/ui/last-assistant-message-is-complete-with-tool-calls.test.ts +371 -0
- package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +39 -0
- package/src/ui/process-text-stream.test.ts +38 -0
- package/src/ui/process-text-stream.ts +16 -0
- package/src/ui/process-ui-message-stream.test.ts +8052 -0
- package/src/ui/process-ui-message-stream.ts +713 -0
- package/src/ui/text-stream-chat-transport.ts +23 -0
- package/src/ui/transform-text-to-ui-message-stream.test.ts +124 -0
- package/src/ui/transform-text-to-ui-message-stream.ts +27 -0
- package/src/ui/ui-messages.test.ts +48 -0
- package/src/ui/ui-messages.ts +534 -0
- package/src/ui/use-completion.ts +84 -0
- package/src/ui/validate-ui-messages.test.ts +1428 -0
- package/src/ui/validate-ui-messages.ts +476 -0
- package/src/ui-message-stream/create-ui-message-stream-response.test.ts +266 -0
- package/src/ui-message-stream/create-ui-message-stream-response.ts +32 -0
- package/src/ui-message-stream/create-ui-message-stream.test.ts +639 -0
- package/src/ui-message-stream/create-ui-message-stream.ts +124 -0
- package/src/ui-message-stream/get-response-ui-message-id.test.ts +55 -0
- package/src/ui-message-stream/get-response-ui-message-id.ts +24 -0
- package/src/ui-message-stream/handle-ui-message-stream-finish.test.ts +429 -0
- package/src/ui-message-stream/handle-ui-message-stream-finish.ts +135 -0
- package/src/ui-message-stream/index.ts +13 -0
- package/src/ui-message-stream/json-to-sse-transform-stream.ts +12 -0
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.test.ts +90 -0
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +40 -0
- package/src/ui-message-stream/read-ui-message-stream.test.ts +122 -0
- package/src/ui-message-stream/read-ui-message-stream.ts +87 -0
- package/src/ui-message-stream/ui-message-chunks.test-d.ts +18 -0
- package/src/ui-message-stream/ui-message-chunks.ts +344 -0
- package/src/ui-message-stream/ui-message-stream-headers.ts +7 -0
- package/src/ui-message-stream/ui-message-stream-on-finish-callback.ts +32 -0
- package/src/ui-message-stream/ui-message-stream-response-init.ts +5 -0
- package/src/ui-message-stream/ui-message-stream-writer.ts +24 -0
- package/src/util/as-array.ts +3 -0
- package/src/util/async-iterable-stream.test.ts +241 -0
- package/src/util/async-iterable-stream.ts +94 -0
- package/src/util/consume-stream.ts +29 -0
- package/src/util/cosine-similarity.test.ts +57 -0
- package/src/util/cosine-similarity.ts +47 -0
- package/src/util/create-resolvable-promise.ts +30 -0
- package/src/util/create-stitchable-stream.test.ts +239 -0
- package/src/util/create-stitchable-stream.ts +112 -0
- package/src/util/data-url.ts +17 -0
- package/src/util/deep-partial.ts +84 -0
- package/src/util/detect-media-type.test.ts +670 -0
- package/src/util/detect-media-type.ts +184 -0
- package/src/util/download/download-function.ts +45 -0
- package/src/util/download/download.test.ts +69 -0
- package/src/util/download/download.ts +46 -0
- package/src/util/error-handler.ts +1 -0
- package/src/util/fix-json.test.ts +279 -0
- package/src/util/fix-json.ts +401 -0
- package/src/util/get-potential-start-index.test.ts +34 -0
- package/src/util/get-potential-start-index.ts +30 -0
- package/src/util/index.ts +11 -0
- package/src/util/is-deep-equal-data.test.ts +119 -0
- package/src/util/is-deep-equal-data.ts +48 -0
- package/src/util/is-non-empty-object.ts +5 -0
- package/src/util/job.ts +1 -0
- package/src/util/log-v2-compatibility-warning.ts +21 -0
- package/src/util/merge-abort-signals.test.ts +155 -0
- package/src/util/merge-abort-signals.ts +43 -0
- package/src/util/merge-objects.test.ts +118 -0
- package/src/util/merge-objects.ts +79 -0
- package/src/util/now.ts +4 -0
- package/src/util/parse-partial-json.test.ts +80 -0
- package/src/util/parse-partial-json.ts +30 -0
- package/src/util/prepare-headers.test.ts +51 -0
- package/src/util/prepare-headers.ts +14 -0
- package/src/util/prepare-retries.test.ts +10 -0
- package/src/util/prepare-retries.ts +47 -0
- package/src/util/retry-error.ts +41 -0
- package/src/util/retry-with-exponential-backoff.test.ts +446 -0
- package/src/util/retry-with-exponential-backoff.ts +154 -0
- package/src/util/serial-job-executor.test.ts +162 -0
- package/src/util/serial-job-executor.ts +36 -0
- package/src/util/simulate-readable-stream.test.ts +98 -0
- package/src/util/simulate-readable-stream.ts +39 -0
- package/src/util/split-array.test.ts +60 -0
- package/src/util/split-array.ts +20 -0
- package/src/util/value-of.ts +65 -0
- package/src/util/write-to-server-response.test.ts +266 -0
- package/src/util/write-to-server-response.ts +49 -0
- package/src/version.ts +5 -0
package/src/ui/chat.ts
ADDED
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FlexibleSchema,
|
|
3
|
+
generateId as generateIdFunc,
|
|
4
|
+
IdGenerator,
|
|
5
|
+
InferSchema,
|
|
6
|
+
} from '@ai-sdk/provider-utils';
|
|
7
|
+
import { FinishReason } from '../types/language-model';
|
|
8
|
+
import { UIMessageChunk } from '../ui-message-stream/ui-message-chunks';
|
|
9
|
+
import { consumeStream } from '../util/consume-stream';
|
|
10
|
+
import { SerialJobExecutor } from '../util/serial-job-executor';
|
|
11
|
+
import { ChatTransport } from './chat-transport';
|
|
12
|
+
import { convertFileListToFileUIParts } from './convert-file-list-to-file-ui-parts';
|
|
13
|
+
import { DefaultChatTransport } from './default-chat-transport';
|
|
14
|
+
import {
|
|
15
|
+
createStreamingUIMessageState,
|
|
16
|
+
processUIMessageStream,
|
|
17
|
+
StreamingUIMessageState,
|
|
18
|
+
} from './process-ui-message-stream';
|
|
19
|
+
import {
|
|
20
|
+
InferUIMessageToolCall,
|
|
21
|
+
isToolUIPart,
|
|
22
|
+
UIMessagePart,
|
|
23
|
+
UITools,
|
|
24
|
+
type DataUIPart,
|
|
25
|
+
type FileUIPart,
|
|
26
|
+
type InferUIMessageData,
|
|
27
|
+
type InferUIMessageMetadata,
|
|
28
|
+
type InferUIMessageTools,
|
|
29
|
+
type UIDataTypes,
|
|
30
|
+
type UIMessage,
|
|
31
|
+
} from './ui-messages';
|
|
32
|
+
|
|
33
|
+
export type CreateUIMessage<UI_MESSAGE extends UIMessage> = Omit<
|
|
34
|
+
UI_MESSAGE,
|
|
35
|
+
'id' | 'role'
|
|
36
|
+
> & {
|
|
37
|
+
id?: UI_MESSAGE['id'];
|
|
38
|
+
role?: UI_MESSAGE['role'];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type UIDataPartSchemas = Record<string, FlexibleSchema>;
|
|
42
|
+
|
|
43
|
+
export type UIDataTypesToSchemas<T extends UIDataTypes> = {
|
|
44
|
+
[K in keyof T]: FlexibleSchema<T[K]>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type InferUIDataParts<T extends UIDataPartSchemas> = {
|
|
48
|
+
[K in keyof T]: InferSchema<T[K]>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type ChatRequestOptions = {
|
|
52
|
+
/**
|
|
53
|
+
Additional headers that should be to be passed to the API endpoint.
|
|
54
|
+
*/
|
|
55
|
+
headers?: Record<string, string> | Headers;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
Additional body JSON properties that should be sent to the API endpoint.
|
|
59
|
+
*/
|
|
60
|
+
body?: object; // TODO JSONStringifyable
|
|
61
|
+
|
|
62
|
+
metadata?: unknown;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Function that can be called to add a tool approval response to the chat.
|
|
67
|
+
*/
|
|
68
|
+
export type ChatAddToolApproveResponseFunction = ({
|
|
69
|
+
id,
|
|
70
|
+
approved,
|
|
71
|
+
reason,
|
|
72
|
+
}: {
|
|
73
|
+
id: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Flag indicating whether the approval was granted or denied.
|
|
77
|
+
*/
|
|
78
|
+
approved: boolean;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Optional reason for the approval or denial.
|
|
82
|
+
*/
|
|
83
|
+
reason?: string;
|
|
84
|
+
}) => void | PromiseLike<void>;
|
|
85
|
+
|
|
86
|
+
export type ChatStatus = 'submitted' | 'streaming' | 'ready' | 'error';
|
|
87
|
+
|
|
88
|
+
type ActiveResponse<UI_MESSAGE extends UIMessage> = {
|
|
89
|
+
state: StreamingUIMessageState<UI_MESSAGE>;
|
|
90
|
+
abortController: AbortController;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export interface ChatState<UI_MESSAGE extends UIMessage> {
|
|
94
|
+
status: ChatStatus;
|
|
95
|
+
|
|
96
|
+
error: Error | undefined;
|
|
97
|
+
|
|
98
|
+
messages: UI_MESSAGE[];
|
|
99
|
+
pushMessage: (message: UI_MESSAGE) => void;
|
|
100
|
+
popMessage: () => void;
|
|
101
|
+
replaceMessage: (index: number, message: UI_MESSAGE) => void;
|
|
102
|
+
|
|
103
|
+
snapshot: <T>(thing: T) => T;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ChatOnErrorCallback = (error: Error) => void;
|
|
107
|
+
|
|
108
|
+
export type ChatOnToolCallCallback<UI_MESSAGE extends UIMessage = UIMessage> =
|
|
109
|
+
(options: {
|
|
110
|
+
toolCall: InferUIMessageToolCall<UI_MESSAGE>;
|
|
111
|
+
}) => void | PromiseLike<void>;
|
|
112
|
+
|
|
113
|
+
export type ChatOnDataCallback<UI_MESSAGE extends UIMessage> = (
|
|
114
|
+
dataPart: DataUIPart<InferUIMessageData<UI_MESSAGE>>,
|
|
115
|
+
) => void;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Function that is called when the assistant response has finished streaming.
|
|
119
|
+
*
|
|
120
|
+
* @param message The assistant message that was streamed.
|
|
121
|
+
* @param messages The full chat history, including the assistant message.
|
|
122
|
+
*
|
|
123
|
+
* @param isAbort Indicates whether the request has been aborted.
|
|
124
|
+
* @param isDisconnect Indicates whether the request has been ended by a network error.
|
|
125
|
+
* @param isError Indicates whether the request has been ended by an error.
|
|
126
|
+
* @param finishReason The reason why the generation finished.
|
|
127
|
+
*/
|
|
128
|
+
export type ChatOnFinishCallback<UI_MESSAGE extends UIMessage> = (options: {
|
|
129
|
+
message: UI_MESSAGE;
|
|
130
|
+
messages: UI_MESSAGE[];
|
|
131
|
+
isAbort: boolean;
|
|
132
|
+
isDisconnect: boolean;
|
|
133
|
+
isError: boolean;
|
|
134
|
+
finishReason?: FinishReason;
|
|
135
|
+
}) => void;
|
|
136
|
+
|
|
137
|
+
export interface ChatInit<UI_MESSAGE extends UIMessage> {
|
|
138
|
+
/**
|
|
139
|
+
* A unique identifier for the chat. If not provided, a random one will be
|
|
140
|
+
* generated.
|
|
141
|
+
*/
|
|
142
|
+
id?: string;
|
|
143
|
+
|
|
144
|
+
messageMetadataSchema?: FlexibleSchema<InferUIMessageMetadata<UI_MESSAGE>>;
|
|
145
|
+
dataPartSchemas?: UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>;
|
|
146
|
+
|
|
147
|
+
messages?: UI_MESSAGE[];
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A way to provide a function that is going to be used for ids for messages and the chat.
|
|
151
|
+
* If not provided the default AI SDK `generateId` is used.
|
|
152
|
+
*/
|
|
153
|
+
generateId?: IdGenerator;
|
|
154
|
+
|
|
155
|
+
transport?: ChatTransport<UI_MESSAGE>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Callback function to be called when an error is encountered.
|
|
159
|
+
*/
|
|
160
|
+
onError?: ChatOnErrorCallback;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
Optional callback function that is invoked when a tool call is received.
|
|
164
|
+
Intended for automatic client-side tool execution.
|
|
165
|
+
|
|
166
|
+
You can optionally return a result for the tool call,
|
|
167
|
+
either synchronously or asynchronously.
|
|
168
|
+
*/
|
|
169
|
+
onToolCall?: ChatOnToolCallCallback<UI_MESSAGE>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Function that is called when the assistant response has finished streaming.
|
|
173
|
+
*/
|
|
174
|
+
onFinish?: ChatOnFinishCallback<UI_MESSAGE>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Optional callback function that is called when a data part is received.
|
|
178
|
+
*
|
|
179
|
+
* @param data The data part that was received.
|
|
180
|
+
*/
|
|
181
|
+
onData?: ChatOnDataCallback<UI_MESSAGE>;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* When provided, this function will be called when the stream is finished or a tool call is added
|
|
185
|
+
* to determine if the current messages should be resubmitted.
|
|
186
|
+
*/
|
|
187
|
+
sendAutomaticallyWhen?: (options: {
|
|
188
|
+
messages: UI_MESSAGE[];
|
|
189
|
+
}) => boolean | PromiseLike<boolean>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
193
|
+
readonly id: string;
|
|
194
|
+
readonly generateId: IdGenerator;
|
|
195
|
+
|
|
196
|
+
protected state: ChatState<UI_MESSAGE>;
|
|
197
|
+
|
|
198
|
+
private messageMetadataSchema:
|
|
199
|
+
| FlexibleSchema<InferUIMessageMetadata<UI_MESSAGE>>
|
|
200
|
+
| undefined;
|
|
201
|
+
private dataPartSchemas:
|
|
202
|
+
| UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>
|
|
203
|
+
| undefined;
|
|
204
|
+
private readonly transport: ChatTransport<UI_MESSAGE>;
|
|
205
|
+
private onError?: ChatInit<UI_MESSAGE>['onError'];
|
|
206
|
+
private onToolCall?: ChatInit<UI_MESSAGE>['onToolCall'];
|
|
207
|
+
private onFinish?: ChatInit<UI_MESSAGE>['onFinish'];
|
|
208
|
+
private onData?: ChatInit<UI_MESSAGE>['onData'];
|
|
209
|
+
private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
|
|
210
|
+
|
|
211
|
+
private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
|
|
212
|
+
private jobExecutor = new SerialJobExecutor();
|
|
213
|
+
|
|
214
|
+
constructor({
|
|
215
|
+
generateId = generateIdFunc,
|
|
216
|
+
id = generateId(),
|
|
217
|
+
transport = new DefaultChatTransport(),
|
|
218
|
+
messageMetadataSchema,
|
|
219
|
+
dataPartSchemas,
|
|
220
|
+
state,
|
|
221
|
+
onError,
|
|
222
|
+
onToolCall,
|
|
223
|
+
onFinish,
|
|
224
|
+
onData,
|
|
225
|
+
sendAutomaticallyWhen,
|
|
226
|
+
}: Omit<ChatInit<UI_MESSAGE>, 'messages'> & {
|
|
227
|
+
state: ChatState<UI_MESSAGE>;
|
|
228
|
+
}) {
|
|
229
|
+
this.id = id;
|
|
230
|
+
this.transport = transport;
|
|
231
|
+
this.generateId = generateId;
|
|
232
|
+
this.messageMetadataSchema = messageMetadataSchema;
|
|
233
|
+
this.dataPartSchemas = dataPartSchemas;
|
|
234
|
+
this.state = state;
|
|
235
|
+
this.onError = onError;
|
|
236
|
+
this.onToolCall = onToolCall;
|
|
237
|
+
this.onFinish = onFinish;
|
|
238
|
+
this.onData = onData;
|
|
239
|
+
this.sendAutomaticallyWhen = sendAutomaticallyWhen;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Hook status:
|
|
244
|
+
*
|
|
245
|
+
* - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
|
|
246
|
+
* - `streaming`: The response is actively streaming in from the API, receiving chunks of data.
|
|
247
|
+
* - `ready`: The full response has been received and processed; a new user message can be submitted.
|
|
248
|
+
* - `error`: An error occurred during the API request, preventing successful completion.
|
|
249
|
+
*/
|
|
250
|
+
get status(): ChatStatus {
|
|
251
|
+
return this.state.status;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
protected setStatus({
|
|
255
|
+
status,
|
|
256
|
+
error,
|
|
257
|
+
}: {
|
|
258
|
+
status: ChatStatus;
|
|
259
|
+
error?: Error;
|
|
260
|
+
}) {
|
|
261
|
+
if (this.status === status) return;
|
|
262
|
+
|
|
263
|
+
this.state.status = status;
|
|
264
|
+
this.state.error = error;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
get error() {
|
|
268
|
+
return this.state.error;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get messages(): UI_MESSAGE[] {
|
|
272
|
+
return this.state.messages;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
get lastMessage(): UI_MESSAGE | undefined {
|
|
276
|
+
return this.state.messages[this.state.messages.length - 1];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
set messages(messages: UI_MESSAGE[]) {
|
|
280
|
+
this.state.messages = messages;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Appends or replaces a user message to the chat list. This triggers the API call to fetch
|
|
285
|
+
* the assistant's response.
|
|
286
|
+
*
|
|
287
|
+
* If a messageId is provided, the message will be replaced.
|
|
288
|
+
*/
|
|
289
|
+
sendMessage = async (
|
|
290
|
+
message?:
|
|
291
|
+
| (CreateUIMessage<UI_MESSAGE> & {
|
|
292
|
+
text?: never;
|
|
293
|
+
files?: never;
|
|
294
|
+
messageId?: string;
|
|
295
|
+
})
|
|
296
|
+
| {
|
|
297
|
+
text: string;
|
|
298
|
+
files?: FileList | FileUIPart[];
|
|
299
|
+
metadata?: InferUIMessageMetadata<UI_MESSAGE>;
|
|
300
|
+
parts?: never;
|
|
301
|
+
messageId?: string;
|
|
302
|
+
}
|
|
303
|
+
| {
|
|
304
|
+
files: FileList | FileUIPart[];
|
|
305
|
+
metadata?: InferUIMessageMetadata<UI_MESSAGE>;
|
|
306
|
+
parts?: never;
|
|
307
|
+
messageId?: string;
|
|
308
|
+
},
|
|
309
|
+
options?: ChatRequestOptions,
|
|
310
|
+
): Promise<void> => {
|
|
311
|
+
if (message == null) {
|
|
312
|
+
await this.makeRequest({
|
|
313
|
+
trigger: 'submit-message',
|
|
314
|
+
messageId: this.lastMessage?.id,
|
|
315
|
+
...options,
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let uiMessage: CreateUIMessage<UI_MESSAGE>;
|
|
321
|
+
|
|
322
|
+
if ('text' in message || 'files' in message) {
|
|
323
|
+
const fileParts = Array.isArray(message.files)
|
|
324
|
+
? message.files
|
|
325
|
+
: await convertFileListToFileUIParts(message.files);
|
|
326
|
+
|
|
327
|
+
uiMessage = {
|
|
328
|
+
parts: [
|
|
329
|
+
...fileParts,
|
|
330
|
+
...('text' in message && message.text != null
|
|
331
|
+
? [{ type: 'text' as const, text: message.text }]
|
|
332
|
+
: []),
|
|
333
|
+
],
|
|
334
|
+
} as UI_MESSAGE;
|
|
335
|
+
} else {
|
|
336
|
+
uiMessage = message;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (message.messageId != null) {
|
|
340
|
+
const messageIndex = this.state.messages.findIndex(
|
|
341
|
+
m => m.id === message.messageId,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
if (messageIndex === -1) {
|
|
345
|
+
throw new Error(`message with id ${message.messageId} not found`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (this.state.messages[messageIndex].role !== 'user') {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`message with id ${message.messageId} is not a user message`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// remove all messages after the message with the given id
|
|
355
|
+
this.state.messages = this.state.messages.slice(0, messageIndex + 1);
|
|
356
|
+
|
|
357
|
+
// update the message with the new content
|
|
358
|
+
this.state.replaceMessage(messageIndex, {
|
|
359
|
+
...uiMessage,
|
|
360
|
+
id: message.messageId,
|
|
361
|
+
role: uiMessage.role ?? 'user',
|
|
362
|
+
metadata: message.metadata,
|
|
363
|
+
} as UI_MESSAGE);
|
|
364
|
+
} else {
|
|
365
|
+
this.state.pushMessage({
|
|
366
|
+
...uiMessage,
|
|
367
|
+
id: uiMessage.id ?? this.generateId(),
|
|
368
|
+
role: uiMessage.role ?? 'user',
|
|
369
|
+
metadata: message.metadata,
|
|
370
|
+
} as UI_MESSAGE);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
await this.makeRequest({
|
|
374
|
+
trigger: 'submit-message',
|
|
375
|
+
messageId: message.messageId,
|
|
376
|
+
...options,
|
|
377
|
+
});
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Regenerate the assistant message with the provided message id.
|
|
382
|
+
* If no message id is provided, the last assistant message will be regenerated.
|
|
383
|
+
*/
|
|
384
|
+
regenerate = async ({
|
|
385
|
+
messageId,
|
|
386
|
+
...options
|
|
387
|
+
}: {
|
|
388
|
+
messageId?: string;
|
|
389
|
+
} & ChatRequestOptions = {}): Promise<void> => {
|
|
390
|
+
const messageIndex =
|
|
391
|
+
messageId == null
|
|
392
|
+
? this.state.messages.length - 1
|
|
393
|
+
: this.state.messages.findIndex(message => message.id === messageId);
|
|
394
|
+
|
|
395
|
+
if (messageIndex === -1) {
|
|
396
|
+
throw new Error(`message ${messageId} not found`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// set the messages to the message before the assistant message
|
|
400
|
+
this.state.messages = this.state.messages.slice(
|
|
401
|
+
0,
|
|
402
|
+
// if the message is a user message, we need to include it in the request:
|
|
403
|
+
this.messages[messageIndex].role === 'assistant'
|
|
404
|
+
? messageIndex
|
|
405
|
+
: messageIndex + 1,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
await this.makeRequest({
|
|
409
|
+
trigger: 'regenerate-message',
|
|
410
|
+
messageId,
|
|
411
|
+
...options,
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Attempt to resume an ongoing streaming response.
|
|
417
|
+
*/
|
|
418
|
+
resumeStream = async (options: ChatRequestOptions = {}): Promise<void> => {
|
|
419
|
+
await this.makeRequest({ trigger: 'resume-stream', ...options });
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Clear the error state and set the status to ready if the chat is in an error state.
|
|
424
|
+
*/
|
|
425
|
+
clearError = () => {
|
|
426
|
+
if (this.status === 'error') {
|
|
427
|
+
this.state.error = undefined;
|
|
428
|
+
this.setStatus({ status: 'ready' });
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
addToolApprovalResponse: ChatAddToolApproveResponseFunction = async ({
|
|
433
|
+
id,
|
|
434
|
+
approved,
|
|
435
|
+
reason,
|
|
436
|
+
}) =>
|
|
437
|
+
this.jobExecutor.run(async () => {
|
|
438
|
+
const messages = this.state.messages;
|
|
439
|
+
const lastMessage = messages[messages.length - 1];
|
|
440
|
+
|
|
441
|
+
const updatePart = (
|
|
442
|
+
part: UIMessagePart<UIDataTypes, UITools>,
|
|
443
|
+
): UIMessagePart<UIDataTypes, UITools> =>
|
|
444
|
+
isToolUIPart(part) &&
|
|
445
|
+
part.state === 'approval-requested' &&
|
|
446
|
+
part.approval.id === id
|
|
447
|
+
? {
|
|
448
|
+
...part,
|
|
449
|
+
state: 'approval-responded',
|
|
450
|
+
approval: { id, approved, reason },
|
|
451
|
+
}
|
|
452
|
+
: part;
|
|
453
|
+
|
|
454
|
+
// update the message to trigger an immediate UI update
|
|
455
|
+
this.state.replaceMessage(messages.length - 1, {
|
|
456
|
+
...lastMessage,
|
|
457
|
+
parts: lastMessage.parts.map(updatePart),
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// update the active response if it exists
|
|
461
|
+
if (this.activeResponse) {
|
|
462
|
+
this.activeResponse.state.message.parts =
|
|
463
|
+
this.activeResponse.state.message.parts.map(updatePart);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
467
|
+
if (
|
|
468
|
+
this.status !== 'streaming' &&
|
|
469
|
+
this.status !== 'submitted' &&
|
|
470
|
+
this.sendAutomaticallyWhen?.({ messages: this.state.messages })
|
|
471
|
+
) {
|
|
472
|
+
// no await to avoid deadlocking
|
|
473
|
+
this.makeRequest({
|
|
474
|
+
trigger: 'submit-message',
|
|
475
|
+
messageId: this.lastMessage?.id,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
addToolOutput = async <TOOL extends keyof InferUIMessageTools<UI_MESSAGE>>({
|
|
481
|
+
state = 'output-available',
|
|
482
|
+
tool,
|
|
483
|
+
toolCallId,
|
|
484
|
+
output,
|
|
485
|
+
errorText,
|
|
486
|
+
}:
|
|
487
|
+
| {
|
|
488
|
+
state?: 'output-available';
|
|
489
|
+
tool: TOOL;
|
|
490
|
+
toolCallId: string;
|
|
491
|
+
output: InferUIMessageTools<UI_MESSAGE>[TOOL]['output'];
|
|
492
|
+
errorText?: never;
|
|
493
|
+
}
|
|
494
|
+
| {
|
|
495
|
+
state: 'output-error';
|
|
496
|
+
tool: TOOL;
|
|
497
|
+
toolCallId: string;
|
|
498
|
+
output?: never;
|
|
499
|
+
errorText: string;
|
|
500
|
+
}) =>
|
|
501
|
+
this.jobExecutor.run(async () => {
|
|
502
|
+
const messages = this.state.messages;
|
|
503
|
+
const lastMessage = messages[messages.length - 1];
|
|
504
|
+
|
|
505
|
+
const updatePart = (
|
|
506
|
+
part: UIMessagePart<UIDataTypes, UITools>,
|
|
507
|
+
): UIMessagePart<UIDataTypes, UITools> =>
|
|
508
|
+
isToolUIPart(part) && part.toolCallId === toolCallId
|
|
509
|
+
? ({ ...part, state, output, errorText } as typeof part)
|
|
510
|
+
: part;
|
|
511
|
+
|
|
512
|
+
// update the message to trigger an immediate UI update
|
|
513
|
+
this.state.replaceMessage(messages.length - 1, {
|
|
514
|
+
...lastMessage,
|
|
515
|
+
parts: lastMessage.parts.map(updatePart),
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// update the active response if it exists
|
|
519
|
+
if (this.activeResponse) {
|
|
520
|
+
this.activeResponse.state.message.parts =
|
|
521
|
+
this.activeResponse.state.message.parts.map(updatePart);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
525
|
+
if (
|
|
526
|
+
this.status !== 'streaming' &&
|
|
527
|
+
this.status !== 'submitted' &&
|
|
528
|
+
this.sendAutomaticallyWhen?.({ messages: this.state.messages })
|
|
529
|
+
) {
|
|
530
|
+
// no await to avoid deadlocking
|
|
531
|
+
this.makeRequest({
|
|
532
|
+
trigger: 'submit-message',
|
|
533
|
+
messageId: this.lastMessage?.id,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
/** @deprecated Use addToolOutput */
|
|
539
|
+
addToolResult = this.addToolOutput;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Abort the current request immediately, keep the generated tokens if any.
|
|
543
|
+
*/
|
|
544
|
+
stop = async () => {
|
|
545
|
+
if (this.status !== 'streaming' && this.status !== 'submitted') return;
|
|
546
|
+
|
|
547
|
+
if (this.activeResponse?.abortController) {
|
|
548
|
+
this.activeResponse.abortController.abort();
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
private async makeRequest({
|
|
553
|
+
trigger,
|
|
554
|
+
metadata,
|
|
555
|
+
headers,
|
|
556
|
+
body,
|
|
557
|
+
messageId,
|
|
558
|
+
}: {
|
|
559
|
+
trigger: 'submit-message' | 'resume-stream' | 'regenerate-message';
|
|
560
|
+
messageId?: string;
|
|
561
|
+
} & ChatRequestOptions) {
|
|
562
|
+
this.setStatus({ status: 'submitted', error: undefined });
|
|
563
|
+
|
|
564
|
+
const lastMessage = this.lastMessage;
|
|
565
|
+
|
|
566
|
+
let isAbort = false;
|
|
567
|
+
let isDisconnect = false;
|
|
568
|
+
let isError = false;
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
const activeResponse = {
|
|
572
|
+
state: createStreamingUIMessageState({
|
|
573
|
+
lastMessage: this.state.snapshot(lastMessage),
|
|
574
|
+
messageId: this.generateId(),
|
|
575
|
+
}),
|
|
576
|
+
abortController: new AbortController(),
|
|
577
|
+
} as ActiveResponse<UI_MESSAGE>;
|
|
578
|
+
|
|
579
|
+
activeResponse.abortController.signal.addEventListener('abort', () => {
|
|
580
|
+
isAbort = true;
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
this.activeResponse = activeResponse;
|
|
584
|
+
|
|
585
|
+
let stream: ReadableStream<UIMessageChunk>;
|
|
586
|
+
|
|
587
|
+
if (trigger === 'resume-stream') {
|
|
588
|
+
const reconnect = await this.transport.reconnectToStream({
|
|
589
|
+
chatId: this.id,
|
|
590
|
+
metadata,
|
|
591
|
+
headers,
|
|
592
|
+
body,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
if (reconnect == null) {
|
|
596
|
+
this.setStatus({ status: 'ready' });
|
|
597
|
+
return; // no active stream found, so we do not resume
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
stream = reconnect;
|
|
601
|
+
} else {
|
|
602
|
+
stream = await this.transport.sendMessages({
|
|
603
|
+
chatId: this.id,
|
|
604
|
+
messages: this.state.messages,
|
|
605
|
+
abortSignal: activeResponse.abortController.signal,
|
|
606
|
+
metadata,
|
|
607
|
+
headers,
|
|
608
|
+
body,
|
|
609
|
+
trigger,
|
|
610
|
+
messageId,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const runUpdateMessageJob = (
|
|
615
|
+
job: (options: {
|
|
616
|
+
state: StreamingUIMessageState<UI_MESSAGE>;
|
|
617
|
+
write: () => void;
|
|
618
|
+
}) => Promise<void>,
|
|
619
|
+
) =>
|
|
620
|
+
// serialize the job execution to avoid race conditions:
|
|
621
|
+
this.jobExecutor.run(() =>
|
|
622
|
+
job({
|
|
623
|
+
state: activeResponse.state,
|
|
624
|
+
write: () => {
|
|
625
|
+
// streaming is set on first write (before it should be "submitted")
|
|
626
|
+
this.setStatus({ status: 'streaming' });
|
|
627
|
+
|
|
628
|
+
const replaceLastMessage =
|
|
629
|
+
activeResponse.state.message.id === this.lastMessage?.id;
|
|
630
|
+
|
|
631
|
+
if (replaceLastMessage) {
|
|
632
|
+
this.state.replaceMessage(
|
|
633
|
+
this.state.messages.length - 1,
|
|
634
|
+
activeResponse.state.message,
|
|
635
|
+
);
|
|
636
|
+
} else {
|
|
637
|
+
this.state.pushMessage(activeResponse.state.message);
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
}),
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
await consumeStream({
|
|
644
|
+
stream: processUIMessageStream({
|
|
645
|
+
stream,
|
|
646
|
+
onToolCall: this.onToolCall,
|
|
647
|
+
onData: this.onData,
|
|
648
|
+
messageMetadataSchema: this.messageMetadataSchema,
|
|
649
|
+
dataPartSchemas: this.dataPartSchemas,
|
|
650
|
+
runUpdateMessageJob,
|
|
651
|
+
onError: error => {
|
|
652
|
+
throw error;
|
|
653
|
+
},
|
|
654
|
+
}),
|
|
655
|
+
onError: error => {
|
|
656
|
+
throw error;
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
this.setStatus({ status: 'ready' });
|
|
661
|
+
} catch (err) {
|
|
662
|
+
// Ignore abort errors as they are expected.
|
|
663
|
+
if (isAbort || (err as any).name === 'AbortError') {
|
|
664
|
+
isAbort = true;
|
|
665
|
+
this.setStatus({ status: 'ready' });
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
isError = true;
|
|
670
|
+
|
|
671
|
+
// Network errors such as disconnected, timeout, etc.
|
|
672
|
+
if (
|
|
673
|
+
err instanceof TypeError &&
|
|
674
|
+
(err.message.toLowerCase().includes('fetch') ||
|
|
675
|
+
err.message.toLowerCase().includes('network'))
|
|
676
|
+
) {
|
|
677
|
+
isDisconnect = true;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (this.onError && err instanceof Error) {
|
|
681
|
+
this.onError(err);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
this.setStatus({ status: 'error', error: err as Error });
|
|
685
|
+
} finally {
|
|
686
|
+
try {
|
|
687
|
+
this.onFinish?.({
|
|
688
|
+
message: this.activeResponse!.state.message,
|
|
689
|
+
messages: this.state.messages,
|
|
690
|
+
isAbort,
|
|
691
|
+
isDisconnect,
|
|
692
|
+
isError,
|
|
693
|
+
finishReason: this.activeResponse?.state.finishReason,
|
|
694
|
+
});
|
|
695
|
+
} catch (err) {
|
|
696
|
+
console.error(err);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
this.activeResponse = undefined;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// automatically send the message if the sendAutomaticallyWhen function returns true
|
|
703
|
+
if (
|
|
704
|
+
this.sendAutomaticallyWhen?.({ messages: this.state.messages }) &&
|
|
705
|
+
!isError
|
|
706
|
+
) {
|
|
707
|
+
await this.makeRequest({
|
|
708
|
+
trigger: 'submit-message',
|
|
709
|
+
messageId: this.lastMessage?.id,
|
|
710
|
+
metadata,
|
|
711
|
+
headers,
|
|
712
|
+
body,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|