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,698 @@
|
|
|
1
|
+
import { fail } from 'assert';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { z } from 'zod/v4';
|
|
4
|
+
import { verifyNoObjectGeneratedError } from '../error/verify-no-object-generated-error';
|
|
5
|
+
import { array, choice, json, object, text } from './output';
|
|
6
|
+
|
|
7
|
+
const context = {
|
|
8
|
+
response: {
|
|
9
|
+
id: '123',
|
|
10
|
+
timestamp: new Date(),
|
|
11
|
+
modelId: '456',
|
|
12
|
+
},
|
|
13
|
+
usage: {
|
|
14
|
+
inputTokens: 1,
|
|
15
|
+
inputTokenDetails: {
|
|
16
|
+
noCacheTokens: 1,
|
|
17
|
+
cacheReadTokens: undefined,
|
|
18
|
+
cacheWriteTokens: undefined,
|
|
19
|
+
},
|
|
20
|
+
outputTokens: 2,
|
|
21
|
+
outputTokenDetails: {
|
|
22
|
+
reasoningTokens: undefined,
|
|
23
|
+
textTokens: 2,
|
|
24
|
+
},
|
|
25
|
+
totalTokens: 3,
|
|
26
|
+
reasoningTokens: undefined,
|
|
27
|
+
cachedInputTokens: undefined,
|
|
28
|
+
},
|
|
29
|
+
finishReason: 'length',
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
describe('Output.text', () => {
|
|
33
|
+
const text1 = text();
|
|
34
|
+
|
|
35
|
+
describe('responseFormat', () => {
|
|
36
|
+
it('should return the text as is', async () => {
|
|
37
|
+
const result = await text1.responseFormat;
|
|
38
|
+
expect(result).toMatchInlineSnapshot(`
|
|
39
|
+
{
|
|
40
|
+
"type": "text",
|
|
41
|
+
}
|
|
42
|
+
`);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('parseCompleteOutput', () => {
|
|
47
|
+
it('should return the text as is', async () => {
|
|
48
|
+
const result = await text1.parseCompleteOutput(
|
|
49
|
+
{ text: 'some output' },
|
|
50
|
+
context,
|
|
51
|
+
);
|
|
52
|
+
expect(result).toBe('some output');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle empty string', async () => {
|
|
56
|
+
const result = await text1.parseCompleteOutput({ text: '' }, context);
|
|
57
|
+
expect(result).toBe('');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle undefined as string "undefined"', async () => {
|
|
61
|
+
// Output.text() expects a string, so passing undefined would be a type error,
|
|
62
|
+
// but we cast for test purposes to ensure what happens
|
|
63
|
+
const result = await text1.parseCompleteOutput(
|
|
64
|
+
{ text: undefined as any },
|
|
65
|
+
context,
|
|
66
|
+
);
|
|
67
|
+
expect(result).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('parsePartialOutput', () => {
|
|
72
|
+
it('should return the string as partial', async () => {
|
|
73
|
+
const result = await text1.parsePartialOutput({ text: 'partial text' });
|
|
74
|
+
expect(result).toEqual({ partial: 'partial text' });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle empty string partial', async () => {
|
|
78
|
+
const result = await text1.parsePartialOutput({ text: '' });
|
|
79
|
+
expect(result).toEqual({ partial: '' });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('Output.object', () => {
|
|
85
|
+
const object1 = object({ schema: z.object({ content: z.string() }) });
|
|
86
|
+
|
|
87
|
+
describe('responseFormat', () => {
|
|
88
|
+
it('should return the JSON schema for the object', async () => {
|
|
89
|
+
const result = await object1.responseFormat;
|
|
90
|
+
expect(result).toMatchInlineSnapshot(`
|
|
91
|
+
{
|
|
92
|
+
"schema": {
|
|
93
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"content": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
"required": [
|
|
101
|
+
"content",
|
|
102
|
+
],
|
|
103
|
+
"type": "object",
|
|
104
|
+
},
|
|
105
|
+
"type": "json",
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should include name and description when provided', async () => {
|
|
111
|
+
const objectWithNameAndDesc = object({
|
|
112
|
+
schema: z.object({ content: z.string() }),
|
|
113
|
+
name: 'test-name',
|
|
114
|
+
description: 'test description',
|
|
115
|
+
});
|
|
116
|
+
const result = await objectWithNameAndDesc.responseFormat;
|
|
117
|
+
expect(result).toMatchInlineSnapshot(`
|
|
118
|
+
{
|
|
119
|
+
"description": "test description",
|
|
120
|
+
"name": "test-name",
|
|
121
|
+
"schema": {
|
|
122
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"properties": {
|
|
125
|
+
"content": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
"required": [
|
|
130
|
+
"content",
|
|
131
|
+
],
|
|
132
|
+
"type": "object",
|
|
133
|
+
},
|
|
134
|
+
"type": "json",
|
|
135
|
+
}
|
|
136
|
+
`);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('parseCompleteOutput', () => {
|
|
141
|
+
it('should parse the output of the model', async () => {
|
|
142
|
+
const result = await object1.parseCompleteOutput(
|
|
143
|
+
{ text: `{ "content": "test" }` },
|
|
144
|
+
context,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expect(result).toStrictEqual({ content: 'test' });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should throw NoObjectGeneratedError when parsing fails', async () => {
|
|
151
|
+
try {
|
|
152
|
+
await object1.parseCompleteOutput({ text: '{ broken json' }, context);
|
|
153
|
+
fail('must throw error');
|
|
154
|
+
} catch (error) {
|
|
155
|
+
verifyNoObjectGeneratedError(error, {
|
|
156
|
+
message: 'No object generated: could not parse the response.',
|
|
157
|
+
response: context.response,
|
|
158
|
+
usage: context.usage,
|
|
159
|
+
finishReason: context.finishReason,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should throw NoObjectGeneratedError when schema validation fails', async () => {
|
|
165
|
+
try {
|
|
166
|
+
await object1.parseCompleteOutput(
|
|
167
|
+
{ text: `{ "content": 123 }` },
|
|
168
|
+
context,
|
|
169
|
+
);
|
|
170
|
+
fail('must throw error');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
verifyNoObjectGeneratedError(error, {
|
|
173
|
+
message: 'No object generated: response did not match schema.',
|
|
174
|
+
response: context.response,
|
|
175
|
+
usage: context.usage,
|
|
176
|
+
finishReason: context.finishReason,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('parsePartialOutput', () => {
|
|
183
|
+
it('should return undefined for undefined input', async () => {
|
|
184
|
+
const result = await object1.parsePartialOutput({
|
|
185
|
+
text: undefined as any,
|
|
186
|
+
});
|
|
187
|
+
expect(result).toBeUndefined();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should return partial object for valid JSON', async () => {
|
|
191
|
+
const result = await object1.parsePartialOutput({
|
|
192
|
+
text: '{ "content": "test" }',
|
|
193
|
+
});
|
|
194
|
+
expect(result).toEqual({ partial: { content: 'test' } });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return partial object for repairable JSON', async () => {
|
|
198
|
+
const result = await object1.parsePartialOutput({
|
|
199
|
+
text: '{ "content": "test"',
|
|
200
|
+
});
|
|
201
|
+
expect(result).toEqual({ partial: { content: 'test' } });
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should handle partial object with missing closing brace', async () => {
|
|
205
|
+
const result = await object1.parsePartialOutput({
|
|
206
|
+
text: '{ "content": "partial", "count": 42',
|
|
207
|
+
});
|
|
208
|
+
expect(result).toEqual({ partial: { content: 'partial', count: 42 } });
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should handle partial array', async () => {
|
|
212
|
+
const arrayOutput = object({
|
|
213
|
+
schema: z.object({ items: z.array(z.string()) }),
|
|
214
|
+
});
|
|
215
|
+
const result = await arrayOutput.parsePartialOutput({
|
|
216
|
+
text: '{ "items": ["a", "b"',
|
|
217
|
+
});
|
|
218
|
+
expect(result).toEqual({ partial: { items: ['a', 'b'] } });
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should handle empty string input', async () => {
|
|
222
|
+
const result = await object1.parsePartialOutput({ text: '' });
|
|
223
|
+
expect(result).toBeUndefined();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should handle partial string value', async () => {
|
|
227
|
+
const result = await object1.parsePartialOutput({
|
|
228
|
+
text: '{ "content": "partial str',
|
|
229
|
+
});
|
|
230
|
+
expect(result).toEqual({ partial: { content: 'partial str' } });
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('Output.array', () => {
|
|
236
|
+
const array1 = array({ element: z.object({ content: z.string() }) });
|
|
237
|
+
|
|
238
|
+
describe('responseFormat', () => {
|
|
239
|
+
it('should return the JSON schema for the array', async () => {
|
|
240
|
+
const result = await array1.responseFormat;
|
|
241
|
+
expect(result).toMatchInlineSnapshot(`
|
|
242
|
+
{
|
|
243
|
+
"schema": {
|
|
244
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
245
|
+
"additionalProperties": false,
|
|
246
|
+
"properties": {
|
|
247
|
+
"elements": {
|
|
248
|
+
"items": {
|
|
249
|
+
"additionalProperties": false,
|
|
250
|
+
"properties": {
|
|
251
|
+
"content": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
"required": [
|
|
256
|
+
"content",
|
|
257
|
+
],
|
|
258
|
+
"type": "object",
|
|
259
|
+
},
|
|
260
|
+
"type": "array",
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
"required": [
|
|
264
|
+
"elements",
|
|
265
|
+
],
|
|
266
|
+
"type": "object",
|
|
267
|
+
},
|
|
268
|
+
"type": "json",
|
|
269
|
+
}
|
|
270
|
+
`);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should include name and description when provided', async () => {
|
|
274
|
+
const arrayWithNameAndDesc = array({
|
|
275
|
+
element: z.object({ content: z.string() }),
|
|
276
|
+
name: 'test-array-name',
|
|
277
|
+
description: 'test array description',
|
|
278
|
+
});
|
|
279
|
+
const result = await arrayWithNameAndDesc.responseFormat;
|
|
280
|
+
expect(result).toMatchInlineSnapshot(`
|
|
281
|
+
{
|
|
282
|
+
"description": "test array description",
|
|
283
|
+
"name": "test-array-name",
|
|
284
|
+
"schema": {
|
|
285
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
286
|
+
"additionalProperties": false,
|
|
287
|
+
"properties": {
|
|
288
|
+
"elements": {
|
|
289
|
+
"items": {
|
|
290
|
+
"additionalProperties": false,
|
|
291
|
+
"properties": {
|
|
292
|
+
"content": {
|
|
293
|
+
"type": "string",
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
"required": [
|
|
297
|
+
"content",
|
|
298
|
+
],
|
|
299
|
+
"type": "object",
|
|
300
|
+
},
|
|
301
|
+
"type": "array",
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
"required": [
|
|
305
|
+
"elements",
|
|
306
|
+
],
|
|
307
|
+
"type": "object",
|
|
308
|
+
},
|
|
309
|
+
"type": "json",
|
|
310
|
+
}
|
|
311
|
+
`);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe('parseCompleteOutput', () => {
|
|
316
|
+
it('should parse the output of the model', async () => {
|
|
317
|
+
const result = await array1.parseCompleteOutput(
|
|
318
|
+
{ text: `{ "elements": [{ "content": "test" }] }` },
|
|
319
|
+
context,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(result).toStrictEqual([{ content: 'test' }]);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should throw NoObjectGeneratedError when parsing fails', async () => {
|
|
326
|
+
try {
|
|
327
|
+
await array1.parseCompleteOutput({ text: '{ broken json' }, context);
|
|
328
|
+
fail('must throw error');
|
|
329
|
+
} catch (error) {
|
|
330
|
+
verifyNoObjectGeneratedError(error, {
|
|
331
|
+
message: 'No object generated: could not parse the response.',
|
|
332
|
+
response: context.response,
|
|
333
|
+
usage: context.usage,
|
|
334
|
+
finishReason: context.finishReason,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should throw NoObjectGeneratedError when schema validation fails', async () => {
|
|
340
|
+
try {
|
|
341
|
+
await array1.parseCompleteOutput(
|
|
342
|
+
{ text: `{ "elements": [{ "content": 123 }] }` },
|
|
343
|
+
context,
|
|
344
|
+
);
|
|
345
|
+
fail('must throw error');
|
|
346
|
+
} catch (error) {
|
|
347
|
+
verifyNoObjectGeneratedError(error, {
|
|
348
|
+
message: 'No object generated: response did not match schema.',
|
|
349
|
+
response: context.response,
|
|
350
|
+
usage: context.usage,
|
|
351
|
+
finishReason: context.finishReason,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('array.parsePartialOutput', () => {
|
|
358
|
+
it('should parse partial output successfully for successful-parse', async () => {
|
|
359
|
+
const partial = await array1.parsePartialOutput({
|
|
360
|
+
text: `{ "elements": [{ "content": "a" }, { "content": "b" }] }`,
|
|
361
|
+
});
|
|
362
|
+
expect(partial).toEqual({
|
|
363
|
+
partial: [{ content: 'a' }, { content: 'b' }],
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should parse partial output successfully for repaired-parse (returns all but last element)', async () => {
|
|
368
|
+
// Simulate an incomplete last element (at the end, missing " }")
|
|
369
|
+
const partial = await array1.parsePartialOutput({
|
|
370
|
+
text: `{ "elements": [{ "content": "a" }, { "content": "b" }`,
|
|
371
|
+
});
|
|
372
|
+
// Should only return [{ content: "a" }]
|
|
373
|
+
expect(partial).toEqual({
|
|
374
|
+
partial: [{ content: 'a' }],
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should return undefined for failed-parse', async () => {
|
|
379
|
+
const partial = await array1.parsePartialOutput({
|
|
380
|
+
text: '{ not valid json',
|
|
381
|
+
});
|
|
382
|
+
expect(partial).toBeUndefined();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should return undefined when input is undefined', async () => {
|
|
386
|
+
const partial = await array1.parsePartialOutput({
|
|
387
|
+
text: undefined as any,
|
|
388
|
+
});
|
|
389
|
+
expect(partial).toBeUndefined();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should return undefined if elements is missing', async () => {
|
|
393
|
+
// "elements" property is missing
|
|
394
|
+
const partial = await array1.parsePartialOutput({
|
|
395
|
+
text: `{ "foo": [1,2,3] }`,
|
|
396
|
+
});
|
|
397
|
+
expect(partial).toBeUndefined();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should return undefined if elements is not an array', async () => {
|
|
401
|
+
// "elements" property exists but is not an array
|
|
402
|
+
const partial = await array1.parsePartialOutput({
|
|
403
|
+
text: `{ "elements": "not-an-array" }`,
|
|
404
|
+
});
|
|
405
|
+
expect(partial).toBeUndefined();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should handle an empty array of elements', async () => {
|
|
409
|
+
const partial = await array1.parsePartialOutput({
|
|
410
|
+
text: `{ "elements": [] }`,
|
|
411
|
+
});
|
|
412
|
+
expect(partial).toEqual({ partial: [] });
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe('Output.choice', () => {
|
|
418
|
+
const choice1 = choice({
|
|
419
|
+
options: ['aaa', 'aab', 'ccc'],
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe('responseFormat', () => {
|
|
423
|
+
it('should return the JSON schema for the choice', async () => {
|
|
424
|
+
const result = await choice1.responseFormat;
|
|
425
|
+
expect(result).toMatchInlineSnapshot(`
|
|
426
|
+
{
|
|
427
|
+
"schema": {
|
|
428
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
429
|
+
"additionalProperties": false,
|
|
430
|
+
"properties": {
|
|
431
|
+
"result": {
|
|
432
|
+
"enum": [
|
|
433
|
+
"aaa",
|
|
434
|
+
"aab",
|
|
435
|
+
"ccc",
|
|
436
|
+
],
|
|
437
|
+
"type": "string",
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
"required": [
|
|
441
|
+
"result",
|
|
442
|
+
],
|
|
443
|
+
"type": "object",
|
|
444
|
+
},
|
|
445
|
+
"type": "json",
|
|
446
|
+
}
|
|
447
|
+
`);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should include name and description when provided', async () => {
|
|
451
|
+
const choiceWithNameAndDesc = choice({
|
|
452
|
+
options: ['aaa', 'aab', 'ccc'],
|
|
453
|
+
name: 'test-choice-name',
|
|
454
|
+
description: 'test choice description',
|
|
455
|
+
});
|
|
456
|
+
const result = await choiceWithNameAndDesc.responseFormat;
|
|
457
|
+
expect(result).toMatchInlineSnapshot(`
|
|
458
|
+
{
|
|
459
|
+
"description": "test choice description",
|
|
460
|
+
"name": "test-choice-name",
|
|
461
|
+
"schema": {
|
|
462
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
463
|
+
"additionalProperties": false,
|
|
464
|
+
"properties": {
|
|
465
|
+
"result": {
|
|
466
|
+
"enum": [
|
|
467
|
+
"aaa",
|
|
468
|
+
"aab",
|
|
469
|
+
"ccc",
|
|
470
|
+
],
|
|
471
|
+
"type": "string",
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
"required": [
|
|
475
|
+
"result",
|
|
476
|
+
],
|
|
477
|
+
"type": "object",
|
|
478
|
+
},
|
|
479
|
+
"type": "json",
|
|
480
|
+
}
|
|
481
|
+
`);
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('parseCompleteOutput', () => {
|
|
486
|
+
it('should parse a valid choice output', async () => {
|
|
487
|
+
const result = await choice1.parseCompleteOutput(
|
|
488
|
+
{ text: `{ "result": "aaa" }` },
|
|
489
|
+
context,
|
|
490
|
+
);
|
|
491
|
+
expect(result).toBe('aaa');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('should throw NoObjectGeneratedError if JSON is invalid', async () => {
|
|
495
|
+
await expect(
|
|
496
|
+
choice1.parseCompleteOutput({ text: '{ broken json' }, context),
|
|
497
|
+
).rejects.toThrowError(
|
|
498
|
+
'No object generated: could not parse the response.',
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should throw NoObjectGeneratedError if result is missing', async () => {
|
|
503
|
+
await expect(
|
|
504
|
+
choice1.parseCompleteOutput({ text: `{}` }, context),
|
|
505
|
+
).rejects.toThrowError(
|
|
506
|
+
'No object generated: response did not match schema.',
|
|
507
|
+
);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('should throw NoObjectGeneratedError if result value is not a valid choice', async () => {
|
|
511
|
+
await expect(
|
|
512
|
+
choice1.parseCompleteOutput({ text: `{ "result": "d" }` }, context),
|
|
513
|
+
).rejects.toThrowError(
|
|
514
|
+
'No object generated: response did not match schema.',
|
|
515
|
+
);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('should throw NoObjectGeneratedError if result is not a string', async () => {
|
|
519
|
+
await expect(
|
|
520
|
+
choice1.parseCompleteOutput({ text: `{ "result": 5 }` }, context),
|
|
521
|
+
).rejects.toThrowError(
|
|
522
|
+
'No object generated: response did not match schema.',
|
|
523
|
+
);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should throw NoObjectGeneratedError if top-level is not an object', async () => {
|
|
527
|
+
await expect(
|
|
528
|
+
choice1.parseCompleteOutput({ text: `"a"` }, context),
|
|
529
|
+
).rejects.toThrowError(
|
|
530
|
+
'No object generated: response did not match schema.',
|
|
531
|
+
);
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
describe('parsePartialOutput', () => {
|
|
536
|
+
it('should parse a valid exact partial choice output', async () => {
|
|
537
|
+
const result = await choice1.parsePartialOutput({
|
|
538
|
+
text: `{ "result": "aaa" }`,
|
|
539
|
+
});
|
|
540
|
+
expect(result).toEqual({ partial: 'aaa' });
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('should return undefined if JSON is invalid', async () => {
|
|
544
|
+
const result = await choice1.parsePartialOutput({
|
|
545
|
+
text: '{ broken json',
|
|
546
|
+
});
|
|
547
|
+
expect(result).toBeUndefined();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('should return undefined if result is missing', async () => {
|
|
551
|
+
const result = await choice1.parsePartialOutput({ text: `{}` });
|
|
552
|
+
expect(result).toBeUndefined();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('should return ambiguous if result is a prefix matching multiple options (repaired-parse)', async () => {
|
|
556
|
+
const result = await choice1.parsePartialOutput({
|
|
557
|
+
text: `{ "result": "`,
|
|
558
|
+
});
|
|
559
|
+
expect(result).toBeUndefined();
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('should return the single matching partial if only one option matches (repaired-parse)', async () => {
|
|
563
|
+
const result = await choice1.parsePartialOutput({
|
|
564
|
+
text: `{ "result": "c`,
|
|
565
|
+
});
|
|
566
|
+
expect(result).toEqual({ partial: 'ccc' });
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it('should return undefined if result does not match any choice', async () => {
|
|
570
|
+
const result = await choice1.parsePartialOutput({
|
|
571
|
+
text: `{ "result": "z" }`,
|
|
572
|
+
});
|
|
573
|
+
expect(result).toBeUndefined();
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('should return undefined if result is not a string', async () => {
|
|
577
|
+
const result = await choice1.parsePartialOutput({
|
|
578
|
+
text: `{ "result": 5 }`,
|
|
579
|
+
});
|
|
580
|
+
expect(result).toBeUndefined();
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('should return undefined if top-level is not an object', async () => {
|
|
584
|
+
const result = await choice1.parsePartialOutput({ text: `"a"` });
|
|
585
|
+
expect(result).toBeUndefined();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should return full match as partial for "successful-parse" and valid choice', async () => {
|
|
589
|
+
const result = await choice1.parsePartialOutput({
|
|
590
|
+
text: `{ "result": "aab" }`,
|
|
591
|
+
});
|
|
592
|
+
expect(result).toEqual({ partial: 'aab' });
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("should return undefined for an incomplete prefix that doesn't match any choice", async () => {
|
|
596
|
+
const result = await choice1.parsePartialOutput({
|
|
597
|
+
text: `{ "result": "x" }`,
|
|
598
|
+
});
|
|
599
|
+
expect(result).toBeUndefined();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('should return undefined for a prefix matching multiple options', async () => {
|
|
603
|
+
const result = await choice1.parsePartialOutput({
|
|
604
|
+
text: `{ "result": "a`,
|
|
605
|
+
});
|
|
606
|
+
expect(result).toBeUndefined();
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('should return undefined if partial result is null', async () => {
|
|
610
|
+
const result = await choice1.parsePartialOutput({ text: `null` });
|
|
611
|
+
expect(result).toBeUndefined();
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
describe('Output.json', () => {
|
|
616
|
+
const json1 = json();
|
|
617
|
+
|
|
618
|
+
describe('responseFormat', () => {
|
|
619
|
+
it('should return json responseFormat', async () => {
|
|
620
|
+
const result = await json1.responseFormat;
|
|
621
|
+
expect(result).toMatchInlineSnapshot(`
|
|
622
|
+
{
|
|
623
|
+
"type": "json",
|
|
624
|
+
}
|
|
625
|
+
`);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('should include name and description when provided', async () => {
|
|
629
|
+
const jsonWithNameAndDesc = json({
|
|
630
|
+
name: 'test-json-name',
|
|
631
|
+
description: 'test json description',
|
|
632
|
+
});
|
|
633
|
+
const result = await jsonWithNameAndDesc.responseFormat;
|
|
634
|
+
expect(result).toMatchInlineSnapshot(`
|
|
635
|
+
{
|
|
636
|
+
"description": "test json description",
|
|
637
|
+
"name": "test-json-name",
|
|
638
|
+
"type": "json",
|
|
639
|
+
}
|
|
640
|
+
`);
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
describe('parseCompleteOutput', () => {
|
|
645
|
+
it('should parse valid JSON', async () => {
|
|
646
|
+
const result = await json1.parseCompleteOutput(
|
|
647
|
+
{ text: `{"a": 1, "b": [2,3]}` },
|
|
648
|
+
context,
|
|
649
|
+
);
|
|
650
|
+
expect(result).toEqual({ a: 1, b: [2, 3] });
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('should throw if JSON is invalid', async () => {
|
|
654
|
+
await expect(() =>
|
|
655
|
+
json1.parseCompleteOutput({ text: `{ a: 1 }` }, context),
|
|
656
|
+
).rejects.toThrow('No object generated: could not parse the response.');
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('should throw if JSON is just text', async () => {
|
|
660
|
+
await expect(() =>
|
|
661
|
+
json1.parseCompleteOutput({ text: `foo` }, context),
|
|
662
|
+
).rejects.toThrow('No object generated: could not parse the response.');
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
describe('parsePartialOutput', () => {
|
|
667
|
+
it('should parse partial valid JSON (successful-parse)', async () => {
|
|
668
|
+
const result = await json1.parsePartialOutput({
|
|
669
|
+
text: `{ "foo": 1, "bar": [2, 3] }`,
|
|
670
|
+
});
|
|
671
|
+
expect(result).toEqual({ partial: { foo: 1, bar: [2, 3] } });
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('should parse partial valid JSON (repaired-parse)', async () => {
|
|
675
|
+
// simulate incomplete/repaired but still valid
|
|
676
|
+
const result = await json1.parsePartialOutput({ text: `{ "foo": 123` });
|
|
677
|
+
// Since parsePartialJson may not be able to repair this, just check it's undefined or a value
|
|
678
|
+
expect([undefined, { partial: expect.anything() }]).toContainEqual(
|
|
679
|
+
result,
|
|
680
|
+
);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('should return undefined for invalid partial', async () => {
|
|
684
|
+
const result = await json1.parsePartialOutput({ text: `invalid!` });
|
|
685
|
+
expect(result).toBeUndefined();
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('should return undefined for undefined input', async () => {
|
|
689
|
+
const result = await json1.parsePartialOutput({ text: `` });
|
|
690
|
+
expect(result).toBeUndefined();
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it('should return undefined if parsed value is undefined', async () => {
|
|
694
|
+
const result = await json1.parsePartialOutput({ text: `undefined` });
|
|
695
|
+
expect(result).toBeUndefined();
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
});
|