@copilotkit/aimock 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +24 -4
- package/dist/_virtual/_rolldown/runtime.cjs +2 -0
- package/dist/_virtual/_rolldown/runtime.js +29 -0
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/aimock-cli.cjs +16 -0
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts +2 -0
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts +2 -0
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +16 -0
- package/dist/aimock-cli.js.map +1 -1
- package/dist/bedrock-converse.cjs +2 -0
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +2 -0
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +2 -0
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +2 -0
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +1 -0
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.js +1 -0
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/convert-mockllm.cjs +232 -0
- package/dist/convert-mockllm.cjs.map +1 -0
- package/dist/convert-mockllm.js +230 -0
- package/dist/convert-mockllm.js.map +1 -0
- package/dist/convert-vidaimock.cjs +110 -0
- package/dist/convert-vidaimock.cjs.map +1 -0
- package/dist/convert-vidaimock.js +108 -0
- package/dist/convert-vidaimock.js.map +1 -0
- package/dist/convert.cjs +158 -0
- package/dist/convert.cjs.map +1 -0
- package/dist/convert.d.cts +16 -0
- package/dist/convert.d.cts.map +1 -0
- package/dist/convert.d.ts +16 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +157 -0
- package/dist/convert.js.map +1 -0
- package/dist/embeddings.cjs +2 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.js +2 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/gemini.cjs +1 -0
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.js +1 -0
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +16 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +6 -2
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +6 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +13 -1
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +166 -0
- package/dist/images.cjs.map +1 -0
- package/dist/images.d.cts +11 -0
- package/dist/images.d.cts.map +1 -0
- package/dist/images.d.ts +11 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +166 -0
- package/dist/images.js.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +6 -2
- package/dist/jest.cjs +70 -0
- package/dist/jest.cjs.map +1 -0
- package/dist/jest.d.cts +33 -0
- package/dist/jest.d.cts.map +1 -0
- package/dist/jest.d.ts +33 -0
- package/dist/jest.d.ts.map +1 -0
- package/dist/jest.js +67 -0
- package/dist/jest.js.map +1 -0
- package/dist/llmock.cjs +37 -1
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +5 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +5 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +37 -1
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +1 -0
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +1 -0
- package/dist/messages.js.map +1 -1
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.2.4/node_modules/@vitest/pretty-format/dist/index.cjs +934 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.2.4/node_modules/@vitest/pretty-format/dist/index.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.2.4/node_modules/@vitest/pretty-format/dist/index.js +934 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.2.4/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.cjs +1051 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.js +1042 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/index.cjs +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.2.4/node_modules/@vitest/runner/dist/index.js +3 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.cjs +96 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +93 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/helpers.cjs +49 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/helpers.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/helpers.js +43 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/index.cjs +456 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/index.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/index.js +456 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/source-map.cjs +170 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/source-map.cjs.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/source-map.js +169 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.2.4/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/.pnpm/js-tokens@9.0.1/node_modules/js-tokens/index.cjs +388 -0
- package/dist/node_modules/.pnpm/js-tokens@9.0.1/node_modules/js-tokens/index.cjs.map +1 -0
- package/dist/node_modules/.pnpm/js-tokens@9.0.1/node_modules/js-tokens/index.js +385 -0
- package/dist/node_modules/.pnpm/js-tokens@9.0.1/node_modules/js-tokens/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.cjs +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.cjs +17 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +17 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.cjs +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.cjs +16 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +16 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.cjs +14 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +14 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.cjs +35 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +35 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.cjs +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.cjs +128 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +123 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.cjs +41 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +40 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.cjs +100 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +100 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.cjs +26 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +26 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.cjs +15 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +15 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.cjs +22 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +22 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.cjs +7 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.cjs +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.cjs +19 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +19 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.cjs +26 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +26 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.cjs +10 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.cjs +31 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.cjs.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +31 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
- package/dist/node_modules/.pnpm/strip-literal@3.1.0/node_modules/strip-literal/dist/index.cjs +52 -0
- package/dist/node_modules/.pnpm/strip-literal@3.1.0/node_modules/strip-literal/dist/index.cjs.map +1 -0
- package/dist/node_modules/.pnpm/strip-literal@3.1.0/node_modules/strip-literal/dist/index.js +52 -0
- package/dist/node_modules/.pnpm/strip-literal@3.1.0/node_modules/strip-literal/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.cjs +83 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.cjs.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.cjs +10 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.cjs.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.js +10 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
- package/dist/ollama.cjs +2 -0
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +2 -0
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +50 -7
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +50 -7
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +1 -0
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.js +1 -0
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +8 -0
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +9 -0
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +80 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -0
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +80 -3
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +144 -0
- package/dist/speech.cjs.map +1 -0
- package/dist/speech.d.cts +11 -0
- package/dist/speech.d.cts.map +1 -0
- package/dist/speech.d.ts +11 -0
- package/dist/speech.d.ts.map +1 -0
- package/dist/speech.js +144 -0
- package/dist/speech.js.map +1 -0
- package/dist/transcription.cjs +134 -0
- package/dist/transcription.cjs.map +1 -0
- package/dist/transcription.d.cts +11 -0
- package/dist/transcription.d.cts.map +1 -0
- package/dist/transcription.d.ts +11 -0
- package/dist/transcription.d.ts.map +1 -0
- package/dist/transcription.js +134 -0
- package/dist/transcription.js.map +1 -0
- package/dist/types.d.cts +44 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +44 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/video.cjs +196 -0
- package/dist/video.cjs.map +1 -0
- package/dist/video.d.cts +14 -0
- package/dist/video.d.cts.map +1 -0
- package/dist/video.d.ts +14 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +195 -0
- package/dist/video.js.map +1 -0
- package/dist/vitest.cjs +80 -0
- package/dist/vitest.cjs.map +1 -0
- package/dist/vitest.d.cts +30 -0
- package/dist/vitest.d.cts.map +1 -0
- package/dist/vitest.d.ts +30 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +77 -0
- package/dist/vitest.js.map +1 -0
- package/fixtures/examples/a2a/a2a-config.json +42 -0
- package/fixtures/examples/agui/agui-text-response.json +35 -0
- package/fixtures/examples/chaos/chaos-config.json +10 -0
- package/fixtures/examples/full-suite.json +116 -0
- package/fixtures/examples/llm/embeddings.json +10 -0
- package/fixtures/examples/llm/error-injection.json +15 -0
- package/fixtures/examples/llm/sequential-responses.json +20 -0
- package/fixtures/examples/llm/streaming-physics.json +15 -0
- package/fixtures/examples/mcp/mcp-config.json +26 -0
- package/fixtures/examples/record-replay/record-config.json +11 -0
- package/fixtures/examples/vector/vector-config.json +34 -0
- package/package.json +60 -2
package/dist/cohere.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cohere.cjs","names":["generateMessageId","generateToolCallId","calculateDelay","delay","flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isTextResponse","createInterruptionSignal","isToolCallResponse"],"sources":["../src/cohere.ts"],"sourcesContent":["/**\n * Cohere v2 Chat API endpoint support.\n *\n * Translates incoming /v2/chat requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * Cohere's typed SSE streaming (or non-streaming) format.\n *\n * Cohere uses typed SSE events (event: + data: lines), similar to the\n * Claude Messages handler in messages.ts.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolCallId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Cohere v2 Chat request types ───────────────────────────────────────────\n\ninterface CohereMessage {\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n content: string;\n tool_call_id?: string;\n}\n\ninterface CohereToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface CohereRequest {\n model: string;\n messages: CohereMessage[];\n stream?: boolean;\n tools?: CohereToolDef[];\n response_format?: { type: string; json_schema?: object };\n}\n\n// ─── Cohere SSE event types ─────────────────────────────────────────────────\n\ninterface CohereSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n// ─── Zero-value usage block ─────────────────────────────────────────────────\n\nconst ZERO_USAGE = {\n billed_units: { input_tokens: 0, output_tokens: 0, search_units: 0, classifications: 0 },\n tokens: { input_tokens: 0, output_tokens: 0 },\n};\n\n// ─── Input conversion: Cohere → ChatCompletionRequest ───────────────────────\n\nexport function cohereToCompletionRequest(req: CohereRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n messages.push({ role: \"system\", content: msg.content });\n } else if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (msg.role === \"tool\") {\n messages.push({\n role: \"tool\",\n content: msg.content,\n tool_call_id: msg.tool_call_id,\n });\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n tools,\n };\n}\n\n// ─── Response building: fixture → Cohere v2 Chat format ─────────────────────\n\n// Non-streaming text response\nfunction buildCohereTextResponse(content: string): object {\n return {\n id: generateMessageId(),\n finish_reason: \"COMPLETE\",\n message: {\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n tool_calls: [],\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// Non-streaming tool call response\nfunction buildCohereToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n const cohereCalls = toolCalls.map((tc) => {\n // Validate arguments JSON\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n }\n return {\n id: tc.id || generateToolCallId(),\n type: \"function\",\n function: {\n name: tc.name,\n arguments: tc.arguments || \"{}\",\n },\n };\n });\n\n return {\n id: generateMessageId(),\n finish_reason: \"TOOL_CALL\",\n message: {\n role: \"assistant\",\n content: [],\n tool_calls: cohereCalls,\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nfunction buildCohereTextStreamEvents(content: string, chunkSize: number): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // content-start (type: \"text\" only, no text field)\n events.push({\n type: \"content-start\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\" },\n },\n },\n });\n\n // content-delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content-delta\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\", text: slice },\n },\n },\n });\n }\n\n // content-end\n events.push({\n type: \"content-end\",\n index: 0,\n });\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"COMPLETE\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\nfunction buildCohereToolCallStreamEvents(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // tool-plan-delta\n events.push({\n type: \"tool-plan-delta\",\n delta: {\n message: {\n tool_plan: \"I will use the requested tool.\",\n },\n },\n });\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n\n // Validate arguments JSON\n let argsJson: string;\n try {\n JSON.parse(tc.arguments || \"{}\");\n argsJson = tc.arguments || \"{}\";\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsJson = \"{}\";\n }\n\n // tool-call-start\n events.push({\n type: \"tool-call-start\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n id: callId,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: \"\",\n },\n },\n },\n },\n });\n\n // tool-call-delta — chunked arguments\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"tool-call-delta\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n function: {\n arguments: slice,\n },\n },\n },\n },\n });\n }\n\n // tool-call-end\n events.push({\n type: \"tool-call-end\",\n index: idx,\n });\n }\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"TOOL_CALL\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\n// ─── SSE writer for Cohere typed events ─────────────────────────────────────\n\ninterface CohereStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeCohereSSEStream(\n res: http.ServerResponse,\n events: CohereSSEEvent[],\n optionsOrLatency?: number | CohereStreamOptions,\n): Promise<boolean> {\n const opts: CohereStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleCohere(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let cohereReq: CohereRequest;\n try {\n cohereReq = JSON.parse(raw) as CohereRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Validate required model field\n if (!cohereReq.model) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"model is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!cohereReq.messages || !Array.isArray(cohereReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = cohereToCompletionRequest(cohereReq);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"cohere\",\n req.url ?? \"/v2/chat\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v2/chat\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereTextStreamEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cohere.cjs","names":["generateMessageId","generateToolCallId","calculateDelay","delay","flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isTextResponse","createInterruptionSignal","isToolCallResponse"],"sources":["../src/cohere.ts"],"sourcesContent":["/**\n * Cohere v2 Chat API endpoint support.\n *\n * Translates incoming /v2/chat requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * Cohere's typed SSE streaming (or non-streaming) format.\n *\n * Cohere uses typed SSE events (event: + data: lines), similar to the\n * Claude Messages handler in messages.ts.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolCallId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Cohere v2 Chat request types ───────────────────────────────────────────\n\ninterface CohereMessage {\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n content: string;\n tool_call_id?: string;\n}\n\ninterface CohereToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface CohereRequest {\n model: string;\n messages: CohereMessage[];\n stream?: boolean;\n tools?: CohereToolDef[];\n response_format?: { type: string; json_schema?: object };\n}\n\n// ─── Cohere SSE event types ─────────────────────────────────────────────────\n\ninterface CohereSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n// ─── Zero-value usage block ─────────────────────────────────────────────────\n\nconst ZERO_USAGE = {\n billed_units: { input_tokens: 0, output_tokens: 0, search_units: 0, classifications: 0 },\n tokens: { input_tokens: 0, output_tokens: 0 },\n};\n\n// ─── Input conversion: Cohere → ChatCompletionRequest ───────────────────────\n\nexport function cohereToCompletionRequest(req: CohereRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n messages.push({ role: \"system\", content: msg.content });\n } else if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (msg.role === \"tool\") {\n messages.push({\n role: \"tool\",\n content: msg.content,\n tool_call_id: msg.tool_call_id,\n });\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n tools,\n };\n}\n\n// ─── Response building: fixture → Cohere v2 Chat format ─────────────────────\n\n// Non-streaming text response\nfunction buildCohereTextResponse(content: string): object {\n return {\n id: generateMessageId(),\n finish_reason: \"COMPLETE\",\n message: {\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n tool_calls: [],\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// Non-streaming tool call response\nfunction buildCohereToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n const cohereCalls = toolCalls.map((tc) => {\n // Validate arguments JSON\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n }\n return {\n id: tc.id || generateToolCallId(),\n type: \"function\",\n function: {\n name: tc.name,\n arguments: tc.arguments || \"{}\",\n },\n };\n });\n\n return {\n id: generateMessageId(),\n finish_reason: \"TOOL_CALL\",\n message: {\n role: \"assistant\",\n content: [],\n tool_calls: cohereCalls,\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nfunction buildCohereTextStreamEvents(content: string, chunkSize: number): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // content-start (type: \"text\" only, no text field)\n events.push({\n type: \"content-start\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\" },\n },\n },\n });\n\n // content-delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content-delta\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\", text: slice },\n },\n },\n });\n }\n\n // content-end\n events.push({\n type: \"content-end\",\n index: 0,\n });\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"COMPLETE\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\nfunction buildCohereToolCallStreamEvents(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // tool-plan-delta\n events.push({\n type: \"tool-plan-delta\",\n delta: {\n message: {\n tool_plan: \"I will use the requested tool.\",\n },\n },\n });\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n\n // Validate arguments JSON\n let argsJson: string;\n try {\n JSON.parse(tc.arguments || \"{}\");\n argsJson = tc.arguments || \"{}\";\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsJson = \"{}\";\n }\n\n // tool-call-start\n events.push({\n type: \"tool-call-start\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n id: callId,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: \"\",\n },\n },\n },\n },\n });\n\n // tool-call-delta — chunked arguments\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"tool-call-delta\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n function: {\n arguments: slice,\n },\n },\n },\n },\n });\n }\n\n // tool-call-end\n events.push({\n type: \"tool-call-end\",\n index: idx,\n });\n }\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"TOOL_CALL\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\n// ─── SSE writer for Cohere typed events ─────────────────────────────────────\n\ninterface CohereStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeCohereSSEStream(\n res: http.ServerResponse,\n events: CohereSSEEvent[],\n optionsOrLatency?: number | CohereStreamOptions,\n): Promise<boolean> {\n const opts: CohereStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleCohere(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let cohereReq: CohereRequest;\n try {\n cohereReq = JSON.parse(raw) as CohereRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Validate required model field\n if (!cohereReq.model) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"model is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!cohereReq.messages || !Array.isArray(cohereReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = cohereToCompletionRequest(cohereReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"cohere\",\n req.url ?? \"/v2/chat\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v2/chat\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereTextStreamEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
|
package/dist/cohere.js
CHANGED
|
@@ -287,6 +287,7 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
|
|
|
287
287
|
return;
|
|
288
288
|
}
|
|
289
289
|
const completionReq = cohereToCompletionRequest(cohereReq);
|
|
290
|
+
completionReq._endpointType = "chat";
|
|
290
291
|
const testId = getTestId(req);
|
|
291
292
|
const fixture = matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
292
293
|
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
package/dist/cohere.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cohere.js","names":[],"sources":["../src/cohere.ts"],"sourcesContent":["/**\n * Cohere v2 Chat API endpoint support.\n *\n * Translates incoming /v2/chat requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * Cohere's typed SSE streaming (or non-streaming) format.\n *\n * Cohere uses typed SSE events (event: + data: lines), similar to the\n * Claude Messages handler in messages.ts.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolCallId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Cohere v2 Chat request types ───────────────────────────────────────────\n\ninterface CohereMessage {\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n content: string;\n tool_call_id?: string;\n}\n\ninterface CohereToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface CohereRequest {\n model: string;\n messages: CohereMessage[];\n stream?: boolean;\n tools?: CohereToolDef[];\n response_format?: { type: string; json_schema?: object };\n}\n\n// ─── Cohere SSE event types ─────────────────────────────────────────────────\n\ninterface CohereSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n// ─── Zero-value usage block ─────────────────────────────────────────────────\n\nconst ZERO_USAGE = {\n billed_units: { input_tokens: 0, output_tokens: 0, search_units: 0, classifications: 0 },\n tokens: { input_tokens: 0, output_tokens: 0 },\n};\n\n// ─── Input conversion: Cohere → ChatCompletionRequest ───────────────────────\n\nexport function cohereToCompletionRequest(req: CohereRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n messages.push({ role: \"system\", content: msg.content });\n } else if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (msg.role === \"tool\") {\n messages.push({\n role: \"tool\",\n content: msg.content,\n tool_call_id: msg.tool_call_id,\n });\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n tools,\n };\n}\n\n// ─── Response building: fixture → Cohere v2 Chat format ─────────────────────\n\n// Non-streaming text response\nfunction buildCohereTextResponse(content: string): object {\n return {\n id: generateMessageId(),\n finish_reason: \"COMPLETE\",\n message: {\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n tool_calls: [],\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// Non-streaming tool call response\nfunction buildCohereToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n const cohereCalls = toolCalls.map((tc) => {\n // Validate arguments JSON\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n }\n return {\n id: tc.id || generateToolCallId(),\n type: \"function\",\n function: {\n name: tc.name,\n arguments: tc.arguments || \"{}\",\n },\n };\n });\n\n return {\n id: generateMessageId(),\n finish_reason: \"TOOL_CALL\",\n message: {\n role: \"assistant\",\n content: [],\n tool_calls: cohereCalls,\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nfunction buildCohereTextStreamEvents(content: string, chunkSize: number): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // content-start (type: \"text\" only, no text field)\n events.push({\n type: \"content-start\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\" },\n },\n },\n });\n\n // content-delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content-delta\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\", text: slice },\n },\n },\n });\n }\n\n // content-end\n events.push({\n type: \"content-end\",\n index: 0,\n });\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"COMPLETE\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\nfunction buildCohereToolCallStreamEvents(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // tool-plan-delta\n events.push({\n type: \"tool-plan-delta\",\n delta: {\n message: {\n tool_plan: \"I will use the requested tool.\",\n },\n },\n });\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n\n // Validate arguments JSON\n let argsJson: string;\n try {\n JSON.parse(tc.arguments || \"{}\");\n argsJson = tc.arguments || \"{}\";\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsJson = \"{}\";\n }\n\n // tool-call-start\n events.push({\n type: \"tool-call-start\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n id: callId,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: \"\",\n },\n },\n },\n },\n });\n\n // tool-call-delta — chunked arguments\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"tool-call-delta\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n function: {\n arguments: slice,\n },\n },\n },\n },\n });\n }\n\n // tool-call-end\n events.push({\n type: \"tool-call-end\",\n index: idx,\n });\n }\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"TOOL_CALL\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\n// ─── SSE writer for Cohere typed events ─────────────────────────────────────\n\ninterface CohereStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeCohereSSEStream(\n res: http.ServerResponse,\n events: CohereSSEEvent[],\n optionsOrLatency?: number | CohereStreamOptions,\n): Promise<boolean> {\n const opts: CohereStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleCohere(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let cohereReq: CohereRequest;\n try {\n cohereReq = JSON.parse(raw) as CohereRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Validate required model field\n if (!cohereReq.model) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"model is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!cohereReq.messages || !Array.isArray(cohereReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = cohereToCompletionRequest(cohereReq);\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"cohere\",\n req.url ?? \"/v2/chat\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v2/chat\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereTextStreamEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cohere.js","names":[],"sources":["../src/cohere.ts"],"sourcesContent":["/**\n * Cohere v2 Chat API endpoint support.\n *\n * Translates incoming /v2/chat requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * Cohere's typed SSE streaming (or non-streaming) format.\n *\n * Cohere uses typed SSE events (event: + data: lines), similar to the\n * Claude Messages handler in messages.ts.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolCallId,\n isTextResponse,\n isToolCallResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Cohere v2 Chat request types ───────────────────────────────────────────\n\ninterface CohereMessage {\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n content: string;\n tool_call_id?: string;\n}\n\ninterface CohereToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface CohereRequest {\n model: string;\n messages: CohereMessage[];\n stream?: boolean;\n tools?: CohereToolDef[];\n response_format?: { type: string; json_schema?: object };\n}\n\n// ─── Cohere SSE event types ─────────────────────────────────────────────────\n\ninterface CohereSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n// ─── Zero-value usage block ─────────────────────────────────────────────────\n\nconst ZERO_USAGE = {\n billed_units: { input_tokens: 0, output_tokens: 0, search_units: 0, classifications: 0 },\n tokens: { input_tokens: 0, output_tokens: 0 },\n};\n\n// ─── Input conversion: Cohere → ChatCompletionRequest ───────────────────────\n\nexport function cohereToCompletionRequest(req: CohereRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n if (msg.role === \"system\") {\n messages.push({ role: \"system\", content: msg.content });\n } else if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (msg.role === \"tool\") {\n messages.push({\n role: \"tool\",\n content: msg.content,\n tool_call_id: msg.tool_call_id,\n });\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n tools,\n };\n}\n\n// ─── Response building: fixture → Cohere v2 Chat format ─────────────────────\n\n// Non-streaming text response\nfunction buildCohereTextResponse(content: string): object {\n return {\n id: generateMessageId(),\n finish_reason: \"COMPLETE\",\n message: {\n role: \"assistant\",\n content: [{ type: \"text\", text: content }],\n tool_calls: [],\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// Non-streaming tool call response\nfunction buildCohereToolCallResponse(toolCalls: ToolCall[], logger: Logger): object {\n const cohereCalls = toolCalls.map((tc) => {\n // Validate arguments JSON\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n }\n return {\n id: tc.id || generateToolCallId(),\n type: \"function\",\n function: {\n name: tc.name,\n arguments: tc.arguments || \"{}\",\n },\n };\n });\n\n return {\n id: generateMessageId(),\n finish_reason: \"TOOL_CALL\",\n message: {\n role: \"assistant\",\n content: [],\n tool_calls: cohereCalls,\n tool_plan: \"\",\n citations: [],\n },\n usage: ZERO_USAGE,\n };\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nfunction buildCohereTextStreamEvents(content: string, chunkSize: number): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // content-start (type: \"text\" only, no text field)\n events.push({\n type: \"content-start\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\" },\n },\n },\n });\n\n // content-delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content-delta\",\n index: 0,\n delta: {\n message: {\n content: { type: \"text\", text: slice },\n },\n },\n });\n }\n\n // content-end\n events.push({\n type: \"content-end\",\n index: 0,\n });\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"COMPLETE\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\nfunction buildCohereToolCallStreamEvents(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n): CohereSSEEvent[] {\n const msgId = generateMessageId();\n const events: CohereSSEEvent[] = [];\n\n // message-start\n events.push({\n id: msgId,\n type: \"message-start\",\n delta: {\n message: {\n role: \"assistant\",\n content: [],\n tool_plan: \"\",\n tool_calls: [],\n citations: [],\n },\n },\n });\n\n // tool-plan-delta\n events.push({\n type: \"tool-plan-delta\",\n delta: {\n message: {\n tool_plan: \"I will use the requested tool.\",\n },\n },\n });\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n\n // Validate arguments JSON\n let argsJson: string;\n try {\n JSON.parse(tc.arguments || \"{}\");\n argsJson = tc.arguments || \"{}\";\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsJson = \"{}\";\n }\n\n // tool-call-start\n events.push({\n type: \"tool-call-start\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n id: callId,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: \"\",\n },\n },\n },\n },\n });\n\n // tool-call-delta — chunked arguments\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"tool-call-delta\",\n index: idx,\n delta: {\n message: {\n tool_calls: {\n function: {\n arguments: slice,\n },\n },\n },\n },\n });\n }\n\n // tool-call-end\n events.push({\n type: \"tool-call-end\",\n index: idx,\n });\n }\n\n // message-end\n events.push({\n type: \"message-end\",\n delta: {\n finish_reason: \"TOOL_CALL\",\n usage: ZERO_USAGE,\n },\n });\n\n return events;\n}\n\n// ─── SSE writer for Cohere typed events ─────────────────────────────────────\n\ninterface CohereStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeCohereSSEStream(\n res: http.ServerResponse,\n events: CohereSSEEvent[],\n optionsOrLatency?: number | CohereStreamOptions,\n): Promise<boolean> {\n const opts: CohereStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleCohere(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let cohereReq: CohereRequest;\n try {\n cohereReq = JSON.parse(raw) as CohereRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Validate required model field\n if (!cohereReq.model) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"model is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!cohereReq.messages || !Array.isArray(cohereReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = cohereToCompletionRequest(cohereReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"cohere\",\n req.url ?? \"/v2/chat\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v2/chat\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereTextResponse(response.content);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereTextStreamEvents(response.content, chunkSize);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (cohereReq.stream !== true) {\n const body = buildCohereToolCallResponse(response.toolCalls, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeCohereSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/chat\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.cts","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":[],"mappings":";;;;;;;;UAciB,aAAA,SAAsB;;AAAvC;AAIiB,UAAA,iBAAA,CAAiB;EASjB,GAAA,EAAA,MAAA;EAAgB,IAAA,EAAA,MAAA;UAEnB,CAAA,EAAA,MAAA;aAF2B,CAAA,EAAA,MAAA;EAAmB,IAAA,CAAA,EAAA,MAAA;EAM3C,IAAA,CAAA,EAAA,MAAS;;AAGhB,UATO,eAAA,SAAwB,mBAS/B,CAAA;QACI,CAAA,EAAA;IACF,QAAA,EATE,KASF,CAAA;MAAe,IAAA,EAAA,MAAA;MAGV,OAAA,EAAA;QAAgB,IAAA,EAAA,MAAA;QAEvB,IAAA,EAAA,MAAA;MACI,CAAA;IACH,CAAA,CAAA;EAAc,CAAA;AAIzB;AAAgC,UAhBf,SAAA,CAgBe;MACnB,CAAA,EAAA,MAAA;YACH,CAAA,EAAA;IACS,IAAA,EAAA,MAAA;IAHqB,OAAA,EAAA,MAAA;EAAkB,CAAA;EAMzC,KAAA,CAAA,EAnBP,aAmBgB,
|
|
1
|
+
{"version":3,"file":"config-loader.d.cts","names":[],"sources":["../src/config-loader.ts"],"sourcesContent":[],"mappings":";;;;;;;;UAciB,aAAA,SAAsB;;AAAvC;AAIiB,UAAA,iBAAA,CAAiB;EASjB,GAAA,EAAA,MAAA;EAAgB,IAAA,EAAA,MAAA;UAEnB,CAAA,EAAA,MAAA;aAF2B,CAAA,EAAA,MAAA;EAAmB,IAAA,CAAA,EAAA,MAAA;EAM3C,IAAA,CAAA,EAAA,MAAS;;AAGhB,UATO,eAAA,SAAwB,mBAS/B,CAAA;QACI,CAAA,EAAA;IACF,QAAA,EATE,KASF,CAAA;MAAe,IAAA,EAAA,MAAA;MAGV,OAAA,EAAA;QAAgB,IAAA,EAAA,MAAA;QAEvB,IAAA,EAAA,MAAA;MACI,CAAA;IACH,CAAA,CAAA;EAAc,CAAA;AAIzB;AAAgC,UAhBf,SAAA,CAgBe;MACnB,CAAA,EAAA,MAAA;YACH,CAAA,EAAA;IACS,IAAA,EAAA,MAAA;IAHqB,OAAA,EAAA,MAAA;EAAkB,CAAA;EAMzC,KAAA,CAAA,EAnBP,aAmBgB,EAAA;EAKT,SAAA,CAAA,EAvBH,iBAuBoB,EAAA;EAOjB,OAAA,CAAA,EA7BL,eA+BC,EAAA;AAGb;AAAuC,UA/BtB,gBAAA,CA+BsB;SAMxB,EAAA,MAAA;OAHH,CAAA,EAhCF,OAgCE,EAAA;WAKK,CAAA,EApCH,WAoCG,EAAA;EAAW,MAAA,CAAA,EAnCjB,cAmCiB,EAAA;EAGX,OAAA,CAAA,EAAA,MAAY;AAK7B;AAA6B,UAvCZ,cAAA,SAAuB,kBAuCX,CAAA;UAGjB,CAAA,EAzCC,gBAyCD,EAAA;OACC,CAAA,EAzCH,gBAyCG,EAAA;gBAEL,CAAA,EA1CW,gBA0CX,EAAA;;AAEC,UAzCQ,SAAA,CAyCR;MACE,CAAA,EAAA,MAAA;EAAY,MAAA,CAAA,EAxCZ,cAwCY,EAAA;AAQvB;AAKsB,UAlDL,iBAAA,CAkDoB;EAAA,KAAA,EAAA;IAC3B,OAAA,CAAA,EAAA,MAAA;IAEW,QAAA,CAAA,EAAA,MAAA;IAAlB,QAAA,CAAA,EAAA,MAAA;EAAO,CAAA;;WAlDC;;;UAIM,UAAA;;aAEJ;;UAGI,sBAAA;;;YAGL;;;eAGG;;iBAEE;;UAGA,YAAA;;gBAED;;UAGC,YAAA;;;YAGL;aACC;;QAEL;QACA;SACC;WACE;;;;;;;;;;;iBAQK,UAAA,sBAAgC;iBAK1B,eAAA,SACZ;;;IAEP;UAAkB"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/convert-mockllm.ts
|
|
3
|
+
function tokenizeYamlLines(input) {
|
|
4
|
+
const lines = [];
|
|
5
|
+
for (const raw of input.split("\n")) {
|
|
6
|
+
const trimmed = raw.trimStart();
|
|
7
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
8
|
+
const indent = raw.length - raw.trimStart().length;
|
|
9
|
+
const content = stripTrailingComment(trimmed);
|
|
10
|
+
const isArrayItem = content.startsWith("- ");
|
|
11
|
+
const arrayItemContent = isArrayItem ? content.slice(2).trim() : "";
|
|
12
|
+
lines.push({
|
|
13
|
+
indent,
|
|
14
|
+
raw,
|
|
15
|
+
content,
|
|
16
|
+
isArrayItem,
|
|
17
|
+
arrayItemContent
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return lines;
|
|
21
|
+
}
|
|
22
|
+
function stripTrailingComment(s) {
|
|
23
|
+
let inSingle = false;
|
|
24
|
+
let inDouble = false;
|
|
25
|
+
for (let i = 0; i < s.length; i++) {
|
|
26
|
+
const ch = s[i];
|
|
27
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
28
|
+
if (ch === "\"" && !inSingle) inDouble = !inDouble;
|
|
29
|
+
if (ch === "#" && !inSingle && !inDouble && i > 0 && s[i - 1] === " ") return s.slice(0, i).trimEnd();
|
|
30
|
+
}
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
function parseScalar(value) {
|
|
34
|
+
if (value === "" || value === "~" || value === "null") return null;
|
|
35
|
+
if (value === "true") return true;
|
|
36
|
+
if (value === "false") return false;
|
|
37
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
38
|
+
const num = Number(value);
|
|
39
|
+
if (!Number.isNaN(num) && value !== "") return num;
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function parseSimpleYaml(input) {
|
|
43
|
+
const lines = tokenizeYamlLines(input);
|
|
44
|
+
if (lines.length === 0) return null;
|
|
45
|
+
return parseBlock(lines, 0, 0).value;
|
|
46
|
+
}
|
|
47
|
+
function parseBlock(lines, startIndex, minIndent) {
|
|
48
|
+
if (startIndex >= lines.length) return {
|
|
49
|
+
value: null,
|
|
50
|
+
nextIndex: startIndex
|
|
51
|
+
};
|
|
52
|
+
const line = lines[startIndex];
|
|
53
|
+
if (line.isArrayItem && line.indent >= minIndent) return parseArray(lines, startIndex, line.indent);
|
|
54
|
+
if (line.content.includes(":")) return parseMap(lines, startIndex, line.indent);
|
|
55
|
+
return {
|
|
56
|
+
value: parseScalar(line.content),
|
|
57
|
+
nextIndex: startIndex + 1
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function parseArray(lines, startIndex, baseIndent) {
|
|
61
|
+
const arr = [];
|
|
62
|
+
let i = startIndex;
|
|
63
|
+
while (i < lines.length) {
|
|
64
|
+
const line = lines[i];
|
|
65
|
+
if (line.indent < baseIndent) break;
|
|
66
|
+
if (line.indent > baseIndent) break;
|
|
67
|
+
if (!line.isArrayItem) break;
|
|
68
|
+
const itemContent = line.arrayItemContent;
|
|
69
|
+
if (itemContent === "") {
|
|
70
|
+
const nested = parseBlock(lines, i + 1, baseIndent + 1);
|
|
71
|
+
arr.push(nested.value);
|
|
72
|
+
i = nested.nextIndex;
|
|
73
|
+
} else if (itemContent.includes(":")) {
|
|
74
|
+
const inlineMap = parseArrayItemMap(lines, i, baseIndent);
|
|
75
|
+
arr.push(inlineMap.value);
|
|
76
|
+
i = inlineMap.nextIndex;
|
|
77
|
+
} else {
|
|
78
|
+
arr.push(parseScalar(itemContent));
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
value: arr,
|
|
84
|
+
nextIndex: i
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function parseArrayItemMap(lines, startIndex, arrayIndent) {
|
|
88
|
+
const map = {};
|
|
89
|
+
const firstContent = lines[startIndex].arrayItemContent;
|
|
90
|
+
const colonIdx = findColon(firstContent);
|
|
91
|
+
if (colonIdx === -1) return {
|
|
92
|
+
value: parseScalar(firstContent),
|
|
93
|
+
nextIndex: startIndex + 1
|
|
94
|
+
};
|
|
95
|
+
const key = firstContent.slice(0, colonIdx).trim();
|
|
96
|
+
const valueStr = firstContent.slice(colonIdx + 1).trim();
|
|
97
|
+
if (valueStr === "") {
|
|
98
|
+
const nested = parseBlock(lines, startIndex + 1, arrayIndent + 2);
|
|
99
|
+
map[key] = nested.value;
|
|
100
|
+
let i = nested.nextIndex;
|
|
101
|
+
const siblingIndent = arrayIndent + 2;
|
|
102
|
+
while (i < lines.length && lines[i].indent >= siblingIndent && !lines[i].isArrayItem) if (lines[i].indent === siblingIndent || lines[i].indent > siblingIndent) if (lines[i].indent === siblingIndent && lines[i].content.includes(":")) i = parseMapEntries(lines, i, siblingIndent, map).nextIndex;
|
|
103
|
+
else break;
|
|
104
|
+
return {
|
|
105
|
+
value: map,
|
|
106
|
+
nextIndex: i
|
|
107
|
+
};
|
|
108
|
+
} else map[key] = parseScalar(valueStr);
|
|
109
|
+
let i = startIndex + 1;
|
|
110
|
+
const contentIndent = arrayIndent + 2;
|
|
111
|
+
while (i < lines.length) {
|
|
112
|
+
const line = lines[i];
|
|
113
|
+
if (line.indent < contentIndent) break;
|
|
114
|
+
if (line.isArrayItem && line.indent <= arrayIndent) break;
|
|
115
|
+
if (line.indent === contentIndent && !line.isArrayItem && line.content.includes(":")) {
|
|
116
|
+
const colonPos = findColon(line.content);
|
|
117
|
+
if (colonPos === -1) break;
|
|
118
|
+
const k = line.content.slice(0, colonPos).trim();
|
|
119
|
+
const v = line.content.slice(colonPos + 1).trim();
|
|
120
|
+
if (v === "") {
|
|
121
|
+
const nested = parseBlock(lines, i + 1, contentIndent + 1);
|
|
122
|
+
map[k] = nested.value;
|
|
123
|
+
i = nested.nextIndex;
|
|
124
|
+
} else {
|
|
125
|
+
map[k] = parseScalar(v);
|
|
126
|
+
i++;
|
|
127
|
+
}
|
|
128
|
+
} else if (line.indent === contentIndent && line.isArrayItem) break;
|
|
129
|
+
else if (line.indent > contentIndent) i++;
|
|
130
|
+
else break;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
value: map,
|
|
134
|
+
nextIndex: i
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function parseMap(lines, startIndex, baseIndent) {
|
|
138
|
+
const map = {};
|
|
139
|
+
return {
|
|
140
|
+
value: map,
|
|
141
|
+
nextIndex: parseMapEntries(lines, startIndex, baseIndent, map).nextIndex
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function parseMapEntries(lines, startIndex, baseIndent, map) {
|
|
145
|
+
let i = startIndex;
|
|
146
|
+
while (i < lines.length) {
|
|
147
|
+
const line = lines[i];
|
|
148
|
+
if (line.indent < baseIndent) break;
|
|
149
|
+
if (line.indent > baseIndent) {
|
|
150
|
+
i++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (line.isArrayItem) break;
|
|
154
|
+
const colonIdx = findColon(line.content);
|
|
155
|
+
if (colonIdx === -1) break;
|
|
156
|
+
const key = line.content.slice(0, colonIdx).trim();
|
|
157
|
+
const valueStr = line.content.slice(colonIdx + 1).trim();
|
|
158
|
+
if (valueStr === "") {
|
|
159
|
+
const nested = parseBlock(lines, i + 1, baseIndent + 1);
|
|
160
|
+
map[key] = nested.value;
|
|
161
|
+
i = nested.nextIndex;
|
|
162
|
+
} else {
|
|
163
|
+
map[key] = parseScalar(valueStr);
|
|
164
|
+
i++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
value: map,
|
|
169
|
+
nextIndex: i
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function findColon(s) {
|
|
173
|
+
let inSingle = false;
|
|
174
|
+
let inDouble = false;
|
|
175
|
+
for (let i = 0; i < s.length; i++) {
|
|
176
|
+
const ch = s[i];
|
|
177
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
178
|
+
if (ch === "\"" && !inSingle) inDouble = !inDouble;
|
|
179
|
+
if (ch === ":" && !inSingle && !inDouble) {
|
|
180
|
+
if (i === s.length - 1 || s[i + 1] === " ") return i;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return -1;
|
|
184
|
+
}
|
|
185
|
+
function convertConfig(config) {
|
|
186
|
+
const fixtures = [];
|
|
187
|
+
if (config.routes) for (const route of config.routes) {
|
|
188
|
+
const fixture = convertRoute(route);
|
|
189
|
+
if (fixture) fixtures.push(fixture);
|
|
190
|
+
}
|
|
191
|
+
const result = { fixtures };
|
|
192
|
+
if (config.mcp?.tools && config.mcp.tools.length > 0) result.mcpTools = config.mcp.tools.map(convertMCPTool);
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
function convertRoute(route) {
|
|
196
|
+
const content = extractResponseContent(route.response);
|
|
197
|
+
if (content === null) return null;
|
|
198
|
+
const fixture = {
|
|
199
|
+
match: {},
|
|
200
|
+
response: { content }
|
|
201
|
+
};
|
|
202
|
+
const userMessage = extractUserMessage(route);
|
|
203
|
+
if (userMessage) fixture.match = { userMessage };
|
|
204
|
+
else fixture._comment = `${route.method ?? "POST"} ${route.path}`;
|
|
205
|
+
return fixture;
|
|
206
|
+
}
|
|
207
|
+
function extractResponseContent(response) {
|
|
208
|
+
const choices = response.choices;
|
|
209
|
+
if (!Array.isArray(choices) || choices.length === 0) return null;
|
|
210
|
+
const message = choices[0].message;
|
|
211
|
+
if (!message) return null;
|
|
212
|
+
const content = message.content;
|
|
213
|
+
if (typeof content !== "string") return null;
|
|
214
|
+
return content;
|
|
215
|
+
}
|
|
216
|
+
function extractUserMessage(route) {
|
|
217
|
+
const messages = route.match?.body?.messages;
|
|
218
|
+
if (!Array.isArray(messages) || messages.length === 0) return null;
|
|
219
|
+
for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") return messages[i].content;
|
|
220
|
+
return messages[messages.length - 1].content ?? null;
|
|
221
|
+
}
|
|
222
|
+
function convertMCPTool(tool) {
|
|
223
|
+
const result = { name: tool.name };
|
|
224
|
+
if (tool.description) result.description = tool.description;
|
|
225
|
+
if (tool.parameters) result.inputSchema = tool.parameters;
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
//#endregion
|
|
230
|
+
exports.convertConfig = convertConfig;
|
|
231
|
+
exports.parseSimpleYaml = parseSimpleYaml;
|
|
232
|
+
//# sourceMappingURL=convert-mockllm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert-mockllm.cjs","names":[],"sources":["../src/convert-mockllm.ts"],"sourcesContent":["/**\n * mock-llm (dwmkerr) -> aimock fixture converter\n *\n * Core conversion logic. Used by both the CLI (`aimock convert mockllm`)\n * and the standalone script (`scripts/convert-mockllm.ts`).\n */\n\n// ---------------------------------------------------------------------------\n// Minimal YAML parser\n// ---------------------------------------------------------------------------\n// Handles the subset used by mock-llm configs: indented maps, arrays with\n// `-` prefix, quoted/unquoted strings, numbers, booleans, and null.\n// Does NOT handle: anchors, aliases, multi-line scalars, flow collections,\n// tags, or other advanced YAML features.\n\ninterface YamlLine {\n indent: number;\n raw: string;\n content: string; // trimmed, without trailing comment\n isArrayItem: boolean;\n arrayItemContent: string; // content after \"- \"\n}\n\nfunction tokenizeYamlLines(input: string): YamlLine[] {\n const lines: YamlLine[] = [];\n for (const raw of input.split(\"\\n\")) {\n // Skip blank lines and full-line comments\n const trimmed = raw.trimStart();\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n\n const indent = raw.length - raw.trimStart().length;\n // Strip trailing comments (but not inside quoted strings)\n const content = stripTrailingComment(trimmed);\n const isArrayItem = content.startsWith(\"- \");\n const arrayItemContent = isArrayItem ? content.slice(2).trim() : \"\";\n\n lines.push({ indent, raw, content, isArrayItem, arrayItemContent });\n }\n return lines;\n}\n\nfunction stripTrailingComment(s: string): string {\n // Naive: find # not inside quotes\n let inSingle = false;\n let inDouble = false;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (ch === \"'\" && !inDouble) inSingle = !inSingle;\n if (ch === '\"' && !inSingle) inDouble = !inDouble;\n if (ch === \"#\" && !inSingle && !inDouble && i > 0 && s[i - 1] === \" \") {\n return s.slice(0, i).trimEnd();\n }\n }\n return s;\n}\n\nfunction parseScalar(value: string): unknown {\n if (value === \"\" || value === \"~\" || value === \"null\") return null;\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Quoted string\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n\n // Number\n const num = Number(value);\n if (!Number.isNaN(num) && value !== \"\") return num;\n\n // Unquoted string\n return value;\n}\n\nexport function parseSimpleYaml(input: string): unknown {\n const lines = tokenizeYamlLines(input);\n if (lines.length === 0) return null;\n\n const result = parseBlock(lines, 0, 0);\n return result.value;\n}\n\ninterface ParseResult {\n value: unknown;\n nextIndex: number;\n}\n\nfunction parseBlock(lines: YamlLine[], startIndex: number, minIndent: number): ParseResult {\n if (startIndex >= lines.length) {\n return { value: null, nextIndex: startIndex };\n }\n\n const line = lines[startIndex];\n\n // Determine if this block is an array or a map\n if (line.isArrayItem && line.indent >= minIndent) {\n return parseArray(lines, startIndex, line.indent);\n }\n\n // Map\n if (line.content.includes(\":\")) {\n return parseMap(lines, startIndex, line.indent);\n }\n\n // Single scalar\n return { value: parseScalar(line.content), nextIndex: startIndex + 1 };\n}\n\nfunction parseArray(lines: YamlLine[], startIndex: number, baseIndent: number): ParseResult {\n const arr: unknown[] = [];\n let i = startIndex;\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.indent < baseIndent) break;\n if (line.indent > baseIndent) break; // shouldn't happen at array level\n if (!line.isArrayItem) break;\n\n const itemContent = line.arrayItemContent;\n\n if (itemContent === \"\") {\n // Array item with nested block on next lines\n const nested = parseBlock(lines, i + 1, baseIndent + 1);\n arr.push(nested.value);\n i = nested.nextIndex;\n } else if (itemContent.includes(\":\")) {\n // Inline map start: \"- key: value\" possibly with more keys below\n // Parse as a map, treating the \"- \" offset as extra indent\n const inlineMap = parseArrayItemMap(lines, i, baseIndent);\n arr.push(inlineMap.value);\n i = inlineMap.nextIndex;\n } else {\n // Simple scalar array item\n arr.push(parseScalar(itemContent));\n i++;\n }\n }\n\n return { value: arr, nextIndex: i };\n}\n\nfunction parseArrayItemMap(\n lines: YamlLine[],\n startIndex: number,\n arrayIndent: number,\n): ParseResult {\n // First line is \"- key: value\", subsequent lines at indent > arrayIndent are part of this map\n const map: Record<string, unknown> = {};\n const firstLine = lines[startIndex];\n const firstContent = firstLine.arrayItemContent;\n\n // Parse the first key: value from the array item line\n const colonIdx = findColon(firstContent);\n if (colonIdx === -1) {\n return { value: parseScalar(firstContent), nextIndex: startIndex + 1 };\n }\n\n const key = firstContent.slice(0, colonIdx).trim();\n const valueStr = firstContent.slice(colonIdx + 1).trim();\n\n if (valueStr === \"\") {\n // Value is a nested block\n const nested = parseBlock(lines, startIndex + 1, arrayIndent + 2);\n map[key] = nested.value;\n let i = nested.nextIndex;\n\n // Continue reading sibling keys at the array-item's content indent\n const siblingIndent = arrayIndent + 2;\n while (i < lines.length && lines[i].indent >= siblingIndent && !lines[i].isArrayItem) {\n if (lines[i].indent === siblingIndent || lines[i].indent > siblingIndent) {\n // Only parse if at exactly sibling indent and is a map key\n if (lines[i].indent === siblingIndent && lines[i].content.includes(\":\")) {\n const mapResult = parseMapEntries(lines, i, siblingIndent, map);\n i = mapResult.nextIndex;\n } else {\n break;\n }\n }\n }\n\n return { value: map, nextIndex: i };\n } else {\n map[key] = parseScalar(valueStr);\n }\n\n // Read additional keys at indent > arrayIndent (the \" key: value\" lines after \"- first: val\")\n let i = startIndex + 1;\n const contentIndent = arrayIndent + 2; // \"- \" adds 2 to effective indent\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.indent < contentIndent) break;\n if (line.isArrayItem && line.indent <= arrayIndent) break;\n\n if (line.indent === contentIndent && !line.isArrayItem && line.content.includes(\":\")) {\n const colonPos = findColon(line.content);\n if (colonPos === -1) break;\n const k = line.content.slice(0, colonPos).trim();\n const v = line.content.slice(colonPos + 1).trim();\n\n if (v === \"\") {\n const nested = parseBlock(lines, i + 1, contentIndent + 1);\n map[k] = nested.value;\n i = nested.nextIndex;\n } else {\n map[k] = parseScalar(v);\n i++;\n }\n } else if (line.indent === contentIndent && line.isArrayItem) {\n // This is a new array item at the same level -- not part of this map\n break;\n } else if (line.indent > contentIndent) {\n // Skip nested content already consumed\n i++;\n } else {\n break;\n }\n }\n\n return { value: map, nextIndex: i };\n}\n\nfunction parseMap(lines: YamlLine[], startIndex: number, baseIndent: number): ParseResult {\n const map: Record<string, unknown> = {};\n const result = parseMapEntries(lines, startIndex, baseIndent, map);\n return { value: map, nextIndex: result.nextIndex };\n}\n\nfunction parseMapEntries(\n lines: YamlLine[],\n startIndex: number,\n baseIndent: number,\n map: Record<string, unknown>,\n): ParseResult {\n let i = startIndex;\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.indent < baseIndent) break;\n if (line.indent > baseIndent) {\n // Shouldn't happen at map level if properly structured -- skip\n i++;\n continue;\n }\n if (line.isArrayItem) break;\n\n const colonIdx = findColon(line.content);\n if (colonIdx === -1) {\n // Not a map entry\n break;\n }\n\n const key = line.content.slice(0, colonIdx).trim();\n const valueStr = line.content.slice(colonIdx + 1).trim();\n\n if (valueStr === \"\") {\n // Value is a nested block on subsequent lines\n const nested = parseBlock(lines, i + 1, baseIndent + 1);\n map[key] = nested.value;\n i = nested.nextIndex;\n } else {\n map[key] = parseScalar(valueStr);\n i++;\n }\n }\n\n return { value: map, nextIndex: i };\n}\n\nfunction findColon(s: string): number {\n let inSingle = false;\n let inDouble = false;\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (ch === \"'\" && !inDouble) inSingle = !inSingle;\n if (ch === '\"' && !inSingle) inDouble = !inDouble;\n if (ch === \":\" && !inSingle && !inDouble) {\n // Must be followed by space, end of line, or nothing\n if (i === s.length - 1 || s[i + 1] === \" \") {\n return i;\n }\n }\n }\n return -1;\n}\n\n// ---------------------------------------------------------------------------\n// mock-llm config types\n// ---------------------------------------------------------------------------\n\nexport interface MockLLMRoute {\n path: string;\n method?: string;\n match?: {\n body?: {\n messages?: Array<{ role: string; content: string }>;\n };\n };\n response: Record<string, unknown>;\n}\n\nexport interface MockLLMTool {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n}\n\nexport interface MockLLMConfig {\n routes?: MockLLMRoute[];\n mcp?: {\n tools?: MockLLMTool[];\n };\n}\n\n// ---------------------------------------------------------------------------\n// aimock output types\n// ---------------------------------------------------------------------------\n\nexport interface AimockFixture {\n match?: { userMessage?: string };\n response: { content?: string; toolCalls?: Array<{ name: string; arguments: string }> };\n _comment?: string;\n}\n\nexport interface AimockMCPTool {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport interface ConvertResult {\n fixtures: AimockFixture[];\n mcpTools?: AimockMCPTool[];\n}\n\n// ---------------------------------------------------------------------------\n// Converter\n// ---------------------------------------------------------------------------\n\nexport function convertConfig(config: MockLLMConfig): ConvertResult {\n const fixtures: AimockFixture[] = [];\n\n if (config.routes) {\n for (const route of config.routes) {\n const fixture = convertRoute(route);\n if (fixture) {\n fixtures.push(fixture);\n }\n }\n }\n\n const result: ConvertResult = { fixtures };\n\n if (config.mcp?.tools && config.mcp.tools.length > 0) {\n result.mcpTools = config.mcp.tools.map(convertMCPTool);\n }\n\n return result;\n}\n\nfunction convertRoute(route: MockLLMRoute): AimockFixture | null {\n // Extract content from response.choices[0].message.content\n const content = extractResponseContent(route.response);\n if (content === null) return null;\n\n const fixture: AimockFixture = {\n match: {},\n response: { content },\n };\n\n // Extract match criteria from match.body.messages\n const userMessage = extractUserMessage(route);\n if (userMessage) {\n fixture.match = { userMessage };\n } else {\n // Use path as a comment/identifier when no match criteria\n fixture._comment = `${route.method ?? \"POST\"} ${route.path}`;\n }\n\n return fixture;\n}\n\nfunction extractResponseContent(response: Record<string, unknown>): string | null {\n const choices = response.choices as Array<Record<string, unknown>> | undefined;\n if (!Array.isArray(choices) || choices.length === 0) return null;\n\n const firstChoice = choices[0];\n const message = firstChoice.message as Record<string, unknown> | undefined;\n if (!message) return null;\n\n const content = message.content;\n if (typeof content !== \"string\") return null;\n\n return content;\n}\n\nfunction extractUserMessage(route: MockLLMRoute): string | null {\n const messages = route.match?.body?.messages;\n if (!Array.isArray(messages) || messages.length === 0) return null;\n\n // Find the last user message\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"user\") {\n return messages[i].content;\n }\n }\n\n // Fall back to last message content regardless of role\n return messages[messages.length - 1].content ?? null;\n}\n\nfunction convertMCPTool(tool: MockLLMTool): AimockMCPTool {\n const result: AimockMCPTool = { name: tool.name };\n if (tool.description) result.description = tool.description;\n if (tool.parameters) result.inputSchema = tool.parameters;\n return result;\n}\n"],"mappings":"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"}
|