@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/speech.cjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const require_helpers = require('./helpers.cjs');
|
|
2
|
+
const require_router = require('./router.cjs');
|
|
3
|
+
const require_sse_writer = require('./sse-writer.cjs');
|
|
4
|
+
const require_chaos = require('./chaos.cjs');
|
|
5
|
+
const require_recorder = require('./recorder.cjs');
|
|
6
|
+
|
|
7
|
+
//#region src/speech.ts
|
|
8
|
+
const FORMAT_TO_CONTENT_TYPE = {
|
|
9
|
+
mp3: "audio/mpeg",
|
|
10
|
+
opus: "audio/opus",
|
|
11
|
+
aac: "audio/aac",
|
|
12
|
+
flac: "audio/flac",
|
|
13
|
+
wav: "audio/wav",
|
|
14
|
+
pcm: "audio/pcm"
|
|
15
|
+
};
|
|
16
|
+
async function handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
17
|
+
setCorsHeaders(res);
|
|
18
|
+
const path = req.url ?? "/v1/audio/speech";
|
|
19
|
+
const method = req.method ?? "POST";
|
|
20
|
+
let speechReq;
|
|
21
|
+
try {
|
|
22
|
+
speechReq = JSON.parse(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
journal.add({
|
|
25
|
+
method,
|
|
26
|
+
path,
|
|
27
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
28
|
+
body: null,
|
|
29
|
+
response: {
|
|
30
|
+
status: 400,
|
|
31
|
+
fixture: null
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
35
|
+
message: "Malformed JSON",
|
|
36
|
+
type: "invalid_request_error",
|
|
37
|
+
code: "invalid_json"
|
|
38
|
+
} }));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const syntheticReq = {
|
|
42
|
+
model: speechReq.model ?? "tts-1",
|
|
43
|
+
messages: [{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: speechReq.input
|
|
46
|
+
}],
|
|
47
|
+
_endpointType: "speech"
|
|
48
|
+
};
|
|
49
|
+
const testId = require_helpers.getTestId(req);
|
|
50
|
+
const fixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
51
|
+
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
52
|
+
if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
53
|
+
method,
|
|
54
|
+
path,
|
|
55
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
56
|
+
body: syntheticReq
|
|
57
|
+
}, defaults.registry, defaults.logger)) return;
|
|
58
|
+
if (!fixture) {
|
|
59
|
+
if (defaults.record) {
|
|
60
|
+
if (await require_recorder.proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/audio/speech", fixtures, defaults, raw)) {
|
|
61
|
+
journal.add({
|
|
62
|
+
method,
|
|
63
|
+
path,
|
|
64
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
65
|
+
body: syntheticReq,
|
|
66
|
+
response: {
|
|
67
|
+
status: res.statusCode ?? 200,
|
|
68
|
+
fixture: null
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const strictStatus = defaults.strict ? 503 : 404;
|
|
75
|
+
const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
|
|
76
|
+
journal.add({
|
|
77
|
+
method,
|
|
78
|
+
path,
|
|
79
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
80
|
+
body: syntheticReq,
|
|
81
|
+
response: {
|
|
82
|
+
status: strictStatus,
|
|
83
|
+
fixture: null
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
|
|
87
|
+
message: strictMessage,
|
|
88
|
+
type: "invalid_request_error",
|
|
89
|
+
code: "no_fixture_match"
|
|
90
|
+
} }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const response = fixture.response;
|
|
94
|
+
if (require_helpers.isErrorResponse(response)) {
|
|
95
|
+
const status = response.status ?? 500;
|
|
96
|
+
journal.add({
|
|
97
|
+
method,
|
|
98
|
+
path,
|
|
99
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
100
|
+
body: syntheticReq,
|
|
101
|
+
response: {
|
|
102
|
+
status,
|
|
103
|
+
fixture
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(response));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!require_helpers.isAudioResponse(response)) {
|
|
110
|
+
journal.add({
|
|
111
|
+
method,
|
|
112
|
+
path,
|
|
113
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
114
|
+
body: syntheticReq,
|
|
115
|
+
response: {
|
|
116
|
+
status: 500,
|
|
117
|
+
fixture
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
121
|
+
message: "Fixture response is not an audio type",
|
|
122
|
+
type: "server_error"
|
|
123
|
+
} }));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
journal.add({
|
|
127
|
+
method,
|
|
128
|
+
path,
|
|
129
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
130
|
+
body: syntheticReq,
|
|
131
|
+
response: {
|
|
132
|
+
status: 200,
|
|
133
|
+
fixture
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
const contentType = FORMAT_TO_CONTENT_TYPE[response.format ?? "mp3"] ?? "audio/mpeg";
|
|
137
|
+
const audioBytes = Buffer.from(response.audio, "base64");
|
|
138
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
139
|
+
res.end(audioBytes);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
exports.handleSpeech = handleSpeech;
|
|
144
|
+
//# sourceMappingURL=speech.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isAudioResponse"],"sources":["../src/speech.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isAudioResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface SpeechRequest {\n model?: string;\n input: string;\n voice?: string;\n response_format?: string;\n speed?: number;\n [key: string]: unknown;\n}\n\nconst FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\nexport async function handleSpeech(\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 setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/speech\";\n const method = req.method ?? \"POST\";\n\n let speechReq: SpeechRequest;\n try {\n speechReq = JSON.parse(raw) as SpeechRequest;\n } catch {\n journal.add({\n method,\n path,\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: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: speechReq.model ?? \"tts-1\",\n messages: [{ role: \"user\", content: speechReq.input }],\n _endpointType: \"speech\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\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 { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\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 syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/speech\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\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 journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isAudioResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not an audio type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const format = response.format ?? \"mp3\";\n const contentType = FORMAT_TO_CONTENT_TYPE[format] ?? \"audio/mpeg\";\n const audioBytes = Buffer.from(response.audio, \"base64\");\n\n res.writeHead(200, { \"Content-Type\": contentType });\n res.end(audioBytes);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Journal } from "./journal.cjs";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.cjs";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
|
|
5
|
+
//#region src/speech.d.ts
|
|
6
|
+
declare function handleSpeech(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=speech.d.ts.map
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { handleSpeech };
|
|
11
|
+
//# sourceMappingURL=speech.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.d.cts","names":[],"sources":["../src/speech.ts"],"sourcesContent":[],"mappings":";;;;;iBA2BsB,YAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
|
package/dist/speech.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Journal } from "./journal.js";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.js";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
|
|
5
|
+
//#region src/speech.d.ts
|
|
6
|
+
declare function handleSpeech(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=speech.d.ts.map
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { handleSpeech };
|
|
11
|
+
//# sourceMappingURL=speech.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.d.ts","names":[],"sources":["../src/speech.ts"],"sourcesContent":[],"mappings":";;;;;iBA2BsB,YAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
|
package/dist/speech.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { flattenHeaders, getTestId, isAudioResponse, isErrorResponse } from "./helpers.js";
|
|
2
|
+
import { matchFixture } from "./router.js";
|
|
3
|
+
import { writeErrorResponse } from "./sse-writer.js";
|
|
4
|
+
import { applyChaos } from "./chaos.js";
|
|
5
|
+
import { proxyAndRecord } from "./recorder.js";
|
|
6
|
+
|
|
7
|
+
//#region src/speech.ts
|
|
8
|
+
const FORMAT_TO_CONTENT_TYPE = {
|
|
9
|
+
mp3: "audio/mpeg",
|
|
10
|
+
opus: "audio/opus",
|
|
11
|
+
aac: "audio/aac",
|
|
12
|
+
flac: "audio/flac",
|
|
13
|
+
wav: "audio/wav",
|
|
14
|
+
pcm: "audio/pcm"
|
|
15
|
+
};
|
|
16
|
+
async function handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
17
|
+
setCorsHeaders(res);
|
|
18
|
+
const path = req.url ?? "/v1/audio/speech";
|
|
19
|
+
const method = req.method ?? "POST";
|
|
20
|
+
let speechReq;
|
|
21
|
+
try {
|
|
22
|
+
speechReq = JSON.parse(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
journal.add({
|
|
25
|
+
method,
|
|
26
|
+
path,
|
|
27
|
+
headers: flattenHeaders(req.headers),
|
|
28
|
+
body: null,
|
|
29
|
+
response: {
|
|
30
|
+
status: 400,
|
|
31
|
+
fixture: null
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
35
|
+
message: "Malformed JSON",
|
|
36
|
+
type: "invalid_request_error",
|
|
37
|
+
code: "invalid_json"
|
|
38
|
+
} }));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const syntheticReq = {
|
|
42
|
+
model: speechReq.model ?? "tts-1",
|
|
43
|
+
messages: [{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: speechReq.input
|
|
46
|
+
}],
|
|
47
|
+
_endpointType: "speech"
|
|
48
|
+
};
|
|
49
|
+
const testId = getTestId(req);
|
|
50
|
+
const fixture = matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
51
|
+
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
52
|
+
if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
53
|
+
method,
|
|
54
|
+
path,
|
|
55
|
+
headers: flattenHeaders(req.headers),
|
|
56
|
+
body: syntheticReq
|
|
57
|
+
}, defaults.registry, defaults.logger)) return;
|
|
58
|
+
if (!fixture) {
|
|
59
|
+
if (defaults.record) {
|
|
60
|
+
if (await proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/audio/speech", fixtures, defaults, raw)) {
|
|
61
|
+
journal.add({
|
|
62
|
+
method,
|
|
63
|
+
path,
|
|
64
|
+
headers: flattenHeaders(req.headers),
|
|
65
|
+
body: syntheticReq,
|
|
66
|
+
response: {
|
|
67
|
+
status: res.statusCode ?? 200,
|
|
68
|
+
fixture: null
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const strictStatus = defaults.strict ? 503 : 404;
|
|
75
|
+
const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
|
|
76
|
+
journal.add({
|
|
77
|
+
method,
|
|
78
|
+
path,
|
|
79
|
+
headers: flattenHeaders(req.headers),
|
|
80
|
+
body: syntheticReq,
|
|
81
|
+
response: {
|
|
82
|
+
status: strictStatus,
|
|
83
|
+
fixture: null
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
|
|
87
|
+
message: strictMessage,
|
|
88
|
+
type: "invalid_request_error",
|
|
89
|
+
code: "no_fixture_match"
|
|
90
|
+
} }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const response = fixture.response;
|
|
94
|
+
if (isErrorResponse(response)) {
|
|
95
|
+
const status = response.status ?? 500;
|
|
96
|
+
journal.add({
|
|
97
|
+
method,
|
|
98
|
+
path,
|
|
99
|
+
headers: flattenHeaders(req.headers),
|
|
100
|
+
body: syntheticReq,
|
|
101
|
+
response: {
|
|
102
|
+
status,
|
|
103
|
+
fixture
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
writeErrorResponse(res, status, JSON.stringify(response));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!isAudioResponse(response)) {
|
|
110
|
+
journal.add({
|
|
111
|
+
method,
|
|
112
|
+
path,
|
|
113
|
+
headers: flattenHeaders(req.headers),
|
|
114
|
+
body: syntheticReq,
|
|
115
|
+
response: {
|
|
116
|
+
status: 500,
|
|
117
|
+
fixture
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
121
|
+
message: "Fixture response is not an audio type",
|
|
122
|
+
type: "server_error"
|
|
123
|
+
} }));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
journal.add({
|
|
127
|
+
method,
|
|
128
|
+
path,
|
|
129
|
+
headers: flattenHeaders(req.headers),
|
|
130
|
+
body: syntheticReq,
|
|
131
|
+
response: {
|
|
132
|
+
status: 200,
|
|
133
|
+
fixture
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
const contentType = FORMAT_TO_CONTENT_TYPE[response.format ?? "mp3"] ?? "audio/mpeg";
|
|
137
|
+
const audioBytes = Buffer.from(response.audio, "base64");
|
|
138
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
139
|
+
res.end(audioBytes);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
export { handleSpeech };
|
|
144
|
+
//# sourceMappingURL=speech.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.js","names":[],"sources":["../src/speech.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isAudioResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface SpeechRequest {\n model?: string;\n input: string;\n voice?: string;\n response_format?: string;\n speed?: number;\n [key: string]: unknown;\n}\n\nconst FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\nexport async function handleSpeech(\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 setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/speech\";\n const method = req.method ?? \"POST\";\n\n let speechReq: SpeechRequest;\n try {\n speechReq = JSON.parse(raw) as SpeechRequest;\n } catch {\n journal.add({\n method,\n path,\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: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: speechReq.model ?? \"tts-1\",\n messages: [{ role: \"user\", content: speechReq.input }],\n _endpointType: \"speech\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\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 { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\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 syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/speech\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\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 journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isAudioResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not an audio type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const format = response.format ?? \"mp3\";\n const contentType = FORMAT_TO_CONTENT_TYPE[format] ?? \"audio/mpeg\";\n const audioBytes = Buffer.from(response.audio, \"base64\");\n\n res.writeHead(200, { \"Content-Type\": contentType });\n res.end(audioBytes);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const require_helpers = require('./helpers.cjs');
|
|
2
|
+
const require_router = require('./router.cjs');
|
|
3
|
+
const require_sse_writer = require('./sse-writer.cjs');
|
|
4
|
+
const require_chaos = require('./chaos.cjs');
|
|
5
|
+
const require_recorder = require('./recorder.cjs');
|
|
6
|
+
|
|
7
|
+
//#region src/transcription.ts
|
|
8
|
+
/**
|
|
9
|
+
* Extract a named field value from a multipart/form-data body.
|
|
10
|
+
* Lightweight parser — scans for Content-Disposition headers
|
|
11
|
+
* to find simple string field values.
|
|
12
|
+
*/
|
|
13
|
+
function extractFormField(raw, fieldName) {
|
|
14
|
+
const pattern = new RegExp(`Content-Disposition:\\s*form-data;[^\\r\\n]*name="${fieldName}"[^\\r\\n]*\\r\\n\\r\\n([^\\r\\n]*)`, "i");
|
|
15
|
+
return raw.match(pattern)?.[1];
|
|
16
|
+
}
|
|
17
|
+
async function handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
18
|
+
setCorsHeaders(res);
|
|
19
|
+
const path = req.url ?? "/v1/audio/transcriptions";
|
|
20
|
+
const method = req.method ?? "POST";
|
|
21
|
+
const model = extractFormField(raw, "model") ?? "whisper-1";
|
|
22
|
+
const responseFormat = extractFormField(raw, "response_format") ?? "json";
|
|
23
|
+
const syntheticReq = {
|
|
24
|
+
model,
|
|
25
|
+
messages: [],
|
|
26
|
+
_endpointType: "transcription"
|
|
27
|
+
};
|
|
28
|
+
const testId = require_helpers.getTestId(req);
|
|
29
|
+
const fixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
30
|
+
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
31
|
+
if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
32
|
+
method,
|
|
33
|
+
path,
|
|
34
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
35
|
+
body: syntheticReq
|
|
36
|
+
}, defaults.registry, defaults.logger)) return;
|
|
37
|
+
if (!fixture) {
|
|
38
|
+
if (defaults.record) {
|
|
39
|
+
if (await require_recorder.proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/audio/transcriptions", fixtures, defaults, raw)) {
|
|
40
|
+
journal.add({
|
|
41
|
+
method,
|
|
42
|
+
path,
|
|
43
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
44
|
+
body: syntheticReq,
|
|
45
|
+
response: {
|
|
46
|
+
status: res.statusCode ?? 200,
|
|
47
|
+
fixture: null
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const strictStatus = defaults.strict ? 503 : 404;
|
|
54
|
+
const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
|
|
55
|
+
journal.add({
|
|
56
|
+
method,
|
|
57
|
+
path,
|
|
58
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
59
|
+
body: syntheticReq,
|
|
60
|
+
response: {
|
|
61
|
+
status: strictStatus,
|
|
62
|
+
fixture: null
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
|
|
66
|
+
message: strictMessage,
|
|
67
|
+
type: "invalid_request_error",
|
|
68
|
+
code: "no_fixture_match"
|
|
69
|
+
} }));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const response = fixture.response;
|
|
73
|
+
if (require_helpers.isErrorResponse(response)) {
|
|
74
|
+
const status = response.status ?? 500;
|
|
75
|
+
journal.add({
|
|
76
|
+
method,
|
|
77
|
+
path,
|
|
78
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
79
|
+
body: syntheticReq,
|
|
80
|
+
response: {
|
|
81
|
+
status,
|
|
82
|
+
fixture
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(response));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!require_helpers.isTranscriptionResponse(response)) {
|
|
89
|
+
journal.add({
|
|
90
|
+
method,
|
|
91
|
+
path,
|
|
92
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
93
|
+
body: syntheticReq,
|
|
94
|
+
response: {
|
|
95
|
+
status: 500,
|
|
96
|
+
fixture
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
100
|
+
message: "Fixture response is not a transcription type",
|
|
101
|
+
type: "server_error"
|
|
102
|
+
} }));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
journal.add({
|
|
106
|
+
method,
|
|
107
|
+
path,
|
|
108
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
109
|
+
body: syntheticReq,
|
|
110
|
+
response: {
|
|
111
|
+
status: 200,
|
|
112
|
+
fixture
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const t = response.transcription;
|
|
116
|
+
if (responseFormat === "verbose_json" || t.words != null || t.segments != null) {
|
|
117
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
118
|
+
res.end(JSON.stringify({
|
|
119
|
+
task: "transcribe",
|
|
120
|
+
language: t.language ?? "english",
|
|
121
|
+
duration: t.duration ?? 0,
|
|
122
|
+
text: t.text,
|
|
123
|
+
words: t.words ?? [],
|
|
124
|
+
segments: t.segments ?? []
|
|
125
|
+
}));
|
|
126
|
+
} else {
|
|
127
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
128
|
+
res.end(JSON.stringify({ text: t.text }));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
exports.handleTranscription = handleTranscription;
|
|
134
|
+
//# sourceMappingURL=transcription.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcription.cjs","names":["getTestId","matchFixture","applyChaos","flattenHeaders","proxyAndRecord","isErrorResponse","isTranscriptionResponse"],"sources":["../src/transcription.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isTranscriptionResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Extract a named field value from a multipart/form-data body.\n * Lightweight parser — scans for Content-Disposition headers\n * to find simple string field values.\n */\nfunction extractFormField(raw: string, fieldName: string): string | undefined {\n const pattern = new RegExp(\n `Content-Disposition:\\\\s*form-data;[^\\\\r\\\\n]*name=\"${fieldName}\"[^\\\\r\\\\n]*\\\\r\\\\n\\\\r\\\\n([^\\\\r\\\\n]*)`,\n \"i\",\n );\n const match = raw.match(pattern);\n return match?.[1];\n}\n\nexport async function handleTranscription(\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 setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/transcriptions\";\n const method = req.method ?? \"POST\";\n\n const model = extractFormField(raw, \"model\") ?? \"whisper-1\";\n const responseFormat = extractFormField(raw, \"response_format\") ?? \"json\";\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [],\n _endpointType: \"transcription\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\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 { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\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 syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/transcriptions\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\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 journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\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 code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response is not a transcription type\",\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const t = response.transcription;\n const useVerbose = responseFormat === \"verbose_json\" || t.words != null || t.segments != null;\n\n if (useVerbose) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n task: \"transcribe\",\n language: t.language ?? \"english\",\n duration: t.duration ?? 0,\n text: t.text,\n words: t.words ?? [],\n segments: t.segments ?? [],\n }),\n );\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ text: t.text }));\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Journal } from "./journal.cjs";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.cjs";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
|
|
5
|
+
//#region src/transcription.d.ts
|
|
6
|
+
declare function handleTranscription(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=transcription.d.ts.map
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { handleTranscription };
|
|
11
|
+
//# sourceMappingURL=transcription.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcription.d.cts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBAuBsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Journal } from "./journal.js";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.js";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
|
|
5
|
+
//#region src/transcription.d.ts
|
|
6
|
+
declare function handleTranscription(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=transcription.d.ts.map
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { handleTranscription };
|
|
11
|
+
//# sourceMappingURL=transcription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcription.d.ts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBAuBsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { flattenHeaders, getTestId, isErrorResponse, isTranscriptionResponse } from "./helpers.js";
|
|
2
|
+
import { matchFixture } from "./router.js";
|
|
3
|
+
import { writeErrorResponse } from "./sse-writer.js";
|
|
4
|
+
import { applyChaos } from "./chaos.js";
|
|
5
|
+
import { proxyAndRecord } from "./recorder.js";
|
|
6
|
+
|
|
7
|
+
//#region src/transcription.ts
|
|
8
|
+
/**
|
|
9
|
+
* Extract a named field value from a multipart/form-data body.
|
|
10
|
+
* Lightweight parser — scans for Content-Disposition headers
|
|
11
|
+
* to find simple string field values.
|
|
12
|
+
*/
|
|
13
|
+
function extractFormField(raw, fieldName) {
|
|
14
|
+
const pattern = new RegExp(`Content-Disposition:\\s*form-data;[^\\r\\n]*name="${fieldName}"[^\\r\\n]*\\r\\n\\r\\n([^\\r\\n]*)`, "i");
|
|
15
|
+
return raw.match(pattern)?.[1];
|
|
16
|
+
}
|
|
17
|
+
async function handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
18
|
+
setCorsHeaders(res);
|
|
19
|
+
const path = req.url ?? "/v1/audio/transcriptions";
|
|
20
|
+
const method = req.method ?? "POST";
|
|
21
|
+
const model = extractFormField(raw, "model") ?? "whisper-1";
|
|
22
|
+
const responseFormat = extractFormField(raw, "response_format") ?? "json";
|
|
23
|
+
const syntheticReq = {
|
|
24
|
+
model,
|
|
25
|
+
messages: [],
|
|
26
|
+
_endpointType: "transcription"
|
|
27
|
+
};
|
|
28
|
+
const testId = getTestId(req);
|
|
29
|
+
const fixture = matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
30
|
+
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
31
|
+
if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
32
|
+
method,
|
|
33
|
+
path,
|
|
34
|
+
headers: flattenHeaders(req.headers),
|
|
35
|
+
body: syntheticReq
|
|
36
|
+
}, defaults.registry, defaults.logger)) return;
|
|
37
|
+
if (!fixture) {
|
|
38
|
+
if (defaults.record) {
|
|
39
|
+
if (await proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? "/v1/audio/transcriptions", fixtures, defaults, raw)) {
|
|
40
|
+
journal.add({
|
|
41
|
+
method,
|
|
42
|
+
path,
|
|
43
|
+
headers: flattenHeaders(req.headers),
|
|
44
|
+
body: syntheticReq,
|
|
45
|
+
response: {
|
|
46
|
+
status: res.statusCode ?? 200,
|
|
47
|
+
fixture: null
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const strictStatus = defaults.strict ? 503 : 404;
|
|
54
|
+
const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
|
|
55
|
+
journal.add({
|
|
56
|
+
method,
|
|
57
|
+
path,
|
|
58
|
+
headers: flattenHeaders(req.headers),
|
|
59
|
+
body: syntheticReq,
|
|
60
|
+
response: {
|
|
61
|
+
status: strictStatus,
|
|
62
|
+
fixture: null
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
|
|
66
|
+
message: strictMessage,
|
|
67
|
+
type: "invalid_request_error",
|
|
68
|
+
code: "no_fixture_match"
|
|
69
|
+
} }));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const response = fixture.response;
|
|
73
|
+
if (isErrorResponse(response)) {
|
|
74
|
+
const status = response.status ?? 500;
|
|
75
|
+
journal.add({
|
|
76
|
+
method,
|
|
77
|
+
path,
|
|
78
|
+
headers: flattenHeaders(req.headers),
|
|
79
|
+
body: syntheticReq,
|
|
80
|
+
response: {
|
|
81
|
+
status,
|
|
82
|
+
fixture
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
writeErrorResponse(res, status, JSON.stringify(response));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!isTranscriptionResponse(response)) {
|
|
89
|
+
journal.add({
|
|
90
|
+
method,
|
|
91
|
+
path,
|
|
92
|
+
headers: flattenHeaders(req.headers),
|
|
93
|
+
body: syntheticReq,
|
|
94
|
+
response: {
|
|
95
|
+
status: 500,
|
|
96
|
+
fixture
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
100
|
+
message: "Fixture response is not a transcription type",
|
|
101
|
+
type: "server_error"
|
|
102
|
+
} }));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
journal.add({
|
|
106
|
+
method,
|
|
107
|
+
path,
|
|
108
|
+
headers: flattenHeaders(req.headers),
|
|
109
|
+
body: syntheticReq,
|
|
110
|
+
response: {
|
|
111
|
+
status: 200,
|
|
112
|
+
fixture
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const t = response.transcription;
|
|
116
|
+
if (responseFormat === "verbose_json" || t.words != null || t.segments != null) {
|
|
117
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
118
|
+
res.end(JSON.stringify({
|
|
119
|
+
task: "transcribe",
|
|
120
|
+
language: t.language ?? "english",
|
|
121
|
+
duration: t.duration ?? 0,
|
|
122
|
+
text: t.text,
|
|
123
|
+
words: t.words ?? [],
|
|
124
|
+
segments: t.segments ?? []
|
|
125
|
+
}));
|
|
126
|
+
} else {
|
|
127
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
128
|
+
res.end(JSON.stringify({ text: t.text }));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { handleTranscription };
|
|
134
|
+
//# sourceMappingURL=transcription.js.map
|