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,181 @@
|
|
|
1
|
+
import { JSONSchema7 } from '@ai-sdk/provider';
|
|
2
|
+
import { injectJsonInstruction } from './inject-json-instruction';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
|
|
5
|
+
const basicSchema: JSONSchema7 = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
name: { type: 'string' },
|
|
9
|
+
age: { type: 'number' },
|
|
10
|
+
},
|
|
11
|
+
required: ['name', 'age'],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
it('should handle basic case with prompt and schema', () => {
|
|
15
|
+
const result = injectJsonInstruction({
|
|
16
|
+
prompt: 'Generate a person',
|
|
17
|
+
schema: basicSchema,
|
|
18
|
+
});
|
|
19
|
+
expect(result).toBe(
|
|
20
|
+
'Generate a person\n\n' +
|
|
21
|
+
'JSON schema:\n' +
|
|
22
|
+
'{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
|
|
23
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle only prompt, no schema', () => {
|
|
28
|
+
const result = injectJsonInstruction({
|
|
29
|
+
prompt: 'Generate a person',
|
|
30
|
+
});
|
|
31
|
+
expect(result).toBe('Generate a person\n\nYou MUST answer with JSON.');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should handle only schema, no prompt', () => {
|
|
35
|
+
const result = injectJsonInstruction({
|
|
36
|
+
schema: basicSchema,
|
|
37
|
+
});
|
|
38
|
+
expect(result).toBe(
|
|
39
|
+
'JSON schema:\n' +
|
|
40
|
+
'{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
|
|
41
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle no prompt, no schema', () => {
|
|
46
|
+
const result = injectJsonInstruction({});
|
|
47
|
+
expect(result).toBe('You MUST answer with JSON.');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle custom schemaPrefix and schemaSuffix', () => {
|
|
51
|
+
const result = injectJsonInstruction({
|
|
52
|
+
prompt: 'Generate a person',
|
|
53
|
+
schema: basicSchema,
|
|
54
|
+
schemaPrefix: 'Custom prefix:',
|
|
55
|
+
schemaSuffix: 'Custom suffix',
|
|
56
|
+
});
|
|
57
|
+
expect(result).toBe(
|
|
58
|
+
'Generate a person\n\n' +
|
|
59
|
+
'Custom prefix:\n' +
|
|
60
|
+
'{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
|
|
61
|
+
'Custom suffix',
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should handle empty string prompt', () => {
|
|
66
|
+
const result = injectJsonInstruction({
|
|
67
|
+
prompt: '',
|
|
68
|
+
schema: basicSchema,
|
|
69
|
+
});
|
|
70
|
+
expect(result).toBe(
|
|
71
|
+
'JSON schema:\n' +
|
|
72
|
+
'{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
|
|
73
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle empty object schema', () => {
|
|
78
|
+
const result = injectJsonInstruction({
|
|
79
|
+
prompt: 'Generate something',
|
|
80
|
+
schema: {},
|
|
81
|
+
});
|
|
82
|
+
expect(result).toBe(
|
|
83
|
+
'Generate something\n\n' +
|
|
84
|
+
'JSON schema:\n' +
|
|
85
|
+
'{}\n' +
|
|
86
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle complex nested schema', () => {
|
|
91
|
+
const complexSchema: JSONSchema7 = {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
person: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
name: { type: 'string' },
|
|
98
|
+
age: { type: 'number' },
|
|
99
|
+
address: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
street: { type: 'string' },
|
|
103
|
+
city: { type: 'string' },
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
const result = injectJsonInstruction({
|
|
111
|
+
prompt: 'Generate a complex person',
|
|
112
|
+
schema: complexSchema,
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBe(
|
|
115
|
+
'Generate a complex person\n\n' +
|
|
116
|
+
'JSON schema:\n' +
|
|
117
|
+
'{"type":"object","properties":{"person":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"},"address":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"}}}}}}}\n' +
|
|
118
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should handle schema with special characters', () => {
|
|
123
|
+
const specialSchema: JSONSchema7 = {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
'special@property': { type: 'string' },
|
|
127
|
+
'emoji😊': { type: 'string' },
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
const result = injectJsonInstruction({
|
|
131
|
+
schema: specialSchema,
|
|
132
|
+
});
|
|
133
|
+
expect(result).toBe(
|
|
134
|
+
'JSON schema:\n' +
|
|
135
|
+
'{"type":"object","properties":{"special@property":{"type":"string"},"emoji😊":{"type":"string"}}}\n' +
|
|
136
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle very long prompt and schema', () => {
|
|
141
|
+
const longPrompt = 'A'.repeat(1000);
|
|
142
|
+
const longSchema: JSONSchema7 = {
|
|
143
|
+
type: 'object',
|
|
144
|
+
properties: {},
|
|
145
|
+
};
|
|
146
|
+
for (let i = 0; i < 100; i++) {
|
|
147
|
+
longSchema.properties![`prop${i}`] = { type: 'string' };
|
|
148
|
+
}
|
|
149
|
+
const result = injectJsonInstruction({
|
|
150
|
+
prompt: longPrompt,
|
|
151
|
+
schema: longSchema,
|
|
152
|
+
});
|
|
153
|
+
expect(result).toBe(
|
|
154
|
+
longPrompt +
|
|
155
|
+
'\n\n' +
|
|
156
|
+
'JSON schema:\n' +
|
|
157
|
+
JSON.stringify(longSchema) +
|
|
158
|
+
'\n' +
|
|
159
|
+
'You MUST answer with a JSON object that matches the JSON schema above.',
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should handle null values for optional parameters', () => {
|
|
164
|
+
const result = injectJsonInstruction({
|
|
165
|
+
prompt: null as any,
|
|
166
|
+
schema: null as any,
|
|
167
|
+
schemaPrefix: null as any,
|
|
168
|
+
schemaSuffix: null as any,
|
|
169
|
+
});
|
|
170
|
+
expect(result).toBe('');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should handle undefined values for optional parameters', () => {
|
|
174
|
+
const result = injectJsonInstruction({
|
|
175
|
+
prompt: undefined,
|
|
176
|
+
schema: undefined,
|
|
177
|
+
schemaPrefix: undefined,
|
|
178
|
+
schemaSuffix: undefined,
|
|
179
|
+
});
|
|
180
|
+
expect(result).toBe('You MUST answer with JSON.');
|
|
181
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { JSONSchema7 } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_SCHEMA_PREFIX = 'JSON schema:';
|
|
4
|
+
const DEFAULT_SCHEMA_SUFFIX =
|
|
5
|
+
'You MUST answer with a JSON object that matches the JSON schema above.';
|
|
6
|
+
const DEFAULT_GENERIC_SUFFIX = 'You MUST answer with JSON.';
|
|
7
|
+
|
|
8
|
+
export function injectJsonInstruction({
|
|
9
|
+
prompt,
|
|
10
|
+
schema,
|
|
11
|
+
schemaPrefix = schema != null ? DEFAULT_SCHEMA_PREFIX : undefined,
|
|
12
|
+
schemaSuffix = schema != null
|
|
13
|
+
? DEFAULT_SCHEMA_SUFFIX
|
|
14
|
+
: DEFAULT_GENERIC_SUFFIX,
|
|
15
|
+
}: {
|
|
16
|
+
prompt?: string;
|
|
17
|
+
schema?: JSONSchema7;
|
|
18
|
+
schemaPrefix?: string;
|
|
19
|
+
schemaSuffix?: string;
|
|
20
|
+
}): string {
|
|
21
|
+
return [
|
|
22
|
+
prompt != null && prompt.length > 0 ? prompt : undefined,
|
|
23
|
+
prompt != null && prompt.length > 0 ? '' : undefined, // add a newline if prompt is not null
|
|
24
|
+
schemaPrefix,
|
|
25
|
+
schema != null ? JSON.stringify(schema) : undefined,
|
|
26
|
+
schemaSuffix,
|
|
27
|
+
]
|
|
28
|
+
.filter(line => line != null)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isJSONArray,
|
|
3
|
+
isJSONObject,
|
|
4
|
+
JSONObject,
|
|
5
|
+
JSONSchema7,
|
|
6
|
+
JSONValue,
|
|
7
|
+
TypeValidationError,
|
|
8
|
+
UnsupportedFunctionalityError,
|
|
9
|
+
} from '@ai-sdk/provider';
|
|
10
|
+
import {
|
|
11
|
+
asSchema,
|
|
12
|
+
FlexibleSchema,
|
|
13
|
+
safeValidateTypes,
|
|
14
|
+
Schema,
|
|
15
|
+
ValidationResult,
|
|
16
|
+
} from '@ai-sdk/provider-utils';
|
|
17
|
+
import { NoObjectGeneratedError } from '../error/no-object-generated-error';
|
|
18
|
+
import {
|
|
19
|
+
FinishReason,
|
|
20
|
+
LanguageModelResponseMetadata,
|
|
21
|
+
LanguageModelUsage,
|
|
22
|
+
} from '../types';
|
|
23
|
+
import {
|
|
24
|
+
AsyncIterableStream,
|
|
25
|
+
createAsyncIterableStream,
|
|
26
|
+
} from '../util/async-iterable-stream';
|
|
27
|
+
import { DeepPartial } from '../util/deep-partial';
|
|
28
|
+
import { ObjectStreamPart } from './stream-object-result';
|
|
29
|
+
|
|
30
|
+
export interface OutputStrategy<PARTIAL, RESULT, ELEMENT_STREAM> {
|
|
31
|
+
readonly type: 'object' | 'array' | 'enum' | 'no-schema';
|
|
32
|
+
|
|
33
|
+
jsonSchema(): Promise<JSONSchema7 | undefined>;
|
|
34
|
+
|
|
35
|
+
validatePartialResult({
|
|
36
|
+
value,
|
|
37
|
+
textDelta,
|
|
38
|
+
isFinalDelta,
|
|
39
|
+
}: {
|
|
40
|
+
value: JSONValue;
|
|
41
|
+
textDelta: string;
|
|
42
|
+
isFirstDelta: boolean;
|
|
43
|
+
isFinalDelta: boolean;
|
|
44
|
+
latestObject: PARTIAL | undefined;
|
|
45
|
+
}): Promise<
|
|
46
|
+
ValidationResult<{
|
|
47
|
+
partial: PARTIAL;
|
|
48
|
+
textDelta: string;
|
|
49
|
+
}>
|
|
50
|
+
>;
|
|
51
|
+
validateFinalResult(
|
|
52
|
+
value: JSONValue | undefined,
|
|
53
|
+
context: {
|
|
54
|
+
text: string;
|
|
55
|
+
response: LanguageModelResponseMetadata;
|
|
56
|
+
usage: LanguageModelUsage;
|
|
57
|
+
},
|
|
58
|
+
): Promise<ValidationResult<RESULT>>;
|
|
59
|
+
|
|
60
|
+
createElementStream(
|
|
61
|
+
originalStream: ReadableStream<ObjectStreamPart<PARTIAL>>,
|
|
62
|
+
): ELEMENT_STREAM;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const noSchemaOutputStrategy: OutputStrategy<JSONValue, JSONValue, never> = {
|
|
66
|
+
type: 'no-schema',
|
|
67
|
+
jsonSchema: async () => undefined,
|
|
68
|
+
|
|
69
|
+
async validatePartialResult({ value, textDelta }) {
|
|
70
|
+
return { success: true, value: { partial: value, textDelta } };
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async validateFinalResult(
|
|
74
|
+
value: JSONValue | undefined,
|
|
75
|
+
context: {
|
|
76
|
+
text: string;
|
|
77
|
+
response: LanguageModelResponseMetadata;
|
|
78
|
+
usage: LanguageModelUsage;
|
|
79
|
+
finishReason: FinishReason;
|
|
80
|
+
},
|
|
81
|
+
): Promise<ValidationResult<JSONValue>> {
|
|
82
|
+
return value === undefined
|
|
83
|
+
? {
|
|
84
|
+
success: false,
|
|
85
|
+
error: new NoObjectGeneratedError({
|
|
86
|
+
message: 'No object generated: response did not match schema.',
|
|
87
|
+
text: context.text,
|
|
88
|
+
response: context.response,
|
|
89
|
+
usage: context.usage,
|
|
90
|
+
finishReason: context.finishReason,
|
|
91
|
+
}),
|
|
92
|
+
}
|
|
93
|
+
: { success: true, value };
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
createElementStream() {
|
|
97
|
+
throw new UnsupportedFunctionalityError({
|
|
98
|
+
functionality: 'element streams in no-schema mode',
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const objectOutputStrategy = <OBJECT>(
|
|
104
|
+
schema: Schema<OBJECT>,
|
|
105
|
+
): OutputStrategy<DeepPartial<OBJECT>, OBJECT, never> => ({
|
|
106
|
+
type: 'object',
|
|
107
|
+
jsonSchema: async () => await schema.jsonSchema,
|
|
108
|
+
|
|
109
|
+
async validatePartialResult({ value, textDelta }) {
|
|
110
|
+
return {
|
|
111
|
+
success: true,
|
|
112
|
+
value: {
|
|
113
|
+
// Note: currently no validation of partial results:
|
|
114
|
+
partial: value as DeepPartial<OBJECT>,
|
|
115
|
+
textDelta,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
async validateFinalResult(
|
|
121
|
+
value: JSONValue | undefined,
|
|
122
|
+
): Promise<ValidationResult<OBJECT>> {
|
|
123
|
+
return safeValidateTypes({ value, schema });
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
createElementStream() {
|
|
127
|
+
throw new UnsupportedFunctionalityError({
|
|
128
|
+
functionality: 'element streams in object mode',
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const arrayOutputStrategy = <ELEMENT>(
|
|
134
|
+
schema: Schema<ELEMENT>,
|
|
135
|
+
): OutputStrategy<ELEMENT[], ELEMENT[], AsyncIterableStream<ELEMENT>> => {
|
|
136
|
+
return {
|
|
137
|
+
type: 'array',
|
|
138
|
+
|
|
139
|
+
// wrap in object that contains array of elements, since most LLMs will not
|
|
140
|
+
// be able to generate an array directly:
|
|
141
|
+
// possible future optimization: use arrays directly when model supports grammar-guided generation
|
|
142
|
+
jsonSchema: async () => {
|
|
143
|
+
// remove $schema from schema.jsonSchema:
|
|
144
|
+
const { $schema, ...itemSchema } = await schema.jsonSchema;
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
elements: { type: 'array', items: itemSchema },
|
|
151
|
+
},
|
|
152
|
+
required: ['elements'],
|
|
153
|
+
additionalProperties: false,
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
async validatePartialResult({
|
|
158
|
+
value,
|
|
159
|
+
latestObject,
|
|
160
|
+
isFirstDelta,
|
|
161
|
+
isFinalDelta,
|
|
162
|
+
}) {
|
|
163
|
+
// check that the value is an object that contains an array of elements:
|
|
164
|
+
if (!isJSONObject(value) || !isJSONArray(value.elements)) {
|
|
165
|
+
return {
|
|
166
|
+
success: false,
|
|
167
|
+
error: new TypeValidationError({
|
|
168
|
+
value,
|
|
169
|
+
cause: 'value must be an object that contains an array of elements',
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const inputArray = value.elements as Array<JSONObject>;
|
|
175
|
+
const resultArray: Array<ELEMENT> = [];
|
|
176
|
+
|
|
177
|
+
for (let i = 0; i < inputArray.length; i++) {
|
|
178
|
+
const element = inputArray[i];
|
|
179
|
+
const result = await safeValidateTypes({ value: element, schema });
|
|
180
|
+
|
|
181
|
+
// special treatment for last processed element:
|
|
182
|
+
// ignore parse or validation failures, since they indicate that the
|
|
183
|
+
// last element is incomplete and should not be included in the result,
|
|
184
|
+
// unless it is the final delta
|
|
185
|
+
if (i === inputArray.length - 1 && !isFinalDelta) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!result.success) {
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
resultArray.push(result.value);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// calculate delta:
|
|
197
|
+
const publishedElementCount = latestObject?.length ?? 0;
|
|
198
|
+
|
|
199
|
+
let textDelta = '';
|
|
200
|
+
|
|
201
|
+
if (isFirstDelta) {
|
|
202
|
+
textDelta += '[';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (publishedElementCount > 0) {
|
|
206
|
+
textDelta += ',';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
textDelta += resultArray
|
|
210
|
+
.slice(publishedElementCount) // only new elements
|
|
211
|
+
.map(element => JSON.stringify(element))
|
|
212
|
+
.join(',');
|
|
213
|
+
|
|
214
|
+
if (isFinalDelta) {
|
|
215
|
+
textDelta += ']';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
success: true,
|
|
220
|
+
value: {
|
|
221
|
+
partial: resultArray,
|
|
222
|
+
textDelta,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async validateFinalResult(
|
|
228
|
+
value: JSONValue | undefined,
|
|
229
|
+
): Promise<ValidationResult<Array<ELEMENT>>> {
|
|
230
|
+
// check that the value is an object that contains an array of elements:
|
|
231
|
+
if (!isJSONObject(value) || !isJSONArray(value.elements)) {
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
error: new TypeValidationError({
|
|
235
|
+
value,
|
|
236
|
+
cause: 'value must be an object that contains an array of elements',
|
|
237
|
+
}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const inputArray = value.elements as Array<JSONObject>;
|
|
242
|
+
|
|
243
|
+
// check that each element in the array is of the correct type:
|
|
244
|
+
for (const element of inputArray) {
|
|
245
|
+
const result = await safeValidateTypes({ value: element, schema });
|
|
246
|
+
if (!result.success) {
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { success: true, value: inputArray as Array<ELEMENT> };
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
createElementStream(
|
|
255
|
+
originalStream: ReadableStream<ObjectStreamPart<ELEMENT[]>>,
|
|
256
|
+
) {
|
|
257
|
+
let publishedElements = 0;
|
|
258
|
+
|
|
259
|
+
return createAsyncIterableStream(
|
|
260
|
+
originalStream.pipeThrough(
|
|
261
|
+
new TransformStream<ObjectStreamPart<ELEMENT[]>, ELEMENT>({
|
|
262
|
+
transform(chunk, controller) {
|
|
263
|
+
switch (chunk.type) {
|
|
264
|
+
case 'object': {
|
|
265
|
+
const array = chunk.object;
|
|
266
|
+
|
|
267
|
+
// publish new elements one by one:
|
|
268
|
+
for (
|
|
269
|
+
;
|
|
270
|
+
publishedElements < array.length;
|
|
271
|
+
publishedElements++
|
|
272
|
+
) {
|
|
273
|
+
controller.enqueue(array[publishedElements]);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
case 'text-delta':
|
|
280
|
+
case 'finish':
|
|
281
|
+
case 'error': // suppress error (use onError instead)
|
|
282
|
+
break;
|
|
283
|
+
|
|
284
|
+
default: {
|
|
285
|
+
const _exhaustiveCheck: never = chunk;
|
|
286
|
+
throw new Error(
|
|
287
|
+
`Unsupported chunk type: ${_exhaustiveCheck}`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
),
|
|
294
|
+
);
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const enumOutputStrategy = <ENUM extends string>(
|
|
300
|
+
enumValues: Array<ENUM>,
|
|
301
|
+
): OutputStrategy<string, ENUM, never> => {
|
|
302
|
+
return {
|
|
303
|
+
type: 'enum',
|
|
304
|
+
|
|
305
|
+
// wrap in object that contains result, since most LLMs will not
|
|
306
|
+
// be able to generate an enum value directly:
|
|
307
|
+
// possible future optimization: use enums directly when model supports top-level enums
|
|
308
|
+
jsonSchema: async () => ({
|
|
309
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
310
|
+
type: 'object',
|
|
311
|
+
properties: {
|
|
312
|
+
result: { type: 'string', enum: enumValues },
|
|
313
|
+
},
|
|
314
|
+
required: ['result'],
|
|
315
|
+
additionalProperties: false,
|
|
316
|
+
}),
|
|
317
|
+
|
|
318
|
+
async validateFinalResult(
|
|
319
|
+
value: JSONValue | undefined,
|
|
320
|
+
): Promise<ValidationResult<ENUM>> {
|
|
321
|
+
// check that the value is an object that contains an array of elements:
|
|
322
|
+
if (!isJSONObject(value) || typeof value.result !== 'string') {
|
|
323
|
+
return {
|
|
324
|
+
success: false,
|
|
325
|
+
error: new TypeValidationError({
|
|
326
|
+
value,
|
|
327
|
+
cause:
|
|
328
|
+
'value must be an object that contains a string in the "result" property.',
|
|
329
|
+
}),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const result = value.result as string;
|
|
334
|
+
|
|
335
|
+
return enumValues.includes(result as ENUM)
|
|
336
|
+
? { success: true, value: result as ENUM }
|
|
337
|
+
: {
|
|
338
|
+
success: false,
|
|
339
|
+
error: new TypeValidationError({
|
|
340
|
+
value,
|
|
341
|
+
cause: 'value must be a string in the enum',
|
|
342
|
+
}),
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
async validatePartialResult({ value, textDelta }) {
|
|
347
|
+
if (!isJSONObject(value) || typeof value.result !== 'string') {
|
|
348
|
+
return {
|
|
349
|
+
success: false,
|
|
350
|
+
error: new TypeValidationError({
|
|
351
|
+
value,
|
|
352
|
+
cause:
|
|
353
|
+
'value must be an object that contains a string in the "result" property.',
|
|
354
|
+
}),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const result = value.result as string;
|
|
359
|
+
const possibleEnumValues = enumValues.filter(enumValue =>
|
|
360
|
+
enumValue.startsWith(result),
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
if (value.result.length === 0 || possibleEnumValues.length === 0) {
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
error: new TypeValidationError({
|
|
367
|
+
value,
|
|
368
|
+
cause: 'value must be a string in the enum',
|
|
369
|
+
}),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
success: true,
|
|
375
|
+
value: {
|
|
376
|
+
partial:
|
|
377
|
+
possibleEnumValues.length > 1 ? result : possibleEnumValues[0],
|
|
378
|
+
textDelta,
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
createElementStream() {
|
|
384
|
+
// no streaming in enum mode
|
|
385
|
+
throw new UnsupportedFunctionalityError({
|
|
386
|
+
functionality: 'element streams in enum mode',
|
|
387
|
+
});
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
export function getOutputStrategy<SCHEMA>({
|
|
393
|
+
output,
|
|
394
|
+
schema,
|
|
395
|
+
enumValues,
|
|
396
|
+
}: {
|
|
397
|
+
output: 'object' | 'array' | 'enum' | 'no-schema';
|
|
398
|
+
schema?: FlexibleSchema<SCHEMA>;
|
|
399
|
+
enumValues?: Array<SCHEMA>;
|
|
400
|
+
}): OutputStrategy<any, any, any> {
|
|
401
|
+
switch (output) {
|
|
402
|
+
case 'object':
|
|
403
|
+
return objectOutputStrategy(asSchema(schema!));
|
|
404
|
+
case 'array':
|
|
405
|
+
return arrayOutputStrategy(asSchema(schema!));
|
|
406
|
+
case 'enum':
|
|
407
|
+
return enumOutputStrategy(enumValues! as Array<string>);
|
|
408
|
+
case 'no-schema':
|
|
409
|
+
return noSchemaOutputStrategy;
|
|
410
|
+
default: {
|
|
411
|
+
const _exhaustiveCheck: never = output;
|
|
412
|
+
throw new Error(`Unsupported output: ${_exhaustiveCheck}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|