ai 6.0.33 → 6.0.35
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.d.mts +50 -21
- package/dist/index.d.ts +50 -21
- package/dist/index.js +348 -286
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +280 -219
- 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/docs/07-reference/05-ai-sdk-errors/ai-ui-message-stream-error.mdx +67 -0
- 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 +35 -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/ui-message-stream-error.ts +48 -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 +8294 -0
- package/src/ui/process-ui-message-stream.ts +761 -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
|
@@ -0,0 +1,2505 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getErrorMessage,
|
|
3
|
+
LanguageModelV3,
|
|
4
|
+
SharedV3Warning,
|
|
5
|
+
UnsupportedFunctionalityError,
|
|
6
|
+
} from '@ai-sdk/provider';
|
|
7
|
+
import {
|
|
8
|
+
createIdGenerator,
|
|
9
|
+
DelayedPromise,
|
|
10
|
+
IdGenerator,
|
|
11
|
+
isAbortError,
|
|
12
|
+
ProviderOptions,
|
|
13
|
+
ToolApprovalResponse,
|
|
14
|
+
ToolContent,
|
|
15
|
+
} from '@ai-sdk/provider-utils';
|
|
16
|
+
import { Span } from '@opentelemetry/api';
|
|
17
|
+
import { ServerResponse } from 'node:http';
|
|
18
|
+
import { NoOutputGeneratedError } from '../error';
|
|
19
|
+
import { logWarnings } from '../logger/log-warnings';
|
|
20
|
+
import { resolveLanguageModel } from '../model/resolve-model';
|
|
21
|
+
import {
|
|
22
|
+
CallSettings,
|
|
23
|
+
getChunkTimeoutMs,
|
|
24
|
+
getStepTimeoutMs,
|
|
25
|
+
getTotalTimeoutMs,
|
|
26
|
+
} from '../prompt/call-settings';
|
|
27
|
+
import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt';
|
|
28
|
+
import { createToolModelOutput } from '../prompt/create-tool-model-output';
|
|
29
|
+
import { prepareCallSettings } from '../prompt/prepare-call-settings';
|
|
30
|
+
import { prepareToolsAndToolChoice } from '../prompt/prepare-tools-and-tool-choice';
|
|
31
|
+
import { Prompt } from '../prompt/prompt';
|
|
32
|
+
import { standardizePrompt } from '../prompt/standardize-prompt';
|
|
33
|
+
import { wrapGatewayError } from '../prompt/wrap-gateway-error';
|
|
34
|
+
import { assembleOperationName } from '../telemetry/assemble-operation-name';
|
|
35
|
+
import { getBaseTelemetryAttributes } from '../telemetry/get-base-telemetry-attributes';
|
|
36
|
+
import { getTracer } from '../telemetry/get-tracer';
|
|
37
|
+
import { recordSpan } from '../telemetry/record-span';
|
|
38
|
+
import { selectTelemetryAttributes } from '../telemetry/select-telemetry-attributes';
|
|
39
|
+
import { stringifyForTelemetry } from '../telemetry/stringify-for-telemetry';
|
|
40
|
+
import { TelemetrySettings } from '../telemetry/telemetry-settings';
|
|
41
|
+
import { createTextStreamResponse } from '../text-stream/create-text-stream-response';
|
|
42
|
+
import { pipeTextStreamToResponse } from '../text-stream/pipe-text-stream-to-response';
|
|
43
|
+
import { LanguageModelRequestMetadata } from '../types';
|
|
44
|
+
import {
|
|
45
|
+
CallWarning,
|
|
46
|
+
FinishReason,
|
|
47
|
+
LanguageModel,
|
|
48
|
+
ToolChoice,
|
|
49
|
+
} from '../types/language-model';
|
|
50
|
+
import { ProviderMetadata } from '../types/provider-metadata';
|
|
51
|
+
import {
|
|
52
|
+
addLanguageModelUsage,
|
|
53
|
+
createNullLanguageModelUsage,
|
|
54
|
+
LanguageModelUsage,
|
|
55
|
+
} from '../types/usage';
|
|
56
|
+
import { UIMessage } from '../ui';
|
|
57
|
+
import { createUIMessageStreamResponse } from '../ui-message-stream/create-ui-message-stream-response';
|
|
58
|
+
import { getResponseUIMessageId } from '../ui-message-stream/get-response-ui-message-id';
|
|
59
|
+
import { handleUIMessageStreamFinish } from '../ui-message-stream/handle-ui-message-stream-finish';
|
|
60
|
+
import { pipeUIMessageStreamToResponse } from '../ui-message-stream/pipe-ui-message-stream-to-response';
|
|
61
|
+
import {
|
|
62
|
+
InferUIMessageChunk,
|
|
63
|
+
UIMessageChunk,
|
|
64
|
+
} from '../ui-message-stream/ui-message-chunks';
|
|
65
|
+
import { UIMessageStreamResponseInit } from '../ui-message-stream/ui-message-stream-response-init';
|
|
66
|
+
import { InferUIMessageData, InferUIMessageMetadata } from '../ui/ui-messages';
|
|
67
|
+
import { asArray } from '../util/as-array';
|
|
68
|
+
import {
|
|
69
|
+
AsyncIterableStream,
|
|
70
|
+
createAsyncIterableStream,
|
|
71
|
+
} from '../util/async-iterable-stream';
|
|
72
|
+
import { consumeStream } from '../util/consume-stream';
|
|
73
|
+
import { createStitchableStream } from '../util/create-stitchable-stream';
|
|
74
|
+
import { DownloadFunction } from '../util/download/download-function';
|
|
75
|
+
import { mergeAbortSignals } from '../util/merge-abort-signals';
|
|
76
|
+
import { mergeObjects } from '../util/merge-objects';
|
|
77
|
+
import { now as originalNow } from '../util/now';
|
|
78
|
+
import { prepareRetries } from '../util/prepare-retries';
|
|
79
|
+
import { collectToolApprovals } from './collect-tool-approvals';
|
|
80
|
+
import { ContentPart } from './content-part';
|
|
81
|
+
import { executeToolCall } from './execute-tool-call';
|
|
82
|
+
import { Output, text } from './output';
|
|
83
|
+
import {
|
|
84
|
+
InferCompleteOutput,
|
|
85
|
+
InferElementOutput,
|
|
86
|
+
InferPartialOutput,
|
|
87
|
+
} from './output-utils';
|
|
88
|
+
import { PrepareStepFunction } from './prepare-step';
|
|
89
|
+
import { ResponseMessage } from './response-message';
|
|
90
|
+
import {
|
|
91
|
+
runToolsTransformation,
|
|
92
|
+
SingleRequestTextStreamPart,
|
|
93
|
+
} from './run-tools-transformation';
|
|
94
|
+
import { DefaultStepResult, StepResult } from './step-result';
|
|
95
|
+
import {
|
|
96
|
+
isStopConditionMet,
|
|
97
|
+
stepCountIs,
|
|
98
|
+
StopCondition,
|
|
99
|
+
} from './stop-condition';
|
|
100
|
+
import {
|
|
101
|
+
ConsumeStreamOptions,
|
|
102
|
+
StreamTextResult,
|
|
103
|
+
TextStreamPart,
|
|
104
|
+
UIMessageStreamOptions,
|
|
105
|
+
} from './stream-text-result';
|
|
106
|
+
import { toResponseMessages } from './to-response-messages';
|
|
107
|
+
import { TypedToolCall } from './tool-call';
|
|
108
|
+
import { ToolCallRepairFunction } from './tool-call-repair-function';
|
|
109
|
+
import { ToolOutput } from './tool-output';
|
|
110
|
+
import { StaticToolOutputDenied } from './tool-output-denied';
|
|
111
|
+
import { ToolSet } from './tool-set';
|
|
112
|
+
|
|
113
|
+
const originalGenerateId = createIdGenerator({
|
|
114
|
+
prefix: 'aitxt',
|
|
115
|
+
size: 24,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
A transformation that is applied to the stream.
|
|
120
|
+
|
|
121
|
+
@param stopStream - A function that stops the source stream.
|
|
122
|
+
@param tools - The tools that are accessible to and can be called by the model. The model needs to support calling tools.
|
|
123
|
+
*/
|
|
124
|
+
export type StreamTextTransform<TOOLS extends ToolSet> = (options: {
|
|
125
|
+
tools: TOOLS; // for type inference
|
|
126
|
+
stopStream: () => void;
|
|
127
|
+
}) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
Callback that is set using the `onError` option.
|
|
131
|
+
|
|
132
|
+
@param event - The event that is passed to the callback.
|
|
133
|
+
*/
|
|
134
|
+
export type StreamTextOnErrorCallback = (event: {
|
|
135
|
+
error: unknown;
|
|
136
|
+
}) => PromiseLike<void> | void;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
Callback that is set using the `onStepFinish` option.
|
|
140
|
+
|
|
141
|
+
@param stepResult - The result of the step.
|
|
142
|
+
*/
|
|
143
|
+
export type StreamTextOnStepFinishCallback<TOOLS extends ToolSet> = (
|
|
144
|
+
stepResult: StepResult<TOOLS>,
|
|
145
|
+
) => PromiseLike<void> | void;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
Callback that is set using the `onChunk` option.
|
|
149
|
+
|
|
150
|
+
@param event - The event that is passed to the callback.
|
|
151
|
+
*/
|
|
152
|
+
export type StreamTextOnChunkCallback<TOOLS extends ToolSet> = (event: {
|
|
153
|
+
chunk: Extract<
|
|
154
|
+
TextStreamPart<TOOLS>,
|
|
155
|
+
{
|
|
156
|
+
type:
|
|
157
|
+
| 'text-delta'
|
|
158
|
+
| 'reasoning-delta'
|
|
159
|
+
| 'source'
|
|
160
|
+
| 'tool-call'
|
|
161
|
+
| 'tool-input-start'
|
|
162
|
+
| 'tool-input-delta'
|
|
163
|
+
| 'tool-result'
|
|
164
|
+
| 'raw';
|
|
165
|
+
}
|
|
166
|
+
>;
|
|
167
|
+
}) => PromiseLike<void> | void;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
Callback that is set using the `onFinish` option.
|
|
171
|
+
|
|
172
|
+
@param event - The event that is passed to the callback.
|
|
173
|
+
*/
|
|
174
|
+
export type StreamTextOnFinishCallback<TOOLS extends ToolSet> = (
|
|
175
|
+
event: StepResult<TOOLS> & {
|
|
176
|
+
/**
|
|
177
|
+
* Details for all steps.
|
|
178
|
+
*/
|
|
179
|
+
readonly steps: StepResult<TOOLS>[];
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Total usage for all steps. This is the sum of the usage of all steps.
|
|
183
|
+
*/
|
|
184
|
+
readonly totalUsage: LanguageModelUsage;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Context that is passed into tool execution.
|
|
188
|
+
*
|
|
189
|
+
* Experimental (can break in patch releases).
|
|
190
|
+
*
|
|
191
|
+
* @default undefined
|
|
192
|
+
*/
|
|
193
|
+
experimental_context: unknown;
|
|
194
|
+
},
|
|
195
|
+
) => PromiseLike<void> | void;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
Callback that is set using the `onAbort` option.
|
|
199
|
+
|
|
200
|
+
@param event - The event that is passed to the callback.
|
|
201
|
+
*/
|
|
202
|
+
export type StreamTextOnAbortCallback<TOOLS extends ToolSet> = (event: {
|
|
203
|
+
/**
|
|
204
|
+
Details for all previously finished steps.
|
|
205
|
+
*/
|
|
206
|
+
readonly steps: StepResult<TOOLS>[];
|
|
207
|
+
}) => PromiseLike<void> | void;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
Generate a text and call tools for a given prompt using a language model.
|
|
211
|
+
|
|
212
|
+
This function streams the output. If you do not want to stream the output, use `generateText` instead.
|
|
213
|
+
|
|
214
|
+
@param model - The language model to use.
|
|
215
|
+
@param tools - Tools that are accessible to and can be called by the model. The model needs to support calling tools.
|
|
216
|
+
|
|
217
|
+
@param system - A system message that will be part of the prompt.
|
|
218
|
+
@param prompt - A simple text prompt. You can either use `prompt` or `messages` but not both.
|
|
219
|
+
@param messages - A list of messages. You can either use `prompt` or `messages` but not both.
|
|
220
|
+
|
|
221
|
+
@param maxOutputTokens - Maximum number of tokens to generate.
|
|
222
|
+
@param temperature - Temperature setting.
|
|
223
|
+
The value is passed through to the provider. The range depends on the provider and model.
|
|
224
|
+
It is recommended to set either `temperature` or `topP`, but not both.
|
|
225
|
+
@param topP - Nucleus sampling.
|
|
226
|
+
The value is passed through to the provider. The range depends on the provider and model.
|
|
227
|
+
It is recommended to set either `temperature` or `topP`, but not both.
|
|
228
|
+
@param topK - Only sample from the top K options for each subsequent token.
|
|
229
|
+
Used to remove "long tail" low probability responses.
|
|
230
|
+
Recommended for advanced use cases only. You usually only need to use temperature.
|
|
231
|
+
@param presencePenalty - Presence penalty setting.
|
|
232
|
+
It affects the likelihood of the model to repeat information that is already in the prompt.
|
|
233
|
+
The value is passed through to the provider. The range depends on the provider and model.
|
|
234
|
+
@param frequencyPenalty - Frequency penalty setting.
|
|
235
|
+
It affects the likelihood of the model to repeatedly use the same words or phrases.
|
|
236
|
+
The value is passed through to the provider. The range depends on the provider and model.
|
|
237
|
+
@param stopSequences - Stop sequences.
|
|
238
|
+
If set, the model will stop generating text when one of the stop sequences is generated.
|
|
239
|
+
@param seed - The seed (integer) to use for random sampling.
|
|
240
|
+
If set and supported by the model, calls will generate deterministic results.
|
|
241
|
+
|
|
242
|
+
@param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
|
|
243
|
+
@param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
244
|
+
@param timeout - An optional timeout in milliseconds. The call will be aborted if it takes longer than the specified timeout.
|
|
245
|
+
@param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
246
|
+
|
|
247
|
+
@param onChunk - Callback that is called for each chunk of the stream. The stream processing will pause until the callback promise is resolved.
|
|
248
|
+
@param onError - Callback that is called when an error occurs during streaming. You can use it to log errors.
|
|
249
|
+
@param onStepFinish - Callback that is called when each step (LLM call) is finished, including intermediate steps.
|
|
250
|
+
@param onFinish - Callback that is called when all steps are finished and the response is complete.
|
|
251
|
+
|
|
252
|
+
@return
|
|
253
|
+
A result object for accessing different stream types and additional information.
|
|
254
|
+
*/
|
|
255
|
+
export function streamText<
|
|
256
|
+
TOOLS extends ToolSet,
|
|
257
|
+
OUTPUT extends Output = Output<string, string, never>,
|
|
258
|
+
>({
|
|
259
|
+
model,
|
|
260
|
+
tools,
|
|
261
|
+
toolChoice,
|
|
262
|
+
system,
|
|
263
|
+
prompt,
|
|
264
|
+
messages,
|
|
265
|
+
maxRetries,
|
|
266
|
+
abortSignal,
|
|
267
|
+
timeout,
|
|
268
|
+
headers,
|
|
269
|
+
stopWhen = stepCountIs(1),
|
|
270
|
+
experimental_output,
|
|
271
|
+
output = experimental_output,
|
|
272
|
+
experimental_telemetry: telemetry,
|
|
273
|
+
prepareStep,
|
|
274
|
+
providerOptions,
|
|
275
|
+
experimental_activeTools,
|
|
276
|
+
activeTools = experimental_activeTools,
|
|
277
|
+
experimental_repairToolCall: repairToolCall,
|
|
278
|
+
experimental_transform: transform,
|
|
279
|
+
experimental_download: download,
|
|
280
|
+
includeRawChunks = false,
|
|
281
|
+
onChunk,
|
|
282
|
+
onError = ({ error }) => {
|
|
283
|
+
console.error(error);
|
|
284
|
+
},
|
|
285
|
+
onFinish,
|
|
286
|
+
onAbort,
|
|
287
|
+
onStepFinish,
|
|
288
|
+
experimental_context,
|
|
289
|
+
_internal: { now = originalNow, generateId = originalGenerateId } = {},
|
|
290
|
+
...settings
|
|
291
|
+
}: CallSettings &
|
|
292
|
+
Prompt & {
|
|
293
|
+
/**
|
|
294
|
+
The language model to use.
|
|
295
|
+
*/
|
|
296
|
+
model: LanguageModel;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
The tools that the model can call. The model needs to support calling tools.
|
|
300
|
+
*/
|
|
301
|
+
tools?: TOOLS;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
The tool choice strategy. Default: 'auto'.
|
|
305
|
+
*/
|
|
306
|
+
toolChoice?: ToolChoice<TOOLS>;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
Condition for stopping the generation when there are tool results in the last step.
|
|
310
|
+
When the condition is an array, any of the conditions can be met to stop the generation.
|
|
311
|
+
|
|
312
|
+
@default stepCountIs(1)
|
|
313
|
+
*/
|
|
314
|
+
stopWhen?:
|
|
315
|
+
| StopCondition<NoInfer<TOOLS>>
|
|
316
|
+
| Array<StopCondition<NoInfer<TOOLS>>>;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
Optional telemetry configuration (experimental).
|
|
320
|
+
*/
|
|
321
|
+
experimental_telemetry?: TelemetrySettings;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
Additional provider-specific options. They are passed through
|
|
325
|
+
to the provider from the AI SDK and enable provider-specific
|
|
326
|
+
functionality that can be fully encapsulated in the provider.
|
|
327
|
+
*/
|
|
328
|
+
providerOptions?: ProviderOptions;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @deprecated Use `activeTools` instead.
|
|
332
|
+
*/
|
|
333
|
+
experimental_activeTools?: Array<keyof NoInfer<TOOLS>>;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
Limits the tools that are available for the model to call without
|
|
337
|
+
changing the tool call and result types in the result.
|
|
338
|
+
*/
|
|
339
|
+
activeTools?: Array<keyof NoInfer<TOOLS>>;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
Optional specification for parsing structured outputs from the LLM response.
|
|
343
|
+
*/
|
|
344
|
+
output?: OUTPUT;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
Optional specification for parsing structured outputs from the LLM response.
|
|
348
|
+
|
|
349
|
+
@deprecated Use `output` instead.
|
|
350
|
+
*/
|
|
351
|
+
experimental_output?: OUTPUT;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
Optional function that you can use to provide different settings for a step.
|
|
355
|
+
|
|
356
|
+
@param options - The options for the step.
|
|
357
|
+
@param options.steps - The steps that have been executed so far.
|
|
358
|
+
@param options.stepNumber - The number of the step that is being executed.
|
|
359
|
+
@param options.model - The model that is being used.
|
|
360
|
+
|
|
361
|
+
@returns An object that contains the settings for the step.
|
|
362
|
+
If you return undefined (or for undefined settings), the settings from the outer level will be used.
|
|
363
|
+
*/
|
|
364
|
+
prepareStep?: PrepareStepFunction<NoInfer<TOOLS>>;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
A function that attempts to repair a tool call that failed to parse.
|
|
368
|
+
*/
|
|
369
|
+
experimental_repairToolCall?: ToolCallRepairFunction<TOOLS>;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
Optional stream transformations.
|
|
373
|
+
They are applied in the order they are provided.
|
|
374
|
+
The stream transformations must maintain the stream structure for streamText to work correctly.
|
|
375
|
+
*/
|
|
376
|
+
experimental_transform?:
|
|
377
|
+
| StreamTextTransform<TOOLS>
|
|
378
|
+
| Array<StreamTextTransform<TOOLS>>;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
Custom download function to use for URLs.
|
|
382
|
+
|
|
383
|
+
By default, files are downloaded if the model does not support the URL for the given media type.
|
|
384
|
+
*/
|
|
385
|
+
experimental_download?: DownloadFunction | undefined;
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
Whether to include raw chunks from the provider in the stream.
|
|
389
|
+
When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
|
|
390
|
+
This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
|
|
391
|
+
Defaults to false.
|
|
392
|
+
*/
|
|
393
|
+
includeRawChunks?: boolean;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
Callback that is called for each chunk of the stream.
|
|
397
|
+
The stream processing will pause until the callback promise is resolved.
|
|
398
|
+
*/
|
|
399
|
+
onChunk?: StreamTextOnChunkCallback<TOOLS>;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
Callback that is invoked when an error occurs during streaming.
|
|
403
|
+
You can use it to log errors.
|
|
404
|
+
The stream processing will pause until the callback promise is resolved.
|
|
405
|
+
*/
|
|
406
|
+
onError?: StreamTextOnErrorCallback;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
Callback that is called when the LLM response and all request tool executions
|
|
410
|
+
(for tools that have an `execute` function) are finished.
|
|
411
|
+
|
|
412
|
+
The usage is the combined usage of all steps.
|
|
413
|
+
*/
|
|
414
|
+
onFinish?: StreamTextOnFinishCallback<TOOLS>;
|
|
415
|
+
|
|
416
|
+
onAbort?: StreamTextOnAbortCallback<TOOLS>;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
Callback that is called when each step (LLM call) is finished, including intermediate steps.
|
|
420
|
+
*/
|
|
421
|
+
onStepFinish?: StreamTextOnStepFinishCallback<TOOLS>;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Context that is passed into tool execution.
|
|
425
|
+
*
|
|
426
|
+
* Experimental (can break in patch releases).
|
|
427
|
+
*
|
|
428
|
+
* @default undefined
|
|
429
|
+
*/
|
|
430
|
+
experimental_context?: unknown;
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
Internal. For test use only. May change without notice.
|
|
434
|
+
*/
|
|
435
|
+
_internal?: {
|
|
436
|
+
now?: () => number;
|
|
437
|
+
generateId?: IdGenerator;
|
|
438
|
+
};
|
|
439
|
+
}): StreamTextResult<TOOLS, OUTPUT> {
|
|
440
|
+
const totalTimeoutMs = getTotalTimeoutMs(timeout);
|
|
441
|
+
const stepTimeoutMs = getStepTimeoutMs(timeout);
|
|
442
|
+
const chunkTimeoutMs = getChunkTimeoutMs(timeout);
|
|
443
|
+
const stepAbortController =
|
|
444
|
+
stepTimeoutMs != null ? new AbortController() : undefined;
|
|
445
|
+
const chunkAbortController =
|
|
446
|
+
chunkTimeoutMs != null ? new AbortController() : undefined;
|
|
447
|
+
return new DefaultStreamTextResult<TOOLS, OUTPUT>({
|
|
448
|
+
model: resolveLanguageModel(model),
|
|
449
|
+
telemetry,
|
|
450
|
+
headers,
|
|
451
|
+
settings,
|
|
452
|
+
maxRetries,
|
|
453
|
+
abortSignal: mergeAbortSignals(
|
|
454
|
+
abortSignal,
|
|
455
|
+
totalTimeoutMs != null ? AbortSignal.timeout(totalTimeoutMs) : undefined,
|
|
456
|
+
stepAbortController?.signal,
|
|
457
|
+
chunkAbortController?.signal,
|
|
458
|
+
),
|
|
459
|
+
stepTimeoutMs,
|
|
460
|
+
stepAbortController,
|
|
461
|
+
chunkTimeoutMs,
|
|
462
|
+
chunkAbortController,
|
|
463
|
+
system,
|
|
464
|
+
prompt,
|
|
465
|
+
messages,
|
|
466
|
+
tools,
|
|
467
|
+
toolChoice,
|
|
468
|
+
transforms: asArray(transform),
|
|
469
|
+
activeTools,
|
|
470
|
+
repairToolCall,
|
|
471
|
+
stopConditions: asArray(stopWhen),
|
|
472
|
+
output,
|
|
473
|
+
providerOptions,
|
|
474
|
+
prepareStep,
|
|
475
|
+
includeRawChunks,
|
|
476
|
+
onChunk,
|
|
477
|
+
onError,
|
|
478
|
+
onFinish,
|
|
479
|
+
onAbort,
|
|
480
|
+
onStepFinish,
|
|
481
|
+
now,
|
|
482
|
+
generateId,
|
|
483
|
+
experimental_context,
|
|
484
|
+
download,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export type EnrichedStreamPart<TOOLS extends ToolSet, PARTIAL_OUTPUT> = {
|
|
489
|
+
part: TextStreamPart<TOOLS>;
|
|
490
|
+
partialOutput: PARTIAL_OUTPUT | undefined;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
function createOutputTransformStream<
|
|
494
|
+
TOOLS extends ToolSet,
|
|
495
|
+
OUTPUT extends Output,
|
|
496
|
+
>(
|
|
497
|
+
output: OUTPUT,
|
|
498
|
+
): TransformStream<
|
|
499
|
+
TextStreamPart<TOOLS>,
|
|
500
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
501
|
+
> {
|
|
502
|
+
let firstTextChunkId: string | undefined = undefined;
|
|
503
|
+
let text = '';
|
|
504
|
+
let textChunk = '';
|
|
505
|
+
let textProviderMetadata: ProviderMetadata | undefined = undefined;
|
|
506
|
+
let lastPublishedJson = '';
|
|
507
|
+
|
|
508
|
+
function publishTextChunk({
|
|
509
|
+
controller,
|
|
510
|
+
partialOutput = undefined,
|
|
511
|
+
}: {
|
|
512
|
+
controller: TransformStreamDefaultController<
|
|
513
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
514
|
+
>;
|
|
515
|
+
partialOutput?: InferPartialOutput<OUTPUT>;
|
|
516
|
+
}) {
|
|
517
|
+
controller.enqueue({
|
|
518
|
+
part: {
|
|
519
|
+
type: 'text-delta',
|
|
520
|
+
id: firstTextChunkId!,
|
|
521
|
+
text: textChunk,
|
|
522
|
+
providerMetadata: textProviderMetadata,
|
|
523
|
+
},
|
|
524
|
+
partialOutput,
|
|
525
|
+
});
|
|
526
|
+
textChunk = '';
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return new TransformStream<
|
|
530
|
+
TextStreamPart<TOOLS>,
|
|
531
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
532
|
+
>({
|
|
533
|
+
async transform(chunk, controller) {
|
|
534
|
+
// ensure that we publish the last text chunk before the step finish:
|
|
535
|
+
if (chunk.type === 'finish-step' && textChunk.length > 0) {
|
|
536
|
+
publishTextChunk({ controller });
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (
|
|
540
|
+
chunk.type !== 'text-delta' &&
|
|
541
|
+
chunk.type !== 'text-start' &&
|
|
542
|
+
chunk.type !== 'text-end'
|
|
543
|
+
) {
|
|
544
|
+
controller.enqueue({ part: chunk, partialOutput: undefined });
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// we have to pick a text chunk which contains the json text
|
|
549
|
+
// since we are streaming, we have to pick the first text chunk
|
|
550
|
+
if (firstTextChunkId == null) {
|
|
551
|
+
firstTextChunkId = chunk.id;
|
|
552
|
+
} else if (chunk.id !== firstTextChunkId) {
|
|
553
|
+
controller.enqueue({ part: chunk, partialOutput: undefined });
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (chunk.type === 'text-start') {
|
|
558
|
+
controller.enqueue({ part: chunk, partialOutput: undefined });
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (chunk.type === 'text-end') {
|
|
563
|
+
if (textChunk.length > 0) {
|
|
564
|
+
publishTextChunk({ controller });
|
|
565
|
+
}
|
|
566
|
+
controller.enqueue({ part: chunk, partialOutput: undefined });
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
text += chunk.text;
|
|
571
|
+
textChunk += chunk.text;
|
|
572
|
+
textProviderMetadata = chunk.providerMetadata ?? textProviderMetadata;
|
|
573
|
+
|
|
574
|
+
// only publish if partial json can be parsed:
|
|
575
|
+
const result = await output.parsePartialOutput({ text });
|
|
576
|
+
|
|
577
|
+
// null should be allowed (valid JSON value) but undefined should not:
|
|
578
|
+
if (result !== undefined) {
|
|
579
|
+
// only send new json if it has changed:
|
|
580
|
+
const currentJson = JSON.stringify(result.partial);
|
|
581
|
+
if (currentJson !== lastPublishedJson) {
|
|
582
|
+
publishTextChunk({ controller, partialOutput: result.partial });
|
|
583
|
+
lastPublishedJson = currentJson;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
class DefaultStreamTextResult<TOOLS extends ToolSet, OUTPUT extends Output>
|
|
591
|
+
implements StreamTextResult<TOOLS, OUTPUT>
|
|
592
|
+
{
|
|
593
|
+
private readonly _totalUsage = new DelayedPromise<
|
|
594
|
+
Awaited<StreamTextResult<TOOLS, OUTPUT>['usage']>
|
|
595
|
+
>();
|
|
596
|
+
private readonly _finishReason = new DelayedPromise<
|
|
597
|
+
Awaited<StreamTextResult<TOOLS, OUTPUT>['finishReason']>
|
|
598
|
+
>();
|
|
599
|
+
private readonly _rawFinishReason = new DelayedPromise<
|
|
600
|
+
Awaited<StreamTextResult<TOOLS, OUTPUT>['rawFinishReason']>
|
|
601
|
+
>();
|
|
602
|
+
private readonly _steps = new DelayedPromise<
|
|
603
|
+
Awaited<StreamTextResult<TOOLS, OUTPUT>['steps']>
|
|
604
|
+
>();
|
|
605
|
+
|
|
606
|
+
private readonly addStream: (
|
|
607
|
+
stream: ReadableStream<TextStreamPart<TOOLS>>,
|
|
608
|
+
) => void;
|
|
609
|
+
|
|
610
|
+
private readonly closeStream: () => void;
|
|
611
|
+
|
|
612
|
+
private baseStream: ReadableStream<
|
|
613
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
614
|
+
>;
|
|
615
|
+
|
|
616
|
+
private outputSpecification: OUTPUT | undefined;
|
|
617
|
+
|
|
618
|
+
private includeRawChunks: boolean;
|
|
619
|
+
|
|
620
|
+
private tools: TOOLS | undefined;
|
|
621
|
+
|
|
622
|
+
constructor({
|
|
623
|
+
model,
|
|
624
|
+
telemetry,
|
|
625
|
+
headers,
|
|
626
|
+
settings,
|
|
627
|
+
maxRetries: maxRetriesArg,
|
|
628
|
+
abortSignal,
|
|
629
|
+
stepTimeoutMs,
|
|
630
|
+
stepAbortController,
|
|
631
|
+
chunkTimeoutMs,
|
|
632
|
+
chunkAbortController,
|
|
633
|
+
system,
|
|
634
|
+
prompt,
|
|
635
|
+
messages,
|
|
636
|
+
tools,
|
|
637
|
+
toolChoice,
|
|
638
|
+
transforms,
|
|
639
|
+
activeTools,
|
|
640
|
+
repairToolCall,
|
|
641
|
+
stopConditions,
|
|
642
|
+
output,
|
|
643
|
+
providerOptions,
|
|
644
|
+
prepareStep,
|
|
645
|
+
includeRawChunks,
|
|
646
|
+
now,
|
|
647
|
+
generateId,
|
|
648
|
+
onChunk,
|
|
649
|
+
onError,
|
|
650
|
+
onFinish,
|
|
651
|
+
onAbort,
|
|
652
|
+
onStepFinish,
|
|
653
|
+
experimental_context,
|
|
654
|
+
download,
|
|
655
|
+
}: {
|
|
656
|
+
model: LanguageModelV3;
|
|
657
|
+
telemetry: TelemetrySettings | undefined;
|
|
658
|
+
headers: Record<string, string | undefined> | undefined;
|
|
659
|
+
settings: Omit<CallSettings, 'abortSignal' | 'headers'>;
|
|
660
|
+
maxRetries: number | undefined;
|
|
661
|
+
abortSignal: AbortSignal | undefined;
|
|
662
|
+
stepTimeoutMs: number | undefined;
|
|
663
|
+
stepAbortController: AbortController | undefined;
|
|
664
|
+
chunkTimeoutMs: number | undefined;
|
|
665
|
+
chunkAbortController: AbortController | undefined;
|
|
666
|
+
system: Prompt['system'];
|
|
667
|
+
prompt: Prompt['prompt'];
|
|
668
|
+
messages: Prompt['messages'];
|
|
669
|
+
tools: TOOLS | undefined;
|
|
670
|
+
toolChoice: ToolChoice<TOOLS> | undefined;
|
|
671
|
+
transforms: Array<StreamTextTransform<TOOLS>>;
|
|
672
|
+
activeTools: Array<keyof TOOLS> | undefined;
|
|
673
|
+
repairToolCall: ToolCallRepairFunction<TOOLS> | undefined;
|
|
674
|
+
stopConditions: Array<StopCondition<NoInfer<TOOLS>>>;
|
|
675
|
+
output: OUTPUT | undefined;
|
|
676
|
+
providerOptions: ProviderOptions | undefined;
|
|
677
|
+
prepareStep: PrepareStepFunction<NoInfer<TOOLS>> | undefined;
|
|
678
|
+
includeRawChunks: boolean;
|
|
679
|
+
now: () => number;
|
|
680
|
+
generateId: () => string;
|
|
681
|
+
experimental_context: unknown;
|
|
682
|
+
download: DownloadFunction | undefined;
|
|
683
|
+
|
|
684
|
+
// callbacks:
|
|
685
|
+
onChunk: undefined | StreamTextOnChunkCallback<TOOLS>;
|
|
686
|
+
onError: StreamTextOnErrorCallback;
|
|
687
|
+
onFinish: undefined | StreamTextOnFinishCallback<TOOLS>;
|
|
688
|
+
onAbort: undefined | StreamTextOnAbortCallback<TOOLS>;
|
|
689
|
+
onStepFinish: undefined | StreamTextOnStepFinishCallback<TOOLS>;
|
|
690
|
+
}) {
|
|
691
|
+
this.outputSpecification = output;
|
|
692
|
+
this.includeRawChunks = includeRawChunks;
|
|
693
|
+
this.tools = tools;
|
|
694
|
+
|
|
695
|
+
// promise to ensure that the step has been fully processed by the event processor
|
|
696
|
+
// before a new step is started. This is required because the continuation condition
|
|
697
|
+
// needs the updated steps to determine if another step is needed.
|
|
698
|
+
let stepFinish!: DelayedPromise<void>;
|
|
699
|
+
|
|
700
|
+
let recordedContent: Array<ContentPart<TOOLS>> = [];
|
|
701
|
+
const recordedResponseMessages: Array<ResponseMessage> = [];
|
|
702
|
+
let recordedFinishReason: FinishReason | undefined = undefined;
|
|
703
|
+
let recordedRawFinishReason: string | undefined = undefined;
|
|
704
|
+
let recordedTotalUsage: LanguageModelUsage | undefined = undefined;
|
|
705
|
+
let recordedRequest: LanguageModelRequestMetadata = {};
|
|
706
|
+
let recordedWarnings: Array<CallWarning> = [];
|
|
707
|
+
const recordedSteps: StepResult<TOOLS>[] = [];
|
|
708
|
+
|
|
709
|
+
// Track provider-executed tool calls that support deferred results
|
|
710
|
+
// (e.g., code_execution in programmatic tool calling scenarios).
|
|
711
|
+
// These tools may not return their results in the same turn as their call.
|
|
712
|
+
const pendingDeferredToolCalls = new Map<string, { toolName: string }>();
|
|
713
|
+
|
|
714
|
+
let rootSpan!: Span;
|
|
715
|
+
|
|
716
|
+
let activeTextContent: Record<
|
|
717
|
+
string,
|
|
718
|
+
{
|
|
719
|
+
type: 'text';
|
|
720
|
+
text: string;
|
|
721
|
+
providerMetadata: ProviderMetadata | undefined;
|
|
722
|
+
}
|
|
723
|
+
> = {};
|
|
724
|
+
|
|
725
|
+
let activeReasoningContent: Record<
|
|
726
|
+
string,
|
|
727
|
+
{
|
|
728
|
+
type: 'reasoning';
|
|
729
|
+
text: string;
|
|
730
|
+
providerMetadata: ProviderMetadata | undefined;
|
|
731
|
+
}
|
|
732
|
+
> = {};
|
|
733
|
+
|
|
734
|
+
const eventProcessor = new TransformStream<
|
|
735
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
|
|
736
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
737
|
+
>({
|
|
738
|
+
async transform(chunk, controller) {
|
|
739
|
+
controller.enqueue(chunk); // forward the chunk to the next stream
|
|
740
|
+
|
|
741
|
+
const { part } = chunk;
|
|
742
|
+
|
|
743
|
+
if (
|
|
744
|
+
part.type === 'text-delta' ||
|
|
745
|
+
part.type === 'reasoning-delta' ||
|
|
746
|
+
part.type === 'source' ||
|
|
747
|
+
part.type === 'tool-call' ||
|
|
748
|
+
part.type === 'tool-result' ||
|
|
749
|
+
part.type === 'tool-input-start' ||
|
|
750
|
+
part.type === 'tool-input-delta' ||
|
|
751
|
+
part.type === 'raw'
|
|
752
|
+
) {
|
|
753
|
+
await onChunk?.({ chunk: part });
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (part.type === 'error') {
|
|
757
|
+
await onError({ error: wrapGatewayError(part.error) });
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (part.type === 'text-start') {
|
|
761
|
+
activeTextContent[part.id] = {
|
|
762
|
+
type: 'text',
|
|
763
|
+
text: '',
|
|
764
|
+
providerMetadata: part.providerMetadata,
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
recordedContent.push(activeTextContent[part.id]);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (part.type === 'text-delta') {
|
|
771
|
+
const activeText = activeTextContent[part.id];
|
|
772
|
+
|
|
773
|
+
if (activeText == null) {
|
|
774
|
+
controller.enqueue({
|
|
775
|
+
part: {
|
|
776
|
+
type: 'error',
|
|
777
|
+
error: `text part ${part.id} not found`,
|
|
778
|
+
},
|
|
779
|
+
partialOutput: undefined,
|
|
780
|
+
});
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
activeText.text += part.text;
|
|
785
|
+
activeText.providerMetadata =
|
|
786
|
+
part.providerMetadata ?? activeText.providerMetadata;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (part.type === 'text-end') {
|
|
790
|
+
const activeText = activeTextContent[part.id];
|
|
791
|
+
|
|
792
|
+
if (activeText == null) {
|
|
793
|
+
controller.enqueue({
|
|
794
|
+
part: {
|
|
795
|
+
type: 'error',
|
|
796
|
+
error: `text part ${part.id} not found`,
|
|
797
|
+
},
|
|
798
|
+
partialOutput: undefined,
|
|
799
|
+
});
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
activeText.providerMetadata =
|
|
804
|
+
part.providerMetadata ?? activeText.providerMetadata;
|
|
805
|
+
|
|
806
|
+
delete activeTextContent[part.id];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (part.type === 'reasoning-start') {
|
|
810
|
+
activeReasoningContent[part.id] = {
|
|
811
|
+
type: 'reasoning',
|
|
812
|
+
text: '',
|
|
813
|
+
providerMetadata: part.providerMetadata,
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
recordedContent.push(activeReasoningContent[part.id]);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (part.type === 'reasoning-delta') {
|
|
820
|
+
const activeReasoning = activeReasoningContent[part.id];
|
|
821
|
+
|
|
822
|
+
if (activeReasoning == null) {
|
|
823
|
+
controller.enqueue({
|
|
824
|
+
part: {
|
|
825
|
+
type: 'error',
|
|
826
|
+
error: `reasoning part ${part.id} not found`,
|
|
827
|
+
},
|
|
828
|
+
partialOutput: undefined,
|
|
829
|
+
});
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
activeReasoning.text += part.text;
|
|
834
|
+
activeReasoning.providerMetadata =
|
|
835
|
+
part.providerMetadata ?? activeReasoning.providerMetadata;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (part.type === 'reasoning-end') {
|
|
839
|
+
const activeReasoning = activeReasoningContent[part.id];
|
|
840
|
+
|
|
841
|
+
if (activeReasoning == null) {
|
|
842
|
+
controller.enqueue({
|
|
843
|
+
part: {
|
|
844
|
+
type: 'error',
|
|
845
|
+
error: `reasoning part ${part.id} not found`,
|
|
846
|
+
},
|
|
847
|
+
partialOutput: undefined,
|
|
848
|
+
});
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
activeReasoning.providerMetadata =
|
|
853
|
+
part.providerMetadata ?? activeReasoning.providerMetadata;
|
|
854
|
+
|
|
855
|
+
delete activeReasoningContent[part.id];
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (part.type === 'file') {
|
|
859
|
+
recordedContent.push({ type: 'file', file: part.file });
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (part.type === 'source') {
|
|
863
|
+
recordedContent.push(part);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (part.type === 'tool-call') {
|
|
867
|
+
recordedContent.push(part);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (part.type === 'tool-result' && !part.preliminary) {
|
|
871
|
+
recordedContent.push(part);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (part.type === 'tool-approval-request') {
|
|
875
|
+
recordedContent.push(part);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (part.type === 'tool-error') {
|
|
879
|
+
recordedContent.push(part);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (part.type === 'start-step') {
|
|
883
|
+
// reset the recorded data when a new step starts:
|
|
884
|
+
recordedContent = [];
|
|
885
|
+
activeReasoningContent = {};
|
|
886
|
+
activeTextContent = {};
|
|
887
|
+
|
|
888
|
+
recordedRequest = part.request;
|
|
889
|
+
recordedWarnings = part.warnings;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (part.type === 'finish-step') {
|
|
893
|
+
const stepMessages = await toResponseMessages({
|
|
894
|
+
content: recordedContent,
|
|
895
|
+
tools,
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
// Add step information (after response messages are updated):
|
|
899
|
+
const currentStepResult: StepResult<TOOLS> = new DefaultStepResult({
|
|
900
|
+
content: recordedContent,
|
|
901
|
+
finishReason: part.finishReason,
|
|
902
|
+
rawFinishReason: part.rawFinishReason,
|
|
903
|
+
usage: part.usage,
|
|
904
|
+
warnings: recordedWarnings,
|
|
905
|
+
request: recordedRequest,
|
|
906
|
+
response: {
|
|
907
|
+
...part.response,
|
|
908
|
+
messages: [...recordedResponseMessages, ...stepMessages],
|
|
909
|
+
},
|
|
910
|
+
providerMetadata: part.providerMetadata,
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
await onStepFinish?.(currentStepResult);
|
|
914
|
+
|
|
915
|
+
logWarnings({
|
|
916
|
+
warnings: recordedWarnings,
|
|
917
|
+
provider: model.provider,
|
|
918
|
+
model: model.modelId,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
recordedSteps.push(currentStepResult);
|
|
922
|
+
|
|
923
|
+
recordedResponseMessages.push(...stepMessages);
|
|
924
|
+
|
|
925
|
+
// resolve the promise to signal that the step has been fully processed
|
|
926
|
+
// by the event processor:
|
|
927
|
+
stepFinish.resolve();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (part.type === 'finish') {
|
|
931
|
+
recordedTotalUsage = part.totalUsage;
|
|
932
|
+
recordedFinishReason = part.finishReason;
|
|
933
|
+
recordedRawFinishReason = part.rawFinishReason;
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
|
|
937
|
+
async flush(controller) {
|
|
938
|
+
try {
|
|
939
|
+
if (recordedSteps.length === 0) {
|
|
940
|
+
const error = abortSignal?.aborted
|
|
941
|
+
? abortSignal.reason
|
|
942
|
+
: new NoOutputGeneratedError({
|
|
943
|
+
message: 'No output generated. Check the stream for errors.',
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
self._finishReason.reject(error);
|
|
947
|
+
self._rawFinishReason.reject(error);
|
|
948
|
+
self._totalUsage.reject(error);
|
|
949
|
+
self._steps.reject(error);
|
|
950
|
+
|
|
951
|
+
return; // no steps recorded (e.g. in error scenario)
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// derived:
|
|
955
|
+
const finishReason = recordedFinishReason ?? 'other';
|
|
956
|
+
const totalUsage =
|
|
957
|
+
recordedTotalUsage ?? createNullLanguageModelUsage();
|
|
958
|
+
|
|
959
|
+
// from finish:
|
|
960
|
+
self._finishReason.resolve(finishReason);
|
|
961
|
+
self._rawFinishReason.resolve(recordedRawFinishReason);
|
|
962
|
+
self._totalUsage.resolve(totalUsage);
|
|
963
|
+
|
|
964
|
+
// aggregate results:
|
|
965
|
+
self._steps.resolve(recordedSteps);
|
|
966
|
+
|
|
967
|
+
// call onFinish callback:
|
|
968
|
+
const finalStep = recordedSteps[recordedSteps.length - 1];
|
|
969
|
+
await onFinish?.({
|
|
970
|
+
finishReason: finalStep.finishReason,
|
|
971
|
+
rawFinishReason: finalStep.rawFinishReason,
|
|
972
|
+
totalUsage,
|
|
973
|
+
usage: finalStep.usage,
|
|
974
|
+
content: finalStep.content,
|
|
975
|
+
text: finalStep.text,
|
|
976
|
+
reasoningText: finalStep.reasoningText,
|
|
977
|
+
reasoning: finalStep.reasoning,
|
|
978
|
+
files: finalStep.files,
|
|
979
|
+
sources: finalStep.sources,
|
|
980
|
+
toolCalls: finalStep.toolCalls,
|
|
981
|
+
staticToolCalls: finalStep.staticToolCalls,
|
|
982
|
+
dynamicToolCalls: finalStep.dynamicToolCalls,
|
|
983
|
+
toolResults: finalStep.toolResults,
|
|
984
|
+
staticToolResults: finalStep.staticToolResults,
|
|
985
|
+
dynamicToolResults: finalStep.dynamicToolResults,
|
|
986
|
+
request: finalStep.request,
|
|
987
|
+
response: finalStep.response,
|
|
988
|
+
warnings: finalStep.warnings,
|
|
989
|
+
providerMetadata: finalStep.providerMetadata,
|
|
990
|
+
steps: recordedSteps,
|
|
991
|
+
experimental_context,
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
// Add response information to the root span:
|
|
995
|
+
rootSpan.setAttributes(
|
|
996
|
+
await selectTelemetryAttributes({
|
|
997
|
+
telemetry,
|
|
998
|
+
attributes: {
|
|
999
|
+
'ai.response.finishReason': finishReason,
|
|
1000
|
+
'ai.response.text': { output: () => finalStep.text },
|
|
1001
|
+
'ai.response.toolCalls': {
|
|
1002
|
+
output: () =>
|
|
1003
|
+
finalStep.toolCalls?.length
|
|
1004
|
+
? JSON.stringify(finalStep.toolCalls)
|
|
1005
|
+
: undefined,
|
|
1006
|
+
},
|
|
1007
|
+
'ai.response.providerMetadata': JSON.stringify(
|
|
1008
|
+
finalStep.providerMetadata,
|
|
1009
|
+
),
|
|
1010
|
+
|
|
1011
|
+
'ai.usage.inputTokens': totalUsage.inputTokens,
|
|
1012
|
+
'ai.usage.outputTokens': totalUsage.outputTokens,
|
|
1013
|
+
'ai.usage.totalTokens': totalUsage.totalTokens,
|
|
1014
|
+
'ai.usage.reasoningTokens': totalUsage.reasoningTokens,
|
|
1015
|
+
'ai.usage.cachedInputTokens': totalUsage.cachedInputTokens,
|
|
1016
|
+
},
|
|
1017
|
+
}),
|
|
1018
|
+
);
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
controller.error(error);
|
|
1021
|
+
} finally {
|
|
1022
|
+
rootSpan.end();
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// initialize the stitchable stream and the transformed stream:
|
|
1028
|
+
const stitchableStream = createStitchableStream<TextStreamPart<TOOLS>>();
|
|
1029
|
+
this.addStream = stitchableStream.addStream;
|
|
1030
|
+
this.closeStream = stitchableStream.close;
|
|
1031
|
+
|
|
1032
|
+
// resilient stream that handles abort signals and errors:
|
|
1033
|
+
const reader = stitchableStream.stream.getReader();
|
|
1034
|
+
let stream = new ReadableStream<TextStreamPart<TOOLS>>({
|
|
1035
|
+
async start(controller) {
|
|
1036
|
+
// send start event:
|
|
1037
|
+
controller.enqueue({ type: 'start' });
|
|
1038
|
+
},
|
|
1039
|
+
|
|
1040
|
+
async pull(controller) {
|
|
1041
|
+
// abort handling:
|
|
1042
|
+
function abort() {
|
|
1043
|
+
onAbort?.({ steps: recordedSteps });
|
|
1044
|
+
controller.enqueue({
|
|
1045
|
+
type: 'abort',
|
|
1046
|
+
// The `reason` is usually of type DOMException, but it can also be of any type,
|
|
1047
|
+
// so we use getErrorMessage for serialization because it is already designed to accept values of the unknown type.
|
|
1048
|
+
// See: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/reason
|
|
1049
|
+
...(abortSignal?.reason !== undefined
|
|
1050
|
+
? { reason: getErrorMessage(abortSignal.reason) }
|
|
1051
|
+
: {}),
|
|
1052
|
+
});
|
|
1053
|
+
controller.close();
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
try {
|
|
1057
|
+
const { done, value } = await reader.read();
|
|
1058
|
+
|
|
1059
|
+
if (done) {
|
|
1060
|
+
controller.close();
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (abortSignal?.aborted) {
|
|
1065
|
+
abort();
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
controller.enqueue(value);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
if (isAbortError(error) && abortSignal?.aborted) {
|
|
1072
|
+
abort();
|
|
1073
|
+
} else {
|
|
1074
|
+
controller.error(error);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
},
|
|
1078
|
+
|
|
1079
|
+
cancel(reason) {
|
|
1080
|
+
return stitchableStream.stream.cancel(reason);
|
|
1081
|
+
},
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
// transform the stream before output parsing
|
|
1085
|
+
// to enable replacement of stream segments:
|
|
1086
|
+
for (const transform of transforms) {
|
|
1087
|
+
stream = stream.pipeThrough(
|
|
1088
|
+
transform({
|
|
1089
|
+
tools: tools as TOOLS,
|
|
1090
|
+
stopStream() {
|
|
1091
|
+
stitchableStream.terminate();
|
|
1092
|
+
},
|
|
1093
|
+
}),
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
this.baseStream = stream
|
|
1098
|
+
.pipeThrough(createOutputTransformStream(output ?? text()))
|
|
1099
|
+
.pipeThrough(eventProcessor);
|
|
1100
|
+
|
|
1101
|
+
const { maxRetries, retry } = prepareRetries({
|
|
1102
|
+
maxRetries: maxRetriesArg,
|
|
1103
|
+
abortSignal,
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
const tracer = getTracer(telemetry);
|
|
1107
|
+
|
|
1108
|
+
const callSettings = prepareCallSettings(settings);
|
|
1109
|
+
|
|
1110
|
+
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
1111
|
+
model,
|
|
1112
|
+
telemetry,
|
|
1113
|
+
headers,
|
|
1114
|
+
settings: { ...callSettings, maxRetries },
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
const self = this;
|
|
1118
|
+
|
|
1119
|
+
recordSpan({
|
|
1120
|
+
name: 'ai.streamText',
|
|
1121
|
+
attributes: selectTelemetryAttributes({
|
|
1122
|
+
telemetry,
|
|
1123
|
+
attributes: {
|
|
1124
|
+
...assembleOperationName({ operationId: 'ai.streamText', telemetry }),
|
|
1125
|
+
...baseTelemetryAttributes,
|
|
1126
|
+
// specific settings that only make sense on the outer level:
|
|
1127
|
+
'ai.prompt': {
|
|
1128
|
+
input: () => JSON.stringify({ system, prompt, messages }),
|
|
1129
|
+
},
|
|
1130
|
+
},
|
|
1131
|
+
}),
|
|
1132
|
+
tracer,
|
|
1133
|
+
endWhenDone: false,
|
|
1134
|
+
fn: async rootSpanArg => {
|
|
1135
|
+
rootSpan = rootSpanArg;
|
|
1136
|
+
|
|
1137
|
+
const initialPrompt = await standardizePrompt({
|
|
1138
|
+
system,
|
|
1139
|
+
prompt,
|
|
1140
|
+
messages,
|
|
1141
|
+
} as Prompt);
|
|
1142
|
+
|
|
1143
|
+
const initialMessages = initialPrompt.messages;
|
|
1144
|
+
const initialResponseMessages: Array<ResponseMessage> = [];
|
|
1145
|
+
|
|
1146
|
+
const { approvedToolApprovals, deniedToolApprovals } =
|
|
1147
|
+
collectToolApprovals<TOOLS>({ messages: initialMessages });
|
|
1148
|
+
|
|
1149
|
+
// initial tool execution step stream
|
|
1150
|
+
if (
|
|
1151
|
+
deniedToolApprovals.length > 0 ||
|
|
1152
|
+
approvedToolApprovals.length > 0
|
|
1153
|
+
) {
|
|
1154
|
+
const providerExecutedToolApprovals = [
|
|
1155
|
+
...approvedToolApprovals,
|
|
1156
|
+
...deniedToolApprovals,
|
|
1157
|
+
].filter(toolApproval => toolApproval.toolCall.providerExecuted);
|
|
1158
|
+
|
|
1159
|
+
const localApprovedToolApprovals = approvedToolApprovals.filter(
|
|
1160
|
+
toolApproval => !toolApproval.toolCall.providerExecuted,
|
|
1161
|
+
);
|
|
1162
|
+
const localDeniedToolApprovals = deniedToolApprovals.filter(
|
|
1163
|
+
toolApproval => !toolApproval.toolCall.providerExecuted,
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
const deniedProviderExecutedToolApprovals =
|
|
1167
|
+
deniedToolApprovals.filter(
|
|
1168
|
+
toolApproval => toolApproval.toolCall.providerExecuted,
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
let toolExecutionStepStreamController:
|
|
1172
|
+
| ReadableStreamDefaultController<TextStreamPart<TOOLS>>
|
|
1173
|
+
| undefined;
|
|
1174
|
+
const toolExecutionStepStream = new ReadableStream<
|
|
1175
|
+
TextStreamPart<TOOLS>
|
|
1176
|
+
>({
|
|
1177
|
+
start(controller) {
|
|
1178
|
+
toolExecutionStepStreamController = controller;
|
|
1179
|
+
},
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
self.addStream(toolExecutionStepStream);
|
|
1183
|
+
|
|
1184
|
+
try {
|
|
1185
|
+
for (const toolApproval of [
|
|
1186
|
+
...localDeniedToolApprovals,
|
|
1187
|
+
...deniedProviderExecutedToolApprovals,
|
|
1188
|
+
]) {
|
|
1189
|
+
toolExecutionStepStreamController?.enqueue({
|
|
1190
|
+
type: 'tool-output-denied',
|
|
1191
|
+
toolCallId: toolApproval.toolCall.toolCallId,
|
|
1192
|
+
toolName: toolApproval.toolCall.toolName,
|
|
1193
|
+
} as StaticToolOutputDenied<TOOLS>);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const toolOutputs: Array<ToolOutput<TOOLS>> = [];
|
|
1197
|
+
|
|
1198
|
+
await Promise.all(
|
|
1199
|
+
localApprovedToolApprovals.map(async toolApproval => {
|
|
1200
|
+
const result = await executeToolCall({
|
|
1201
|
+
toolCall: toolApproval.toolCall,
|
|
1202
|
+
tools,
|
|
1203
|
+
tracer,
|
|
1204
|
+
telemetry,
|
|
1205
|
+
messages: initialMessages,
|
|
1206
|
+
abortSignal,
|
|
1207
|
+
experimental_context,
|
|
1208
|
+
onPreliminaryToolResult: result => {
|
|
1209
|
+
toolExecutionStepStreamController?.enqueue(result);
|
|
1210
|
+
},
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
if (result != null) {
|
|
1214
|
+
toolExecutionStepStreamController?.enqueue(result);
|
|
1215
|
+
toolOutputs.push(result);
|
|
1216
|
+
}
|
|
1217
|
+
}),
|
|
1218
|
+
);
|
|
1219
|
+
|
|
1220
|
+
// forward provider-executed approval responses to the provider (do not execute locally):
|
|
1221
|
+
if (providerExecutedToolApprovals.length > 0) {
|
|
1222
|
+
initialResponseMessages.push({
|
|
1223
|
+
role: 'tool',
|
|
1224
|
+
content: providerExecutedToolApprovals.map(
|
|
1225
|
+
toolApproval =>
|
|
1226
|
+
({
|
|
1227
|
+
type: 'tool-approval-response',
|
|
1228
|
+
approvalId: toolApproval.approvalResponse.approvalId,
|
|
1229
|
+
approved: toolApproval.approvalResponse.approved,
|
|
1230
|
+
reason: toolApproval.approvalResponse.reason,
|
|
1231
|
+
providerExecuted: true,
|
|
1232
|
+
}) satisfies ToolApprovalResponse,
|
|
1233
|
+
),
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// Local tool results (approved + denied) are sent as tool results:
|
|
1238
|
+
if (toolOutputs.length > 0 || localDeniedToolApprovals.length > 0) {
|
|
1239
|
+
const localToolContent: ToolContent = [];
|
|
1240
|
+
|
|
1241
|
+
// add regular tool results for approved tool calls:
|
|
1242
|
+
for (const output of toolOutputs) {
|
|
1243
|
+
localToolContent.push({
|
|
1244
|
+
type: 'tool-result' as const,
|
|
1245
|
+
toolCallId: output.toolCallId,
|
|
1246
|
+
toolName: output.toolName,
|
|
1247
|
+
output: await createToolModelOutput({
|
|
1248
|
+
toolCallId: output.toolCallId,
|
|
1249
|
+
input: output.input,
|
|
1250
|
+
tool: tools?.[output.toolName],
|
|
1251
|
+
output:
|
|
1252
|
+
output.type === 'tool-result'
|
|
1253
|
+
? output.output
|
|
1254
|
+
: output.error,
|
|
1255
|
+
errorMode: output.type === 'tool-error' ? 'json' : 'none',
|
|
1256
|
+
}),
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// add execution denied tool results for denied local tool approvals:
|
|
1261
|
+
for (const toolApproval of localDeniedToolApprovals) {
|
|
1262
|
+
localToolContent.push({
|
|
1263
|
+
type: 'tool-result' as const,
|
|
1264
|
+
toolCallId: toolApproval.toolCall.toolCallId,
|
|
1265
|
+
toolName: toolApproval.toolCall.toolName,
|
|
1266
|
+
output: {
|
|
1267
|
+
type: 'execution-denied' as const,
|
|
1268
|
+
reason: toolApproval.approvalResponse.reason,
|
|
1269
|
+
},
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
initialResponseMessages.push({
|
|
1274
|
+
role: 'tool',
|
|
1275
|
+
content: localToolContent,
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
} finally {
|
|
1279
|
+
toolExecutionStepStreamController?.close();
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
recordedResponseMessages.push(...initialResponseMessages);
|
|
1284
|
+
|
|
1285
|
+
async function streamStep({
|
|
1286
|
+
currentStep,
|
|
1287
|
+
responseMessages,
|
|
1288
|
+
usage,
|
|
1289
|
+
}: {
|
|
1290
|
+
currentStep: number;
|
|
1291
|
+
responseMessages: Array<ResponseMessage>;
|
|
1292
|
+
usage: LanguageModelUsage;
|
|
1293
|
+
}) {
|
|
1294
|
+
const includeRawChunks = self.includeRawChunks;
|
|
1295
|
+
|
|
1296
|
+
// Set up step timeout if configured
|
|
1297
|
+
const stepTimeoutId =
|
|
1298
|
+
stepTimeoutMs != null
|
|
1299
|
+
? setTimeout(() => stepAbortController!.abort(), stepTimeoutMs)
|
|
1300
|
+
: undefined;
|
|
1301
|
+
|
|
1302
|
+
// Set up chunk timeout tracking (will be reset on each chunk)
|
|
1303
|
+
let chunkTimeoutId: ReturnType<typeof setTimeout> | undefined =
|
|
1304
|
+
undefined;
|
|
1305
|
+
|
|
1306
|
+
function resetChunkTimeout() {
|
|
1307
|
+
if (chunkTimeoutMs != null) {
|
|
1308
|
+
if (chunkTimeoutId != null) {
|
|
1309
|
+
clearTimeout(chunkTimeoutId);
|
|
1310
|
+
}
|
|
1311
|
+
chunkTimeoutId = setTimeout(
|
|
1312
|
+
() => chunkAbortController!.abort(),
|
|
1313
|
+
chunkTimeoutMs,
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
function clearChunkTimeout() {
|
|
1319
|
+
if (chunkTimeoutId != null) {
|
|
1320
|
+
clearTimeout(chunkTimeoutId);
|
|
1321
|
+
chunkTimeoutId = undefined;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function clearStepTimeout() {
|
|
1326
|
+
if (stepTimeoutId != null) {
|
|
1327
|
+
clearTimeout(stepTimeoutId);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
stepFinish = new DelayedPromise<void>();
|
|
1332
|
+
|
|
1333
|
+
const stepInputMessages = [...initialMessages, ...responseMessages];
|
|
1334
|
+
|
|
1335
|
+
const prepareStepResult = await prepareStep?.({
|
|
1336
|
+
model,
|
|
1337
|
+
steps: recordedSteps,
|
|
1338
|
+
stepNumber: recordedSteps.length,
|
|
1339
|
+
messages: stepInputMessages,
|
|
1340
|
+
experimental_context,
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
const stepModel = resolveLanguageModel(
|
|
1344
|
+
prepareStepResult?.model ?? model,
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
const promptMessages = await convertToLanguageModelPrompt({
|
|
1348
|
+
prompt: {
|
|
1349
|
+
system: prepareStepResult?.system ?? initialPrompt.system,
|
|
1350
|
+
messages: prepareStepResult?.messages ?? stepInputMessages,
|
|
1351
|
+
},
|
|
1352
|
+
supportedUrls: await stepModel.supportedUrls,
|
|
1353
|
+
download,
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
const { toolChoice: stepToolChoice, tools: stepTools } =
|
|
1357
|
+
await prepareToolsAndToolChoice({
|
|
1358
|
+
tools,
|
|
1359
|
+
toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
|
|
1360
|
+
activeTools: prepareStepResult?.activeTools ?? activeTools,
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
experimental_context =
|
|
1364
|
+
prepareStepResult?.experimental_context ?? experimental_context;
|
|
1365
|
+
|
|
1366
|
+
const stepProviderOptions = mergeObjects(
|
|
1367
|
+
providerOptions,
|
|
1368
|
+
prepareStepResult?.providerOptions,
|
|
1369
|
+
);
|
|
1370
|
+
const {
|
|
1371
|
+
result: { stream, response, request },
|
|
1372
|
+
doStreamSpan,
|
|
1373
|
+
startTimestampMs,
|
|
1374
|
+
} = await retry(() =>
|
|
1375
|
+
recordSpan({
|
|
1376
|
+
name: 'ai.streamText.doStream',
|
|
1377
|
+
attributes: selectTelemetryAttributes({
|
|
1378
|
+
telemetry,
|
|
1379
|
+
attributes: {
|
|
1380
|
+
...assembleOperationName({
|
|
1381
|
+
operationId: 'ai.streamText.doStream',
|
|
1382
|
+
telemetry,
|
|
1383
|
+
}),
|
|
1384
|
+
...baseTelemetryAttributes,
|
|
1385
|
+
// model:
|
|
1386
|
+
'ai.model.provider': stepModel.provider,
|
|
1387
|
+
'ai.model.id': stepModel.modelId,
|
|
1388
|
+
// prompt:
|
|
1389
|
+
'ai.prompt.messages': {
|
|
1390
|
+
input: () => stringifyForTelemetry(promptMessages),
|
|
1391
|
+
},
|
|
1392
|
+
'ai.prompt.tools': {
|
|
1393
|
+
// convert the language model level tools:
|
|
1394
|
+
input: () => stepTools?.map(tool => JSON.stringify(tool)),
|
|
1395
|
+
},
|
|
1396
|
+
'ai.prompt.toolChoice': {
|
|
1397
|
+
input: () =>
|
|
1398
|
+
stepToolChoice != null
|
|
1399
|
+
? JSON.stringify(stepToolChoice)
|
|
1400
|
+
: undefined,
|
|
1401
|
+
},
|
|
1402
|
+
|
|
1403
|
+
// standardized gen-ai llm span attributes:
|
|
1404
|
+
'gen_ai.system': stepModel.provider,
|
|
1405
|
+
'gen_ai.request.model': stepModel.modelId,
|
|
1406
|
+
'gen_ai.request.frequency_penalty':
|
|
1407
|
+
callSettings.frequencyPenalty,
|
|
1408
|
+
'gen_ai.request.max_tokens': callSettings.maxOutputTokens,
|
|
1409
|
+
'gen_ai.request.presence_penalty':
|
|
1410
|
+
callSettings.presencePenalty,
|
|
1411
|
+
'gen_ai.request.stop_sequences': callSettings.stopSequences,
|
|
1412
|
+
'gen_ai.request.temperature': callSettings.temperature,
|
|
1413
|
+
'gen_ai.request.top_k': callSettings.topK,
|
|
1414
|
+
'gen_ai.request.top_p': callSettings.topP,
|
|
1415
|
+
},
|
|
1416
|
+
}),
|
|
1417
|
+
tracer,
|
|
1418
|
+
endWhenDone: false,
|
|
1419
|
+
fn: async doStreamSpan => ({
|
|
1420
|
+
startTimestampMs: now(), // get before the call
|
|
1421
|
+
doStreamSpan,
|
|
1422
|
+
result: await stepModel.doStream({
|
|
1423
|
+
...callSettings,
|
|
1424
|
+
tools: stepTools,
|
|
1425
|
+
toolChoice: stepToolChoice,
|
|
1426
|
+
responseFormat: await output?.responseFormat,
|
|
1427
|
+
prompt: promptMessages,
|
|
1428
|
+
providerOptions: stepProviderOptions,
|
|
1429
|
+
abortSignal,
|
|
1430
|
+
headers,
|
|
1431
|
+
includeRawChunks,
|
|
1432
|
+
}),
|
|
1433
|
+
}),
|
|
1434
|
+
}),
|
|
1435
|
+
);
|
|
1436
|
+
|
|
1437
|
+
const streamWithToolResults = runToolsTransformation({
|
|
1438
|
+
tools,
|
|
1439
|
+
generatorStream: stream,
|
|
1440
|
+
tracer,
|
|
1441
|
+
telemetry,
|
|
1442
|
+
system,
|
|
1443
|
+
messages: stepInputMessages,
|
|
1444
|
+
repairToolCall,
|
|
1445
|
+
abortSignal,
|
|
1446
|
+
experimental_context,
|
|
1447
|
+
generateId,
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
const stepRequest = request ?? {};
|
|
1451
|
+
const stepToolCalls: TypedToolCall<TOOLS>[] = [];
|
|
1452
|
+
const stepToolOutputs: ToolOutput<TOOLS>[] = [];
|
|
1453
|
+
let warnings: SharedV3Warning[] | undefined;
|
|
1454
|
+
|
|
1455
|
+
const activeToolCallToolNames: Record<string, string> = {};
|
|
1456
|
+
|
|
1457
|
+
let stepFinishReason: FinishReason = 'other';
|
|
1458
|
+
let stepRawFinishReason: string | undefined = undefined;
|
|
1459
|
+
|
|
1460
|
+
let stepUsage: LanguageModelUsage = createNullLanguageModelUsage();
|
|
1461
|
+
let stepProviderMetadata: ProviderMetadata | undefined;
|
|
1462
|
+
let stepFirstChunk = true;
|
|
1463
|
+
let stepResponse: { id: string; timestamp: Date; modelId: string } = {
|
|
1464
|
+
id: generateId(),
|
|
1465
|
+
timestamp: new Date(),
|
|
1466
|
+
modelId: model.modelId,
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
// raw text as it comes from the provider. recorded for telemetry.
|
|
1470
|
+
let activeText = '';
|
|
1471
|
+
|
|
1472
|
+
self.addStream(
|
|
1473
|
+
streamWithToolResults.pipeThrough(
|
|
1474
|
+
new TransformStream<
|
|
1475
|
+
SingleRequestTextStreamPart<TOOLS>,
|
|
1476
|
+
TextStreamPart<TOOLS>
|
|
1477
|
+
>({
|
|
1478
|
+
async transform(chunk, controller): Promise<void> {
|
|
1479
|
+
resetChunkTimeout();
|
|
1480
|
+
|
|
1481
|
+
if (chunk.type === 'stream-start') {
|
|
1482
|
+
warnings = chunk.warnings;
|
|
1483
|
+
return; // stream start chunks are sent immediately and do not count as first chunk
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
if (stepFirstChunk) {
|
|
1487
|
+
// Telemetry for first chunk:
|
|
1488
|
+
const msToFirstChunk = now() - startTimestampMs;
|
|
1489
|
+
|
|
1490
|
+
stepFirstChunk = false;
|
|
1491
|
+
|
|
1492
|
+
doStreamSpan.addEvent('ai.stream.firstChunk', {
|
|
1493
|
+
'ai.response.msToFirstChunk': msToFirstChunk,
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
doStreamSpan.setAttributes({
|
|
1497
|
+
'ai.response.msToFirstChunk': msToFirstChunk,
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
// Step start:
|
|
1501
|
+
controller.enqueue({
|
|
1502
|
+
type: 'start-step',
|
|
1503
|
+
request: stepRequest,
|
|
1504
|
+
warnings: warnings ?? [],
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
const chunkType = chunk.type;
|
|
1509
|
+
switch (chunkType) {
|
|
1510
|
+
case 'tool-approval-request':
|
|
1511
|
+
case 'text-start':
|
|
1512
|
+
case 'text-end': {
|
|
1513
|
+
controller.enqueue(chunk);
|
|
1514
|
+
break;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
case 'text-delta': {
|
|
1518
|
+
if (chunk.delta.length > 0) {
|
|
1519
|
+
controller.enqueue({
|
|
1520
|
+
type: 'text-delta',
|
|
1521
|
+
id: chunk.id,
|
|
1522
|
+
text: chunk.delta,
|
|
1523
|
+
providerMetadata: chunk.providerMetadata,
|
|
1524
|
+
});
|
|
1525
|
+
activeText += chunk.delta;
|
|
1526
|
+
}
|
|
1527
|
+
break;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
case 'reasoning-start':
|
|
1531
|
+
case 'reasoning-end': {
|
|
1532
|
+
controller.enqueue(chunk);
|
|
1533
|
+
break;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
case 'reasoning-delta': {
|
|
1537
|
+
controller.enqueue({
|
|
1538
|
+
type: 'reasoning-delta',
|
|
1539
|
+
id: chunk.id,
|
|
1540
|
+
text: chunk.delta,
|
|
1541
|
+
providerMetadata: chunk.providerMetadata,
|
|
1542
|
+
});
|
|
1543
|
+
break;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
case 'tool-call': {
|
|
1547
|
+
controller.enqueue(chunk);
|
|
1548
|
+
// store tool calls for onFinish callback and toolCalls promise:
|
|
1549
|
+
stepToolCalls.push(chunk);
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
case 'tool-result': {
|
|
1554
|
+
controller.enqueue(chunk);
|
|
1555
|
+
|
|
1556
|
+
if (!chunk.preliminary) {
|
|
1557
|
+
stepToolOutputs.push(chunk);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
break;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
case 'tool-error': {
|
|
1564
|
+
controller.enqueue(chunk);
|
|
1565
|
+
stepToolOutputs.push(chunk);
|
|
1566
|
+
break;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
case 'response-metadata': {
|
|
1570
|
+
stepResponse = {
|
|
1571
|
+
id: chunk.id ?? stepResponse.id,
|
|
1572
|
+
timestamp: chunk.timestamp ?? stepResponse.timestamp,
|
|
1573
|
+
modelId: chunk.modelId ?? stepResponse.modelId,
|
|
1574
|
+
};
|
|
1575
|
+
break;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
case 'finish': {
|
|
1579
|
+
// Note: tool executions might not be finished yet when the finish event is emitted.
|
|
1580
|
+
// store usage and finish reason for promises and onFinish callback:
|
|
1581
|
+
stepUsage = chunk.usage;
|
|
1582
|
+
stepFinishReason = chunk.finishReason;
|
|
1583
|
+
stepRawFinishReason = chunk.rawFinishReason;
|
|
1584
|
+
stepProviderMetadata = chunk.providerMetadata;
|
|
1585
|
+
|
|
1586
|
+
// Telemetry for finish event timing
|
|
1587
|
+
// (since tool executions can take longer and distort calculations)
|
|
1588
|
+
const msToFinish = now() - startTimestampMs;
|
|
1589
|
+
doStreamSpan.addEvent('ai.stream.finish');
|
|
1590
|
+
doStreamSpan.setAttributes({
|
|
1591
|
+
'ai.response.msToFinish': msToFinish,
|
|
1592
|
+
'ai.response.avgOutputTokensPerSecond':
|
|
1593
|
+
(1000 * (stepUsage.outputTokens ?? 0)) / msToFinish,
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
break;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
case 'file': {
|
|
1600
|
+
controller.enqueue(chunk);
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
case 'source': {
|
|
1605
|
+
controller.enqueue(chunk);
|
|
1606
|
+
break;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
case 'tool-input-start': {
|
|
1610
|
+
activeToolCallToolNames[chunk.id] = chunk.toolName;
|
|
1611
|
+
|
|
1612
|
+
const tool = tools?.[chunk.toolName];
|
|
1613
|
+
if (tool?.onInputStart != null) {
|
|
1614
|
+
await tool.onInputStart({
|
|
1615
|
+
toolCallId: chunk.id,
|
|
1616
|
+
messages: stepInputMessages,
|
|
1617
|
+
abortSignal,
|
|
1618
|
+
experimental_context,
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
controller.enqueue({
|
|
1623
|
+
...chunk,
|
|
1624
|
+
dynamic: chunk.dynamic ?? tool?.type === 'dynamic',
|
|
1625
|
+
title: tool?.title,
|
|
1626
|
+
});
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
case 'tool-input-end': {
|
|
1631
|
+
delete activeToolCallToolNames[chunk.id];
|
|
1632
|
+
controller.enqueue(chunk);
|
|
1633
|
+
break;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
case 'tool-input-delta': {
|
|
1637
|
+
const toolName = activeToolCallToolNames[chunk.id];
|
|
1638
|
+
const tool = tools?.[toolName];
|
|
1639
|
+
|
|
1640
|
+
if (tool?.onInputDelta != null) {
|
|
1641
|
+
await tool.onInputDelta({
|
|
1642
|
+
inputTextDelta: chunk.delta,
|
|
1643
|
+
toolCallId: chunk.id,
|
|
1644
|
+
messages: stepInputMessages,
|
|
1645
|
+
abortSignal,
|
|
1646
|
+
experimental_context,
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
controller.enqueue(chunk);
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
case 'error': {
|
|
1655
|
+
controller.enqueue(chunk);
|
|
1656
|
+
stepFinishReason = 'error';
|
|
1657
|
+
break;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
case 'raw': {
|
|
1661
|
+
if (includeRawChunks) {
|
|
1662
|
+
controller.enqueue(chunk);
|
|
1663
|
+
}
|
|
1664
|
+
break;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
default: {
|
|
1668
|
+
const exhaustiveCheck: never = chunkType;
|
|
1669
|
+
throw new Error(`Unknown chunk type: ${exhaustiveCheck}`);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
|
|
1674
|
+
// invoke onFinish callback and resolve toolResults promise when the stream is about to close:
|
|
1675
|
+
async flush(controller) {
|
|
1676
|
+
const stepToolCallsJson =
|
|
1677
|
+
stepToolCalls.length > 0
|
|
1678
|
+
? JSON.stringify(stepToolCalls)
|
|
1679
|
+
: undefined;
|
|
1680
|
+
|
|
1681
|
+
// record telemetry information first to ensure best effort timing
|
|
1682
|
+
try {
|
|
1683
|
+
doStreamSpan.setAttributes(
|
|
1684
|
+
await selectTelemetryAttributes({
|
|
1685
|
+
telemetry,
|
|
1686
|
+
attributes: {
|
|
1687
|
+
'ai.response.finishReason': stepFinishReason,
|
|
1688
|
+
'ai.response.text': {
|
|
1689
|
+
output: () => activeText,
|
|
1690
|
+
},
|
|
1691
|
+
'ai.response.toolCalls': {
|
|
1692
|
+
output: () => stepToolCallsJson,
|
|
1693
|
+
},
|
|
1694
|
+
'ai.response.id': stepResponse.id,
|
|
1695
|
+
'ai.response.model': stepResponse.modelId,
|
|
1696
|
+
'ai.response.timestamp':
|
|
1697
|
+
stepResponse.timestamp.toISOString(),
|
|
1698
|
+
'ai.response.providerMetadata':
|
|
1699
|
+
JSON.stringify(stepProviderMetadata),
|
|
1700
|
+
|
|
1701
|
+
'ai.usage.inputTokens': stepUsage.inputTokens,
|
|
1702
|
+
'ai.usage.outputTokens': stepUsage.outputTokens,
|
|
1703
|
+
'ai.usage.totalTokens': stepUsage.totalTokens,
|
|
1704
|
+
'ai.usage.reasoningTokens': stepUsage.reasoningTokens,
|
|
1705
|
+
'ai.usage.cachedInputTokens':
|
|
1706
|
+
stepUsage.cachedInputTokens,
|
|
1707
|
+
|
|
1708
|
+
// standardized gen-ai llm span attributes:
|
|
1709
|
+
'gen_ai.response.finish_reasons': [stepFinishReason],
|
|
1710
|
+
'gen_ai.response.id': stepResponse.id,
|
|
1711
|
+
'gen_ai.response.model': stepResponse.modelId,
|
|
1712
|
+
'gen_ai.usage.input_tokens': stepUsage.inputTokens,
|
|
1713
|
+
'gen_ai.usage.output_tokens': stepUsage.outputTokens,
|
|
1714
|
+
},
|
|
1715
|
+
}),
|
|
1716
|
+
);
|
|
1717
|
+
} catch (error) {
|
|
1718
|
+
// ignore error setting telemetry attributes
|
|
1719
|
+
} finally {
|
|
1720
|
+
// finish doStreamSpan before other operations for correct timing:
|
|
1721
|
+
doStreamSpan.end();
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
controller.enqueue({
|
|
1725
|
+
type: 'finish-step',
|
|
1726
|
+
finishReason: stepFinishReason,
|
|
1727
|
+
rawFinishReason: stepRawFinishReason,
|
|
1728
|
+
usage: stepUsage,
|
|
1729
|
+
providerMetadata: stepProviderMetadata,
|
|
1730
|
+
response: {
|
|
1731
|
+
...stepResponse,
|
|
1732
|
+
headers: response?.headers,
|
|
1733
|
+
},
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
const combinedUsage = addLanguageModelUsage(usage, stepUsage);
|
|
1737
|
+
|
|
1738
|
+
// wait for the step to be fully processed by the event processor
|
|
1739
|
+
// to ensure that the recorded steps are complete:
|
|
1740
|
+
await stepFinish.promise;
|
|
1741
|
+
|
|
1742
|
+
const clientToolCalls = stepToolCalls.filter(
|
|
1743
|
+
toolCall => toolCall.providerExecuted !== true,
|
|
1744
|
+
);
|
|
1745
|
+
const clientToolOutputs = stepToolOutputs.filter(
|
|
1746
|
+
toolOutput => toolOutput.providerExecuted !== true,
|
|
1747
|
+
);
|
|
1748
|
+
|
|
1749
|
+
// Track provider-executed tool calls that support deferred results.
|
|
1750
|
+
// In programmatic tool calling, a server tool (e.g., code_execution) may
|
|
1751
|
+
// trigger a client tool, and the server tool's result is deferred until
|
|
1752
|
+
// the client tool's result is sent back.
|
|
1753
|
+
for (const toolCall of stepToolCalls) {
|
|
1754
|
+
if (toolCall.providerExecuted !== true) continue;
|
|
1755
|
+
const tool = tools?.[toolCall.toolName];
|
|
1756
|
+
if (
|
|
1757
|
+
tool?.type === 'provider' &&
|
|
1758
|
+
tool.supportsDeferredResults
|
|
1759
|
+
) {
|
|
1760
|
+
// Check if this tool call already has a result in the current step
|
|
1761
|
+
const hasResultInStep = stepToolOutputs.some(
|
|
1762
|
+
output =>
|
|
1763
|
+
output.type === 'tool-result' &&
|
|
1764
|
+
output.toolCallId === toolCall.toolCallId,
|
|
1765
|
+
);
|
|
1766
|
+
if (!hasResultInStep) {
|
|
1767
|
+
pendingDeferredToolCalls.set(toolCall.toolCallId, {
|
|
1768
|
+
toolName: toolCall.toolName,
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// Mark deferred tool calls as resolved when we receive their results
|
|
1775
|
+
for (const output of stepToolOutputs) {
|
|
1776
|
+
if (output.type === 'tool-result') {
|
|
1777
|
+
pendingDeferredToolCalls.delete(output.toolCallId);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// Clear the step and chunk timeouts before the next step is started
|
|
1782
|
+
clearStepTimeout();
|
|
1783
|
+
clearChunkTimeout();
|
|
1784
|
+
|
|
1785
|
+
if (
|
|
1786
|
+
// Continue if:
|
|
1787
|
+
// 1. There are client tool calls that have all been executed, OR
|
|
1788
|
+
// 2. There are pending deferred results from provider-executed tools
|
|
1789
|
+
((clientToolCalls.length > 0 &&
|
|
1790
|
+
clientToolOutputs.length === clientToolCalls.length) ||
|
|
1791
|
+
pendingDeferredToolCalls.size > 0) &&
|
|
1792
|
+
// continue until a stop condition is met:
|
|
1793
|
+
!(await isStopConditionMet({
|
|
1794
|
+
stopConditions,
|
|
1795
|
+
steps: recordedSteps,
|
|
1796
|
+
}))
|
|
1797
|
+
) {
|
|
1798
|
+
// append to messages for the next step:
|
|
1799
|
+
responseMessages.push(
|
|
1800
|
+
...(await toResponseMessages({
|
|
1801
|
+
content:
|
|
1802
|
+
// use transformed content to create the messages for the next step:
|
|
1803
|
+
recordedSteps[recordedSteps.length - 1].content,
|
|
1804
|
+
tools,
|
|
1805
|
+
})),
|
|
1806
|
+
);
|
|
1807
|
+
|
|
1808
|
+
try {
|
|
1809
|
+
await streamStep({
|
|
1810
|
+
currentStep: currentStep + 1,
|
|
1811
|
+
responseMessages,
|
|
1812
|
+
usage: combinedUsage,
|
|
1813
|
+
});
|
|
1814
|
+
} catch (error) {
|
|
1815
|
+
controller.enqueue({
|
|
1816
|
+
type: 'error',
|
|
1817
|
+
error,
|
|
1818
|
+
});
|
|
1819
|
+
|
|
1820
|
+
self.closeStream();
|
|
1821
|
+
}
|
|
1822
|
+
} else {
|
|
1823
|
+
controller.enqueue({
|
|
1824
|
+
type: 'finish',
|
|
1825
|
+
finishReason: stepFinishReason,
|
|
1826
|
+
rawFinishReason: stepRawFinishReason,
|
|
1827
|
+
totalUsage: combinedUsage,
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
self.closeStream(); // close the stitchable stream
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
}),
|
|
1834
|
+
),
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
// add the initial stream to the stitchable stream
|
|
1839
|
+
await streamStep({
|
|
1840
|
+
currentStep: 0,
|
|
1841
|
+
responseMessages: initialResponseMessages,
|
|
1842
|
+
usage: createNullLanguageModelUsage(),
|
|
1843
|
+
});
|
|
1844
|
+
},
|
|
1845
|
+
}).catch(error => {
|
|
1846
|
+
// add an error stream part and close the streams:
|
|
1847
|
+
self.addStream(
|
|
1848
|
+
new ReadableStream({
|
|
1849
|
+
start(controller) {
|
|
1850
|
+
controller.enqueue({ type: 'error', error });
|
|
1851
|
+
controller.close();
|
|
1852
|
+
},
|
|
1853
|
+
}),
|
|
1854
|
+
);
|
|
1855
|
+
self.closeStream();
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
get steps() {
|
|
1860
|
+
// when any of the promises are accessed, the stream is consumed
|
|
1861
|
+
// so it resolves without needing to consume the stream separately
|
|
1862
|
+
this.consumeStream();
|
|
1863
|
+
|
|
1864
|
+
return this._steps.promise;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
private get finalStep() {
|
|
1868
|
+
return this.steps.then(steps => steps[steps.length - 1]);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
get content() {
|
|
1872
|
+
return this.finalStep.then(step => step.content);
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
get warnings() {
|
|
1876
|
+
return this.finalStep.then(step => step.warnings);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
get providerMetadata() {
|
|
1880
|
+
return this.finalStep.then(step => step.providerMetadata);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
get text() {
|
|
1884
|
+
return this.finalStep.then(step => step.text);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
get reasoningText() {
|
|
1888
|
+
return this.finalStep.then(step => step.reasoningText);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
get reasoning() {
|
|
1892
|
+
return this.finalStep.then(step => step.reasoning);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
get sources() {
|
|
1896
|
+
return this.finalStep.then(step => step.sources);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
get files() {
|
|
1900
|
+
return this.finalStep.then(step => step.files);
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
get toolCalls() {
|
|
1904
|
+
return this.finalStep.then(step => step.toolCalls);
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
get staticToolCalls() {
|
|
1908
|
+
return this.finalStep.then(step => step.staticToolCalls);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
get dynamicToolCalls() {
|
|
1912
|
+
return this.finalStep.then(step => step.dynamicToolCalls);
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
get toolResults() {
|
|
1916
|
+
return this.finalStep.then(step => step.toolResults);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
get staticToolResults() {
|
|
1920
|
+
return this.finalStep.then(step => step.staticToolResults);
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
get dynamicToolResults() {
|
|
1924
|
+
return this.finalStep.then(step => step.dynamicToolResults);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
get usage() {
|
|
1928
|
+
return this.finalStep.then(step => step.usage);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
get request() {
|
|
1932
|
+
return this.finalStep.then(step => step.request);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
get response() {
|
|
1936
|
+
return this.finalStep.then(step => step.response);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
get totalUsage() {
|
|
1940
|
+
// when any of the promises are accessed, the stream is consumed
|
|
1941
|
+
// so it resolves without needing to consume the stream separately
|
|
1942
|
+
this.consumeStream();
|
|
1943
|
+
|
|
1944
|
+
return this._totalUsage.promise;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
get finishReason() {
|
|
1948
|
+
// when any of the promises are accessed, the stream is consumed
|
|
1949
|
+
// so it resolves without needing to consume the stream separately
|
|
1950
|
+
this.consumeStream();
|
|
1951
|
+
|
|
1952
|
+
return this._finishReason.promise;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
get rawFinishReason() {
|
|
1956
|
+
// when any of the promises are accessed, the stream is consumed
|
|
1957
|
+
// so it resolves without needing to consume the stream separately
|
|
1958
|
+
this.consumeStream();
|
|
1959
|
+
|
|
1960
|
+
return this._rawFinishReason.promise;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
Split out a new stream from the original stream.
|
|
1965
|
+
The original stream is replaced to allow for further splitting,
|
|
1966
|
+
since we do not know how many times the stream will be split.
|
|
1967
|
+
|
|
1968
|
+
Note: this leads to buffering the stream content on the server.
|
|
1969
|
+
However, the LLM results are expected to be small enough to not cause issues.
|
|
1970
|
+
*/
|
|
1971
|
+
private teeStream() {
|
|
1972
|
+
const [stream1, stream2] = this.baseStream.tee();
|
|
1973
|
+
this.baseStream = stream2;
|
|
1974
|
+
return stream1;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
get textStream(): AsyncIterableStream<string> {
|
|
1978
|
+
return createAsyncIterableStream(
|
|
1979
|
+
this.teeStream().pipeThrough(
|
|
1980
|
+
new TransformStream<
|
|
1981
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
|
|
1982
|
+
string
|
|
1983
|
+
>({
|
|
1984
|
+
transform({ part }, controller) {
|
|
1985
|
+
if (part.type === 'text-delta') {
|
|
1986
|
+
controller.enqueue(part.text);
|
|
1987
|
+
}
|
|
1988
|
+
},
|
|
1989
|
+
}),
|
|
1990
|
+
),
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
get fullStream(): AsyncIterableStream<TextStreamPart<TOOLS>> {
|
|
1995
|
+
return createAsyncIterableStream(
|
|
1996
|
+
this.teeStream().pipeThrough(
|
|
1997
|
+
new TransformStream<
|
|
1998
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
|
|
1999
|
+
TextStreamPart<TOOLS>
|
|
2000
|
+
>({
|
|
2001
|
+
transform({ part }, controller) {
|
|
2002
|
+
controller.enqueue(part);
|
|
2003
|
+
},
|
|
2004
|
+
}),
|
|
2005
|
+
),
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
async consumeStream(options?: ConsumeStreamOptions): Promise<void> {
|
|
2010
|
+
try {
|
|
2011
|
+
await consumeStream({
|
|
2012
|
+
stream: this.fullStream,
|
|
2013
|
+
onError: options?.onError,
|
|
2014
|
+
});
|
|
2015
|
+
} catch (error) {
|
|
2016
|
+
options?.onError?.(error);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
get experimental_partialOutputStream(): AsyncIterableStream<
|
|
2021
|
+
InferPartialOutput<OUTPUT>
|
|
2022
|
+
> {
|
|
2023
|
+
return this.partialOutputStream;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
get partialOutputStream(): AsyncIterableStream<InferPartialOutput<OUTPUT>> {
|
|
2027
|
+
return createAsyncIterableStream(
|
|
2028
|
+
this.teeStream().pipeThrough(
|
|
2029
|
+
new TransformStream<
|
|
2030
|
+
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
|
|
2031
|
+
InferPartialOutput<OUTPUT>
|
|
2032
|
+
>({
|
|
2033
|
+
transform({ partialOutput }, controller) {
|
|
2034
|
+
if (partialOutput != null) {
|
|
2035
|
+
controller.enqueue(partialOutput);
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
}),
|
|
2039
|
+
),
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
get elementStream(): AsyncIterableStream<InferElementOutput<OUTPUT>> {
|
|
2044
|
+
const transform = this.outputSpecification?.createElementStreamTransform();
|
|
2045
|
+
|
|
2046
|
+
if (transform == null) {
|
|
2047
|
+
throw new UnsupportedFunctionalityError({
|
|
2048
|
+
functionality: `element streams in ${this.outputSpecification?.name ?? 'text'} mode`,
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
return createAsyncIterableStream(this.teeStream().pipeThrough(transform));
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
get output(): Promise<InferCompleteOutput<OUTPUT>> {
|
|
2056
|
+
return this.finalStep.then(step => {
|
|
2057
|
+
const output = this.outputSpecification ?? text();
|
|
2058
|
+
return output.parseCompleteOutput(
|
|
2059
|
+
{ text: step.text },
|
|
2060
|
+
{
|
|
2061
|
+
response: step.response,
|
|
2062
|
+
usage: step.usage,
|
|
2063
|
+
finishReason: step.finishReason,
|
|
2064
|
+
},
|
|
2065
|
+
);
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
toUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
2070
|
+
originalMessages,
|
|
2071
|
+
generateMessageId,
|
|
2072
|
+
onFinish,
|
|
2073
|
+
messageMetadata,
|
|
2074
|
+
sendReasoning = true,
|
|
2075
|
+
sendSources = false,
|
|
2076
|
+
sendStart = true,
|
|
2077
|
+
sendFinish = true,
|
|
2078
|
+
onError = getErrorMessage,
|
|
2079
|
+
}: UIMessageStreamOptions<UI_MESSAGE> = {}): AsyncIterableStream<
|
|
2080
|
+
InferUIMessageChunk<UI_MESSAGE>
|
|
2081
|
+
> {
|
|
2082
|
+
const responseMessageId =
|
|
2083
|
+
generateMessageId != null
|
|
2084
|
+
? getResponseUIMessageId({
|
|
2085
|
+
originalMessages,
|
|
2086
|
+
responseMessageId: generateMessageId,
|
|
2087
|
+
})
|
|
2088
|
+
: undefined;
|
|
2089
|
+
|
|
2090
|
+
// TODO simplify once dynamic is no longer needed for invalid tool inputs
|
|
2091
|
+
const isDynamic = (part: { toolName: string; dynamic?: boolean }) => {
|
|
2092
|
+
const tool = this.tools?.[part.toolName];
|
|
2093
|
+
|
|
2094
|
+
// provider-executed, dynamic tools are not listed in the tools object
|
|
2095
|
+
if (tool == null) {
|
|
2096
|
+
return part.dynamic;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
return tool?.type === 'dynamic' ? true : undefined;
|
|
2100
|
+
};
|
|
2101
|
+
|
|
2102
|
+
const baseStream = this.fullStream.pipeThrough(
|
|
2103
|
+
new TransformStream<
|
|
2104
|
+
TextStreamPart<TOOLS>,
|
|
2105
|
+
UIMessageChunk<
|
|
2106
|
+
InferUIMessageMetadata<UI_MESSAGE>,
|
|
2107
|
+
InferUIMessageData<UI_MESSAGE>
|
|
2108
|
+
>
|
|
2109
|
+
>({
|
|
2110
|
+
transform: async (part, controller) => {
|
|
2111
|
+
const messageMetadataValue = messageMetadata?.({ part });
|
|
2112
|
+
|
|
2113
|
+
const partType = part.type;
|
|
2114
|
+
switch (partType) {
|
|
2115
|
+
case 'text-start': {
|
|
2116
|
+
controller.enqueue({
|
|
2117
|
+
type: 'text-start',
|
|
2118
|
+
id: part.id,
|
|
2119
|
+
...(part.providerMetadata != null
|
|
2120
|
+
? { providerMetadata: part.providerMetadata }
|
|
2121
|
+
: {}),
|
|
2122
|
+
});
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
case 'text-delta': {
|
|
2127
|
+
controller.enqueue({
|
|
2128
|
+
type: 'text-delta',
|
|
2129
|
+
id: part.id,
|
|
2130
|
+
delta: part.text,
|
|
2131
|
+
...(part.providerMetadata != null
|
|
2132
|
+
? { providerMetadata: part.providerMetadata }
|
|
2133
|
+
: {}),
|
|
2134
|
+
});
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
case 'text-end': {
|
|
2139
|
+
controller.enqueue({
|
|
2140
|
+
type: 'text-end',
|
|
2141
|
+
id: part.id,
|
|
2142
|
+
...(part.providerMetadata != null
|
|
2143
|
+
? { providerMetadata: part.providerMetadata }
|
|
2144
|
+
: {}),
|
|
2145
|
+
});
|
|
2146
|
+
break;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
case 'reasoning-start': {
|
|
2150
|
+
controller.enqueue({
|
|
2151
|
+
type: 'reasoning-start',
|
|
2152
|
+
id: part.id,
|
|
2153
|
+
...(part.providerMetadata != null
|
|
2154
|
+
? { providerMetadata: part.providerMetadata }
|
|
2155
|
+
: {}),
|
|
2156
|
+
});
|
|
2157
|
+
break;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
case 'reasoning-delta': {
|
|
2161
|
+
if (sendReasoning) {
|
|
2162
|
+
controller.enqueue({
|
|
2163
|
+
type: 'reasoning-delta',
|
|
2164
|
+
id: part.id,
|
|
2165
|
+
delta: part.text,
|
|
2166
|
+
...(part.providerMetadata != null
|
|
2167
|
+
? { providerMetadata: part.providerMetadata }
|
|
2168
|
+
: {}),
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
break;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
case 'reasoning-end': {
|
|
2175
|
+
controller.enqueue({
|
|
2176
|
+
type: 'reasoning-end',
|
|
2177
|
+
id: part.id,
|
|
2178
|
+
...(part.providerMetadata != null
|
|
2179
|
+
? { providerMetadata: part.providerMetadata }
|
|
2180
|
+
: {}),
|
|
2181
|
+
});
|
|
2182
|
+
break;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
case 'file': {
|
|
2186
|
+
controller.enqueue({
|
|
2187
|
+
type: 'file',
|
|
2188
|
+
mediaType: part.file.mediaType,
|
|
2189
|
+
url: `data:${part.file.mediaType};base64,${part.file.base64}`,
|
|
2190
|
+
});
|
|
2191
|
+
break;
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
case 'source': {
|
|
2195
|
+
if (sendSources && part.sourceType === 'url') {
|
|
2196
|
+
controller.enqueue({
|
|
2197
|
+
type: 'source-url',
|
|
2198
|
+
sourceId: part.id,
|
|
2199
|
+
url: part.url,
|
|
2200
|
+
title: part.title,
|
|
2201
|
+
...(part.providerMetadata != null
|
|
2202
|
+
? { providerMetadata: part.providerMetadata }
|
|
2203
|
+
: {}),
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
if (sendSources && part.sourceType === 'document') {
|
|
2208
|
+
controller.enqueue({
|
|
2209
|
+
type: 'source-document',
|
|
2210
|
+
sourceId: part.id,
|
|
2211
|
+
mediaType: part.mediaType,
|
|
2212
|
+
title: part.title,
|
|
2213
|
+
filename: part.filename,
|
|
2214
|
+
...(part.providerMetadata != null
|
|
2215
|
+
? { providerMetadata: part.providerMetadata }
|
|
2216
|
+
: {}),
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
break;
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
case 'tool-input-start': {
|
|
2223
|
+
const dynamic = isDynamic(part);
|
|
2224
|
+
|
|
2225
|
+
controller.enqueue({
|
|
2226
|
+
type: 'tool-input-start',
|
|
2227
|
+
toolCallId: part.id,
|
|
2228
|
+
toolName: part.toolName,
|
|
2229
|
+
...(part.providerExecuted != null
|
|
2230
|
+
? { providerExecuted: part.providerExecuted }
|
|
2231
|
+
: {}),
|
|
2232
|
+
...(dynamic != null ? { dynamic } : {}),
|
|
2233
|
+
...(part.title != null ? { title: part.title } : {}),
|
|
2234
|
+
});
|
|
2235
|
+
break;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
case 'tool-input-delta': {
|
|
2239
|
+
controller.enqueue({
|
|
2240
|
+
type: 'tool-input-delta',
|
|
2241
|
+
toolCallId: part.id,
|
|
2242
|
+
inputTextDelta: part.delta,
|
|
2243
|
+
});
|
|
2244
|
+
break;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
case 'tool-call': {
|
|
2248
|
+
const dynamic = isDynamic(part);
|
|
2249
|
+
|
|
2250
|
+
if (part.invalid) {
|
|
2251
|
+
controller.enqueue({
|
|
2252
|
+
type: 'tool-input-error',
|
|
2253
|
+
toolCallId: part.toolCallId,
|
|
2254
|
+
toolName: part.toolName,
|
|
2255
|
+
input: part.input,
|
|
2256
|
+
...(part.providerExecuted != null
|
|
2257
|
+
? { providerExecuted: part.providerExecuted }
|
|
2258
|
+
: {}),
|
|
2259
|
+
...(part.providerMetadata != null
|
|
2260
|
+
? { providerMetadata: part.providerMetadata }
|
|
2261
|
+
: {}),
|
|
2262
|
+
...(dynamic != null ? { dynamic } : {}),
|
|
2263
|
+
errorText: onError(part.error),
|
|
2264
|
+
...(part.title != null ? { title: part.title } : {}),
|
|
2265
|
+
});
|
|
2266
|
+
} else {
|
|
2267
|
+
controller.enqueue({
|
|
2268
|
+
type: 'tool-input-available',
|
|
2269
|
+
toolCallId: part.toolCallId,
|
|
2270
|
+
toolName: part.toolName,
|
|
2271
|
+
input: part.input,
|
|
2272
|
+
...(part.providerExecuted != null
|
|
2273
|
+
? { providerExecuted: part.providerExecuted }
|
|
2274
|
+
: {}),
|
|
2275
|
+
...(part.providerMetadata != null
|
|
2276
|
+
? { providerMetadata: part.providerMetadata }
|
|
2277
|
+
: {}),
|
|
2278
|
+
...(dynamic != null ? { dynamic } : {}),
|
|
2279
|
+
...(part.title != null ? { title: part.title } : {}),
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
break;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
case 'tool-approval-request': {
|
|
2287
|
+
controller.enqueue({
|
|
2288
|
+
type: 'tool-approval-request',
|
|
2289
|
+
approvalId: part.approvalId,
|
|
2290
|
+
toolCallId: part.toolCall.toolCallId,
|
|
2291
|
+
});
|
|
2292
|
+
break;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
case 'tool-result': {
|
|
2296
|
+
const dynamic = isDynamic(part);
|
|
2297
|
+
|
|
2298
|
+
controller.enqueue({
|
|
2299
|
+
type: 'tool-output-available',
|
|
2300
|
+
toolCallId: part.toolCallId,
|
|
2301
|
+
output: part.output,
|
|
2302
|
+
...(part.providerExecuted != null
|
|
2303
|
+
? { providerExecuted: part.providerExecuted }
|
|
2304
|
+
: {}),
|
|
2305
|
+
...(part.preliminary != null
|
|
2306
|
+
? { preliminary: part.preliminary }
|
|
2307
|
+
: {}),
|
|
2308
|
+
...(dynamic != null ? { dynamic } : {}),
|
|
2309
|
+
});
|
|
2310
|
+
break;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
case 'tool-error': {
|
|
2314
|
+
const dynamic = isDynamic(part);
|
|
2315
|
+
|
|
2316
|
+
controller.enqueue({
|
|
2317
|
+
type: 'tool-output-error',
|
|
2318
|
+
toolCallId: part.toolCallId,
|
|
2319
|
+
errorText: onError(part.error),
|
|
2320
|
+
...(part.providerExecuted != null
|
|
2321
|
+
? { providerExecuted: part.providerExecuted }
|
|
2322
|
+
: {}),
|
|
2323
|
+
...(dynamic != null ? { dynamic } : {}),
|
|
2324
|
+
});
|
|
2325
|
+
break;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
case 'tool-output-denied': {
|
|
2329
|
+
controller.enqueue({
|
|
2330
|
+
type: 'tool-output-denied',
|
|
2331
|
+
toolCallId: part.toolCallId,
|
|
2332
|
+
});
|
|
2333
|
+
break;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
case 'error': {
|
|
2337
|
+
controller.enqueue({
|
|
2338
|
+
type: 'error',
|
|
2339
|
+
errorText: onError(part.error),
|
|
2340
|
+
});
|
|
2341
|
+
break;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
case 'start-step': {
|
|
2345
|
+
controller.enqueue({ type: 'start-step' });
|
|
2346
|
+
break;
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
case 'finish-step': {
|
|
2350
|
+
controller.enqueue({ type: 'finish-step' });
|
|
2351
|
+
break;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
case 'start': {
|
|
2355
|
+
if (sendStart) {
|
|
2356
|
+
controller.enqueue({
|
|
2357
|
+
type: 'start',
|
|
2358
|
+
...(messageMetadataValue != null
|
|
2359
|
+
? { messageMetadata: messageMetadataValue }
|
|
2360
|
+
: {}),
|
|
2361
|
+
...(responseMessageId != null
|
|
2362
|
+
? { messageId: responseMessageId }
|
|
2363
|
+
: {}),
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
case 'finish': {
|
|
2370
|
+
if (sendFinish) {
|
|
2371
|
+
controller.enqueue({
|
|
2372
|
+
type: 'finish',
|
|
2373
|
+
finishReason: part.finishReason,
|
|
2374
|
+
...(messageMetadataValue != null
|
|
2375
|
+
? { messageMetadata: messageMetadataValue }
|
|
2376
|
+
: {}),
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2379
|
+
break;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
case 'abort': {
|
|
2383
|
+
controller.enqueue(part);
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
case 'tool-input-end': {
|
|
2388
|
+
break;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
case 'raw': {
|
|
2392
|
+
// Raw chunks are not included in UI message streams
|
|
2393
|
+
// as they contain provider-specific data for developer use
|
|
2394
|
+
break;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
default: {
|
|
2398
|
+
const exhaustiveCheck: never = partType;
|
|
2399
|
+
throw new Error(`Unknown chunk type: ${exhaustiveCheck}`);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
// start and finish events already have metadata
|
|
2404
|
+
// so we only need to send metadata for other parts
|
|
2405
|
+
if (
|
|
2406
|
+
messageMetadataValue != null &&
|
|
2407
|
+
partType !== 'start' &&
|
|
2408
|
+
partType !== 'finish'
|
|
2409
|
+
) {
|
|
2410
|
+
controller.enqueue({
|
|
2411
|
+
type: 'message-metadata',
|
|
2412
|
+
messageMetadata: messageMetadataValue,
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
},
|
|
2416
|
+
}),
|
|
2417
|
+
);
|
|
2418
|
+
|
|
2419
|
+
return createAsyncIterableStream(
|
|
2420
|
+
handleUIMessageStreamFinish<UI_MESSAGE>({
|
|
2421
|
+
stream: baseStream,
|
|
2422
|
+
messageId: responseMessageId ?? generateMessageId?.(),
|
|
2423
|
+
originalMessages,
|
|
2424
|
+
onFinish,
|
|
2425
|
+
onError,
|
|
2426
|
+
}),
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
pipeUIMessageStreamToResponse<UI_MESSAGE extends UIMessage>(
|
|
2431
|
+
response: ServerResponse,
|
|
2432
|
+
{
|
|
2433
|
+
originalMessages,
|
|
2434
|
+
generateMessageId,
|
|
2435
|
+
onFinish,
|
|
2436
|
+
messageMetadata,
|
|
2437
|
+
sendReasoning,
|
|
2438
|
+
sendSources,
|
|
2439
|
+
sendFinish,
|
|
2440
|
+
sendStart,
|
|
2441
|
+
onError,
|
|
2442
|
+
...init
|
|
2443
|
+
}: UIMessageStreamResponseInit & UIMessageStreamOptions<UI_MESSAGE> = {},
|
|
2444
|
+
) {
|
|
2445
|
+
pipeUIMessageStreamToResponse({
|
|
2446
|
+
response,
|
|
2447
|
+
stream: this.toUIMessageStream({
|
|
2448
|
+
originalMessages,
|
|
2449
|
+
generateMessageId,
|
|
2450
|
+
onFinish,
|
|
2451
|
+
messageMetadata,
|
|
2452
|
+
sendReasoning,
|
|
2453
|
+
sendSources,
|
|
2454
|
+
sendFinish,
|
|
2455
|
+
sendStart,
|
|
2456
|
+
onError,
|
|
2457
|
+
}),
|
|
2458
|
+
...init,
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
pipeTextStreamToResponse(response: ServerResponse, init?: ResponseInit) {
|
|
2463
|
+
pipeTextStreamToResponse({
|
|
2464
|
+
response,
|
|
2465
|
+
textStream: this.textStream,
|
|
2466
|
+
...init,
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
toUIMessageStreamResponse<UI_MESSAGE extends UIMessage>({
|
|
2471
|
+
originalMessages,
|
|
2472
|
+
generateMessageId,
|
|
2473
|
+
onFinish,
|
|
2474
|
+
messageMetadata,
|
|
2475
|
+
sendReasoning,
|
|
2476
|
+
sendSources,
|
|
2477
|
+
sendFinish,
|
|
2478
|
+
sendStart,
|
|
2479
|
+
onError,
|
|
2480
|
+
...init
|
|
2481
|
+
}: UIMessageStreamResponseInit &
|
|
2482
|
+
UIMessageStreamOptions<UI_MESSAGE> = {}): Response {
|
|
2483
|
+
return createUIMessageStreamResponse({
|
|
2484
|
+
stream: this.toUIMessageStream({
|
|
2485
|
+
originalMessages,
|
|
2486
|
+
generateMessageId,
|
|
2487
|
+
onFinish,
|
|
2488
|
+
messageMetadata,
|
|
2489
|
+
sendReasoning,
|
|
2490
|
+
sendSources,
|
|
2491
|
+
sendFinish,
|
|
2492
|
+
sendStart,
|
|
2493
|
+
onError,
|
|
2494
|
+
}),
|
|
2495
|
+
...init,
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
toTextStreamResponse(init?: ResponseInit): Response {
|
|
2500
|
+
return createTextStreamResponse({
|
|
2501
|
+
textStream: this.textStream,
|
|
2502
|
+
...init,
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
}
|