@copilotkit/aimock 1.45.1 → 1.47.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/CHANGELOG.md +25 -1
- package/README.md +11 -2
- package/dist/agui-mock.cjs +25 -25
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +25 -25
- package/dist/agui-mock.js.map +1 -1
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/aimock-cli.cjs +15 -1
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +16 -2
- package/dist/aimock-cli.js.map +1 -1
- package/dist/api-key-auth.cjs +10 -9
- package/dist/api-key-auth.cjs.map +1 -1
- package/dist/api-key-auth.d.cts.map +1 -1
- package/dist/api-key-auth.d.ts.map +1 -1
- package/dist/api-key-auth.js +11 -10
- package/dist/api-key-auth.js.map +1 -1
- package/dist/aws-event-stream.cjs +20 -23
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +0 -1
- package/dist/aws-event-stream.d.ts +0 -1
- package/dist/aws-event-stream.js +20 -24
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/batches.cjs +2 -4
- package/dist/batches.cjs.map +1 -1
- package/dist/batches.d.cts.map +1 -1
- package/dist/batches.d.ts.map +1 -1
- package/dist/batches.js +2 -4
- package/dist/batches.js.map +1 -1
- package/dist/bedrock-converse.cjs +139 -161
- 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 +137 -159
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +175 -216
- 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 +172 -213
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +11 -10
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.d.cts.map +1 -1
- package/dist/byteplus-video.d.ts.map +1 -1
- package/dist/byteplus-video.js +5 -4
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +5 -4
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +5 -4
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +15 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +107 -180
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +106 -179
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +29 -1
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +7 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +7 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +29 -2
- package/dist/config-loader.js.map +1 -1
- package/dist/count-tokens.cjs +146 -0
- package/dist/count-tokens.cjs.map +1 -0
- package/dist/count-tokens.js +146 -0
- package/dist/count-tokens.js.map +1 -0
- package/dist/elevenlabs-audio.cjs +36 -34
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +33 -31
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/elevenlabs-voice.cjs +38 -37
- package/dist/elevenlabs-voice.cjs.map +1 -1
- package/dist/elevenlabs-voice.d.cts.map +1 -1
- package/dist/elevenlabs-voice.d.ts.map +1 -1
- package/dist/elevenlabs-voice.js +33 -32
- package/dist/elevenlabs-voice.js.map +1 -1
- package/dist/embedding-request.cjs +16 -0
- package/dist/embedding-request.cjs.map +1 -0
- package/dist/embedding-request.js +14 -0
- package/dist/embedding-request.js.map +1 -0
- package/dist/embeddings.cjs +30 -20
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +30 -20
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +45 -43
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +42 -40
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +17 -15
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.js +16 -14
- package/dist/fal.js.map +1 -1
- package/dist/files.cjs +2 -4
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +2 -4
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +2 -4
- package/dist/fine-tuning.cjs.map +1 -1
- package/dist/fine-tuning.d.cts.map +1 -1
- package/dist/fine-tuning.d.ts.map +1 -1
- package/dist/fine-tuning.js +2 -4
- package/dist/fine-tuning.js.map +1 -1
- package/dist/fixture-loader.cjs +140 -44
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +141 -46
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +26 -24
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +26 -24
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +113 -156
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts +0 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts +0 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +113 -156
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +295 -218
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +295 -219
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +14 -12
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.d.cts.map +1 -1
- package/dist/grok-video.d.ts.map +1 -1
- package/dist/grok-video.js +8 -6
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +411 -75
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +51 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +51 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +402 -74
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +30 -28
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +27 -25
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/journal.cjs +3 -1
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +2 -1
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +3 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +3 -0
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/mcp-handler.cjs +7 -3
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.js +7 -3
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-recorder.cjs +2 -1
- package/dist/mcp-recorder.cjs.map +1 -1
- package/dist/mcp-recorder.js +3 -2
- package/dist/mcp-recorder.js.map +1 -1
- package/dist/messages.cjs +235 -202
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +234 -202
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +5 -2
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +6 -3
- package/dist/metrics.js.map +1 -1
- package/dist/misbehavior.cjs +62 -3
- package/dist/misbehavior.cjs.map +1 -1
- package/dist/misbehavior.d.cts.map +1 -1
- package/dist/misbehavior.d.ts.map +1 -1
- package/dist/misbehavior.js +63 -4
- package/dist/misbehavior.js.map +1 -1
- package/dist/model-utils.cjs +1 -0
- package/dist/model-utils.cjs.map +1 -1
- package/dist/model-utils.js +1 -1
- package/dist/model-utils.js.map +1 -1
- package/dist/moderation.cjs +15 -12
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.js +14 -11
- package/dist/moderation.js.map +1 -1
- package/dist/ndjson-writer.cjs +21 -23
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +0 -1
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +0 -1
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +21 -24
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +276 -210
- 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 +273 -207
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +323 -3
- package/dist/openapi.cjs.map +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +324 -4
- package/dist/openapi.js.map +1 -1
- package/dist/openrouter-chat.cjs +11 -155
- package/dist/openrouter-chat.cjs.map +1 -1
- package/dist/openrouter-chat.js +11 -155
- package/dist/openrouter-chat.js.map +1 -1
- package/dist/openrouter-video.cjs +51 -74
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +37 -60
- package/dist/openrouter-video.js.map +1 -1
- package/dist/raw-validation.cjs +177 -0
- package/dist/raw-validation.cjs.map +1 -0
- package/dist/raw-validation.js +176 -0
- package/dist/raw-validation.js.map +1 -0
- package/dist/recorder.cjs +176 -95
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +176 -95
- package/dist/recorder.js.map +1 -1
- package/dist/rerank.cjs +13 -11
- package/dist/rerank.cjs.map +1 -1
- package/dist/rerank.js +12 -10
- package/dist/rerank.js.map +1 -1
- package/dist/response/catalog/anthropic-page.cjs +39 -0
- package/dist/response/catalog/anthropic-page.cjs.map +1 -0
- package/dist/response/catalog/anthropic-page.js +38 -0
- package/dist/response/catalog/anthropic-page.js.map +1 -0
- package/dist/response/catalog/collect.cjs +61 -0
- package/dist/response/catalog/collect.cjs.map +1 -0
- package/dist/response/catalog/collect.js +59 -0
- package/dist/response/catalog/collect.js.map +1 -0
- package/dist/response/catalog/gemini-page.cjs +25 -0
- package/dist/response/catalog/gemini-page.cjs.map +1 -0
- package/dist/response/catalog/gemini-page.js +24 -0
- package/dist/response/catalog/gemini-page.js.map +1 -0
- package/dist/response/catalog/render.cjs +182 -0
- package/dist/response/catalog/render.cjs.map +1 -0
- package/dist/response/catalog/render.js +171 -0
- package/dist/response/catalog/render.js.map +1 -0
- package/dist/response/deliver.cjs +144 -0
- package/dist/response/deliver.cjs.map +1 -0
- package/dist/response/deliver.js +143 -0
- package/dist/response/deliver.js.map +1 -0
- package/dist/response/errors.cjs +225 -0
- package/dist/response/errors.cjs.map +1 -0
- package/dist/response/errors.js +224 -0
- package/dist/response/errors.js.map +1 -0
- package/dist/response/finish.cjs +39 -0
- package/dist/response/finish.cjs.map +1 -0
- package/dist/response/finish.js +35 -0
- package/dist/response/finish.js.map +1 -0
- package/dist/response/frame-loop.cjs +102 -0
- package/dist/response/frame-loop.cjs.map +1 -0
- package/dist/response/frame-loop.js +100 -0
- package/dist/response/frame-loop.js.map +1 -0
- package/dist/response/lifecycle.cjs +223 -0
- package/dist/response/lifecycle.cjs.map +1 -0
- package/dist/response/lifecycle.js +211 -0
- package/dist/response/lifecycle.js.map +1 -0
- package/dist/response/policy.cjs +126 -0
- package/dist/response/policy.cjs.map +1 -0
- package/dist/response/policy.js +120 -0
- package/dist/response/policy.js.map +1 -0
- package/dist/response/profiles.cjs +69 -0
- package/dist/response/profiles.cjs.map +1 -0
- package/dist/response/profiles.js +65 -0
- package/dist/response/profiles.js.map +1 -0
- package/dist/response/proxy.cjs +71 -0
- package/dist/response/proxy.cjs.map +1 -0
- package/dist/response/proxy.js +70 -0
- package/dist/response/proxy.js.map +1 -0
- package/dist/response/raw.cjs +228 -0
- package/dist/response/raw.cjs.map +1 -0
- package/dist/response/raw.js +227 -0
- package/dist/response/raw.js.map +1 -0
- package/dist/response/timing.cjs +157 -0
- package/dist/response/timing.cjs.map +1 -0
- package/dist/response/timing.js +150 -0
- package/dist/response/timing.js.map +1 -0
- package/dist/response/usage/codecs/anthropic.cjs +52 -0
- package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
- package/dist/response/usage/codecs/anthropic.js +50 -0
- package/dist/response/usage/codecs/anthropic.js.map +1 -0
- package/dist/response/usage/codecs/cohere.cjs +36 -0
- package/dist/response/usage/codecs/cohere.cjs.map +1 -0
- package/dist/response/usage/codecs/cohere.js +35 -0
- package/dist/response/usage/codecs/cohere.js.map +1 -0
- package/dist/response/usage/codecs/converse.cjs +16 -0
- package/dist/response/usage/codecs/converse.cjs.map +1 -0
- package/dist/response/usage/codecs/converse.js +15 -0
- package/dist/response/usage/codecs/converse.js.map +1 -0
- package/dist/response/usage/codecs/gemini.cjs +31 -0
- package/dist/response/usage/codecs/gemini.cjs.map +1 -0
- package/dist/response/usage/codecs/gemini.js +31 -0
- package/dist/response/usage/codecs/gemini.js.map +1 -0
- package/dist/response/usage/codecs/interactions.cjs +22 -0
- package/dist/response/usage/codecs/interactions.cjs.map +1 -0
- package/dist/response/usage/codecs/interactions.js +21 -0
- package/dist/response/usage/codecs/interactions.js.map +1 -0
- package/dist/response/usage/codecs/ollama.cjs +17 -0
- package/dist/response/usage/codecs/ollama.cjs.map +1 -0
- package/dist/response/usage/codecs/ollama.js +17 -0
- package/dist/response/usage/codecs/ollama.js.map +1 -0
- package/dist/response/usage/codecs/openai.cjs +52 -0
- package/dist/response/usage/codecs/openai.cjs.map +1 -0
- package/dist/response/usage/codecs/openai.js +50 -0
- package/dist/response/usage/codecs/openai.js.map +1 -0
- package/dist/response/usage/codecs/openrouter.cjs +68 -0
- package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
- package/dist/response/usage/codecs/openrouter.js +67 -0
- package/dist/response/usage/codecs/openrouter.js.map +1 -0
- package/dist/response/usage/codecs/realtime.cjs +19 -0
- package/dist/response/usage/codecs/realtime.cjs.map +1 -0
- package/dist/response/usage/codecs/realtime.js +18 -0
- package/dist/response/usage/codecs/realtime.js.map +1 -0
- package/dist/response/usage/codecs/responses.cjs +21 -0
- package/dist/response/usage/codecs/responses.cjs.map +1 -0
- package/dist/response/usage/codecs/responses.js +20 -0
- package/dist/response/usage/codecs/responses.js.map +1 -0
- package/dist/response/usage/estimate.cjs +23 -0
- package/dist/response/usage/estimate.cjs.map +1 -0
- package/dist/response/usage/estimate.js +21 -0
- package/dist/response/usage/estimate.js.map +1 -0
- package/dist/response/usage/fields.cjs +72 -0
- package/dist/response/usage/fields.cjs.map +1 -0
- package/dist/response/usage/fields.js +70 -0
- package/dist/response/usage/fields.js.map +1 -0
- package/dist/response/usage/recording.cjs +52 -0
- package/dist/response/usage/recording.cjs.map +1 -0
- package/dist/response/usage/recording.js +52 -0
- package/dist/response/usage/recording.js.map +1 -0
- package/dist/responses.cjs +304 -196
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +303 -195
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +45 -0
- package/dist/route-registry.cjs.map +1 -1
- package/dist/route-registry.d.cts.map +1 -1
- package/dist/route-registry.d.ts.map +1 -1
- package/dist/route-registry.js +41 -1
- package/dist/route-registry.js.map +1 -1
- package/dist/router.cjs +24 -4
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +7 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +24 -5
- package/dist/router.js.map +1 -1
- package/dist/search.cjs +13 -11
- package/dist/search.cjs.map +1 -1
- package/dist/search.js +12 -10
- package/dist/search.js.map +1 -1
- package/dist/server.cjs +293 -248
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +294 -249
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +20 -18
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +19 -17
- package/dist/speech.js.map +1 -1
- package/dist/sse-writer.cjs +56 -30
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +0 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +0 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +55 -31
- package/dist/sse-writer.js.map +1 -1
- package/dist/stream-collapse.cjs +118 -11
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +10 -9
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +10 -9
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +118 -11
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +31 -43
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +31 -43
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +231 -9
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +231 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vector-mock.cjs +3 -8
- package/dist/vector-mock.cjs.map +1 -1
- package/dist/vector-mock.d.cts.map +1 -1
- package/dist/vector-mock.d.ts.map +1 -1
- package/dist/vector-mock.js +3 -8
- package/dist/vector-mock.js.map +1 -1
- package/dist/vector-stores.cjs +2 -4
- package/dist/vector-stores.cjs.map +1 -1
- package/dist/vector-stores.d.cts.map +1 -1
- package/dist/vector-stores.d.ts.map +1 -1
- package/dist/vector-stores.js +2 -4
- package/dist/vector-stores.js.map +1 -1
- package/dist/veo-video.cjs +16 -14
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +10 -8
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +21 -19
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +20 -18
- package/dist/video.js.map +1 -1
- package/dist/ws-framing.cjs +12 -6
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +2 -0
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +2 -0
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +12 -6
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +131 -96
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +0 -1
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +0 -1
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +130 -95
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-live.cjs +6 -5
- package/dist/ws-live.cjs.map +1 -1
- package/dist/ws-live.js +5 -4
- package/dist/ws-live.js.map +1 -1
- package/dist/ws-realtime.cjs +108 -124
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +105 -121
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +80 -59
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +79 -58
- package/dist/ws-responses.js.map +1 -1
- package/package.json +4 -1
- package/skills/write-fixtures/SKILL.md +57 -16
package/dist/server.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { isAnthropicModelRequest, missingRequiredHeader } from "./response/profiles.js";
|
|
2
|
+
import { renderError } from "./response/errors.js";
|
|
1
3
|
import { DEFAULT_TEST_ID } from "./constants.js";
|
|
2
4
|
import { build, msg, quote } from "./message-text.js";
|
|
3
5
|
import { markAuthenticatedRequest, resolveInboundAuth, validateRequestApiKey, writeApiKeyHttpRejection, writeApiKeyUpgradeRejection } from "./api-key-auth.js";
|
|
4
|
-
import {
|
|
6
|
+
import { parsePolicy, resolvePolicy, resolveRequestDecisions, strictOverrideField } from "./response/policy.js";
|
|
7
|
+
import { RequestBodyTooLargeError, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildOpenAIContentFilterChunks, buildOpenAIContentFilterCompletion, buildOpenAIReasoningChunks, buildOpenAIReasoningCompletion, buildOpenAIRefusalChunks, buildOpenAIRefusalCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, createServerFixtureList, describeMatch, extractOverrides, fixtureToolCallErrorCode, flattenHeaders, getContext, getTestId, googleFixtureToolCallErrorDetails, isAudioResponse, isCombinedFixtureResponse, isEmbeddingResponse, isErrorResponse, isExtendedResponsesToolsList, isFixtureToolCallError, isImageResponse, isJSONResponse, isJsonObject, isRawResponse, isResponseFactory, isServerOwnedFixtures, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, markMintedRequestId, prepareOpenAIChatMisbehavior, readBody, readBodyBufferBounded, rejectGeneratedFiles, requireEmittedFunctionToolCalls, requireServedFunctionToolCalls, resolveFixtureBlockOutcome, resolveOpenAIChatMisbehaviorUsage, resolveReasoningForModel, resolveRequestId, resolveResponse, resolveTestId, resolveUsage, runWithToolArgumentsScope, serializeErrorResponse, strictNoMatchLogLine, strictNoMatchMessage, validateToolsField, withoutResponsesToolKeys, wouldProxyMiss } from "./helpers.js";
|
|
5
8
|
import { normalizeLiveOptions } from "./live-fixture.js";
|
|
6
|
-
import {
|
|
9
|
+
import { sseCodec, writeErrorResponse } from "./sse-writer.js";
|
|
7
10
|
import { CHAOS_FIELDS, CHAOS_FIELD_NAMES, applyChaosAction, awaitChaosLatency, describeUnwritableReason, evaluateChaos, isChaosScope, parseChaosField, resetChaosWarnings, resolveChaosConfig, responseGoneReason } from "./chaos.js";
|
|
8
11
|
import { MISBEHAVIOR_CATALOG, WIRE_SUPPORT, getFixtureMisbehaviorPosition, parseMisbehavior, parseMisbehaviorHeader, planMisbehavior, recordMisbehaviorOutcome, resolveMisbehaviorSeed, resolveMisbehaviorShortCircuit, setFixtureMisbehaviorPosition } from "./misbehavior.js";
|
|
9
12
|
import { FixtureLoadError, INJECTED_STATUS_RANGE, MisbehaviorConfigError, claimOneShotError, clearFixtureQueue, enableHeldFixtureMisbehavior, entryToFixture, isInjectableStatus, isOneShotError, markFixtureResponsesToolsExtended, queueOneShotError, releaseOneShotError, responsesToolsExtendedView, responsesToolsViewSource, validateFixtures } from "./fixture-loader.js";
|
|
@@ -11,11 +14,20 @@ import { matchFixtureDiagnostic, recordMatchOptions } from "./router.js";
|
|
|
11
14
|
import { proxyAndRecord } from "./recorder.js";
|
|
12
15
|
import { Logger } from "./logger.js";
|
|
13
16
|
import { Journal } from "./journal.js";
|
|
14
|
-
import {
|
|
17
|
+
import { CLIENT_ABORTED } from "./response/lifecycle.js";
|
|
18
|
+
import { extendRecordedTimings, misbehaviorPayloadLength } from "./response/timing.js";
|
|
19
|
+
import { deliverJson, deliverStream } from "./response/deliver.js";
|
|
20
|
+
import { proxyInterruption, withHangUpDetection } from "./response/proxy.js";
|
|
21
|
+
import { collect, idsFor } from "./response/catalog/collect.js";
|
|
22
|
+
import { coreModelIds, renderAnthropicModel, renderAnthropicModels, renderGeminiModel, renderGeminiModels, renderOllamaTags, renderOpenAIModel, renderOpenAIModels } from "./response/catalog/render.js";
|
|
15
23
|
import { buildOpenRouterCandidates, handleOpenRouterCredits, handleOpenRouterKey, handleOpenRouterModels, isOpenRouterPath, resolveOpenRouterShaping, serializeOpenRouterError, shapeOpenRouterChunks, shapeOpenRouterCompletion } from "./openrouter-chat.js";
|
|
24
|
+
import { serveRawResponse } from "./response/raw.js";
|
|
25
|
+
import { anthropicPage } from "./response/catalog/anthropic-page.js";
|
|
26
|
+
import { geminiPage } from "./response/catalog/gemini-page.js";
|
|
16
27
|
import { handleResponses } from "./responses.js";
|
|
17
28
|
import { handleMessages } from "./messages.js";
|
|
18
29
|
import { handleGemini } from "./gemini.js";
|
|
30
|
+
import { handleCountTokens } from "./count-tokens.js";
|
|
19
31
|
import { handleGeminiEmbedContent } from "./gemini-embeddings.js";
|
|
20
32
|
import { handleBedrock, handleBedrockStream } from "./bedrock.js";
|
|
21
33
|
import { handleConverse, handleConverseStream } from "./bedrock-converse.js";
|
|
@@ -47,7 +59,7 @@ import { handleLiveSession } from "./ws-live.js";
|
|
|
47
59
|
import { handleWebSocketResponses } from "./ws-responses.js";
|
|
48
60
|
import { handleWebSocketRealtime } from "./ws-realtime.js";
|
|
49
61
|
import { handleWebSocketGeminiLive } from "./ws-gemini-live.js";
|
|
50
|
-
import { AZURE_DEPLOYMENT_RE, BATCHES_CANCEL_RE, BATCHES_ID_RE, BATCHES_PATH, BEDROCK_CONVERSE_RE, BEDROCK_CONVERSE_STREAM_RE, BEDROCK_INVOKE_RE, BEDROCK_STREAM_RE, BYTEPLUS_VIDEO_STATUS_RE, BYTEPLUS_VIDEO_SUBMIT_RE, COHERE_CHAT_PATH, COHERE_EMBED_PATH, COMPLETIONS_PATH, CONTROL_PREFIX, ELEVENLABS_MUSIC_RE, ELEVENLABS_SOUND_GENERATION_PATH, ELEVENLABS_TTS_RE, ELEVENLABS_VOICE_CREATE_PATH, ELEVENLABS_VOICE_DESIGN_PATH, ELEVENLABS_VOICE_RE, EMBEDDINGS_PATH, FAL_QUEUE_REQUESTS_RE, FAL_QUEUE_SUBMIT_RE, FAL_ROUTE_RE, FAL_RUN_RE, FILES_CONTENT_RE, FILES_ID_RE, FILES_PATH, FINE_TUNING_CANCEL_RE, FINE_TUNING_EVENTS_RE, FINE_TUNING_ID_RE, FINE_TUNING_JOBS_PATH, GEMINI_EMBED_RE, GEMINI_INTERACTIONS_PATH, GEMINI_LIVE_PATH, GEMINI_PATH_RE, GEMINI_PREDICT_RE, GROK_VIDEO_STATUS_RE, GROK_VIDEO_SUBMIT_PATH, HEALTH_PATH, IMAGES_EDIT_PATH, IMAGES_PATH, IMAGES_VARIATIONS_PATH, LIVE_PATH, MESSAGES_PATH, METRICS_PATH, MODELS_PATH, MODERATIONS_PATH, OLLAMA_CHAT_PATH, OLLAMA_EMBEDDINGS_PATH, OLLAMA_EMBED_PATH, OLLAMA_GENERATE_PATH, OLLAMA_TAGS_PATH, OPENROUTER_CREDITS_PATH, OPENROUTER_KEY_PATH, OPENROUTER_MODELS_PATH, OPENROUTER_VIDEOS_PATH, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_MODELS_PATH, OPENROUTER_VIDEO_STATUS_RE, READY_PATH, REALTIME_PATH, REQUESTS_PATH, RERANK_PATH, RESPONSES_PATH, SEARCH_PATH, SPEECH_PATH, TRANSCRIPTIONS_PATH, TRANSLATIONS_PATH, VECTOR_STORES_BATCH_CANCEL_RE, VECTOR_STORES_BATCH_FILES_RE, VECTOR_STORES_BATCH_RE, VECTOR_STORES_FILES_RE, VECTOR_STORES_FILE_BATCHES_RE, VECTOR_STORES_FILE_RE, VECTOR_STORES_ID_RE, VECTOR_STORES_PATH, VECTOR_STORES_SEARCH_RE, VEO_OPERATION_RE, VEO_PREDICT_LRO_RE, VERTEX_AI_RE, VIDEOS_PATH, matchRouteDefinition } from "./route-registry.js";
|
|
62
|
+
import { AZURE_DEPLOYMENT_RE, BATCHES_CANCEL_RE, BATCHES_ID_RE, BATCHES_PATH, BEDROCK_CONVERSE_RE, BEDROCK_CONVERSE_STREAM_RE, BEDROCK_INVOKE_RE, BEDROCK_STREAM_RE, BYTEPLUS_VIDEO_STATUS_RE, BYTEPLUS_VIDEO_SUBMIT_RE, COHERE_CHAT_PATH, COHERE_EMBED_PATH, COMPLETIONS_PATH, CONTROL_PREFIX, ELEVENLABS_MUSIC_RE, ELEVENLABS_SOUND_GENERATION_PATH, ELEVENLABS_TTS_RE, ELEVENLABS_VOICE_CREATE_PATH, ELEVENLABS_VOICE_DESIGN_PATH, ELEVENLABS_VOICE_RE, EMBEDDINGS_PATH, FAL_QUEUE_REQUESTS_RE, FAL_QUEUE_SUBMIT_RE, FAL_ROUTE_RE, FAL_RUN_RE, FILES_CONTENT_RE, FILES_ID_RE, FILES_PATH, FINE_TUNING_CANCEL_RE, FINE_TUNING_EVENTS_RE, FINE_TUNING_ID_RE, FINE_TUNING_JOBS_PATH, GEMINI_COUNT_RE, GEMINI_EMBED_RE, GEMINI_INTERACTIONS_PATH, GEMINI_LIVE_PATH, GEMINI_MODELS_PATH, GEMINI_MODEL_DETAIL_RE, GEMINI_PATH_RE, GEMINI_PREDICT_RE, GROK_VIDEO_STATUS_RE, GROK_VIDEO_SUBMIT_PATH, HEALTH_PATH, IMAGES_EDIT_PATH, IMAGES_PATH, IMAGES_VARIATIONS_PATH, LIVE_PATH, MESSAGES_COUNT_PATH, MESSAGES_PATH, METRICS_PATH, MODELS_DETAIL_RE, MODELS_PATH, MODERATIONS_PATH, OLLAMA_CHAT_PATH, OLLAMA_EMBEDDINGS_PATH, OLLAMA_EMBED_PATH, OLLAMA_GENERATE_PATH, OLLAMA_TAGS_PATH, OPENROUTER_CREDITS_PATH, OPENROUTER_KEY_PATH, OPENROUTER_MODELS_PATH, OPENROUTER_VIDEOS_PATH, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_MODELS_PATH, OPENROUTER_VIDEO_STATUS_RE, READY_PATH, REALTIME_PATH, REQUESTS_PATH, RERANK_PATH, RESPONSES_PATH, SEARCH_PATH, SPEECH_PATH, TRANSCRIPTIONS_PATH, TRANSLATIONS_PATH, VECTOR_STORES_BATCH_CANCEL_RE, VECTOR_STORES_BATCH_FILES_RE, VECTOR_STORES_BATCH_RE, VECTOR_STORES_FILES_RE, VECTOR_STORES_FILE_BATCHES_RE, VECTOR_STORES_FILE_RE, VECTOR_STORES_ID_RE, VECTOR_STORES_PATH, VECTOR_STORES_SEARCH_RE, VEO_OPERATION_RE, VEO_PREDICT_LRO_RE, VERTEX_AI_RE, VIDEOS_PATH, matchRouteDefinition } from "./route-registry.js";
|
|
51
63
|
import { CATALOG_ROUTES, buildOpenApiDocument } from "./openapi.js";
|
|
52
64
|
import { DESTROYED_STATUS_LABEL, createMetricsRegistry, normalizePathLabel } from "./metrics.js";
|
|
53
65
|
import { MCP_FAKES_DEFAULT_MOUNT, McpFakeStore, McpFakesAddError, blockIdOf } from "./mcp-fakes.js";
|
|
@@ -87,13 +99,6 @@ function normalizeCompatPath(pathname, logger) {
|
|
|
87
99
|
}
|
|
88
100
|
return pathname;
|
|
89
101
|
}
|
|
90
|
-
const DEFAULT_MODELS = [
|
|
91
|
-
"gpt-4",
|
|
92
|
-
"gpt-4o",
|
|
93
|
-
"claude-3-5-sonnet-20241022",
|
|
94
|
-
"gemini-2.0-flash",
|
|
95
|
-
"text-embedding-3-small"
|
|
96
|
-
];
|
|
97
102
|
const CORS_HEADERS = {
|
|
98
103
|
"Access-Control-Allow-Origin": "*",
|
|
99
104
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
@@ -110,10 +115,10 @@ function handleOptions(res) {
|
|
|
110
115
|
}
|
|
111
116
|
function handleNotFound(res, message) {
|
|
112
117
|
setCorsHeaders(res);
|
|
113
|
-
writeErrorResponse(res, 404,
|
|
118
|
+
writeErrorResponse(res, 404, renderError("openai", "route-failure", {
|
|
114
119
|
message,
|
|
115
120
|
type: "not_found"
|
|
116
|
-
}
|
|
121
|
+
}).body);
|
|
117
122
|
}
|
|
118
123
|
/** The complete `GET /__aimock/fixtures` query-param vocabulary; anything else 400s. */
|
|
119
124
|
const FIXTURES_PARAMS = new Set(["include"]);
|
|
@@ -210,6 +215,7 @@ function fixtureResponseKind(response) {
|
|
|
210
215
|
if (isCombinedFixtureResponse(response)) return "contentWithToolCalls";
|
|
211
216
|
if (isToolCallResponse(response)) return "toolCalls";
|
|
212
217
|
if (isTextResponse(response)) return "text";
|
|
218
|
+
if (isRawResponse(response)) return "raw";
|
|
213
219
|
return "unknown";
|
|
214
220
|
}
|
|
215
221
|
/**
|
|
@@ -895,7 +901,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
895
901
|
const clientAbort = clientAbortedMidBody(req, res, err);
|
|
896
902
|
const bodyLimitDisconnect = clientFault && (req.destroyed || res.destroyed);
|
|
897
903
|
if (clientAbort || bodyLimitDisconnect) {
|
|
898
|
-
const interruptReason = bodyLimitDisconnect ? "request body exceeded size limit" :
|
|
904
|
+
const interruptReason = bodyLimitDisconnect ? "request body exceeded size limit" : CLIENT_ABORTED;
|
|
899
905
|
defaults.logger.warn(bodyLimitDisconnect ? `${route}: ${msg}` : `${route}: client aborted mid-body (${msg})`);
|
|
900
906
|
journal.add({
|
|
901
907
|
method: req.method ?? "POST",
|
|
@@ -924,10 +930,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
924
930
|
fixture: null
|
|
925
931
|
}
|
|
926
932
|
});
|
|
927
|
-
writeErrorResponse(res, status, openRouter ? serializeOpenRouterError(status, `Request body read failed: ${msg}`) :
|
|
933
|
+
writeErrorResponse(res, status, openRouter ? serializeOpenRouterError(status, `Request body read failed: ${msg}`) : renderError("openai", "transport-failure", {
|
|
928
934
|
message: `Request body read failed: ${msg}`,
|
|
929
935
|
type: clientFault ? "invalid_request_error" : "server_error"
|
|
930
|
-
}
|
|
936
|
+
}).body);
|
|
931
937
|
return;
|
|
932
938
|
}
|
|
933
939
|
let body;
|
|
@@ -945,12 +951,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
945
951
|
fixture: null
|
|
946
952
|
}
|
|
947
953
|
});
|
|
948
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, `Malformed JSON: ${detail}`) :
|
|
954
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, `Malformed JSON: ${detail}`) : renderError("openai", "validation", {
|
|
949
955
|
message: `Malformed JSON: ${detail}`,
|
|
950
956
|
type: "invalid_request_error",
|
|
951
957
|
param: null,
|
|
952
|
-
code: "invalid_json"
|
|
953
|
-
|
|
958
|
+
code: "invalid_json",
|
|
959
|
+
includeParam: true
|
|
960
|
+
}).body);
|
|
954
961
|
return;
|
|
955
962
|
}
|
|
956
963
|
if (!isJsonObject(body)) {
|
|
@@ -964,10 +971,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
964
971
|
fixture: null
|
|
965
972
|
}
|
|
966
973
|
});
|
|
967
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Request body must be a JSON object") :
|
|
974
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Request body must be a JSON object") : renderError("openai", "validation", {
|
|
968
975
|
message: "Request body must be a JSON object",
|
|
969
976
|
type: "invalid_request_error"
|
|
970
|
-
}
|
|
977
|
+
}).body);
|
|
971
978
|
return;
|
|
972
979
|
}
|
|
973
980
|
if (modelFallback && !body.model) body.model = modelFallback;
|
|
@@ -982,12 +989,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
982
989
|
fixture: null
|
|
983
990
|
}
|
|
984
991
|
});
|
|
985
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Missing required parameter: 'messages'") :
|
|
992
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Missing required parameter: 'messages'") : renderError("openai", "validation", {
|
|
986
993
|
message: "Missing required parameter: 'messages'",
|
|
987
994
|
type: "invalid_request_error",
|
|
988
995
|
param: null,
|
|
989
|
-
code: null
|
|
990
|
-
|
|
996
|
+
code: null,
|
|
997
|
+
includeParam: true
|
|
998
|
+
}).body);
|
|
991
999
|
return;
|
|
992
1000
|
}
|
|
993
1001
|
const nullMessageIndex = body.messages.findIndex((message) => message === null);
|
|
@@ -1003,12 +1011,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1003
1011
|
fixture: null
|
|
1004
1012
|
}
|
|
1005
1013
|
});
|
|
1006
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, message) :
|
|
1014
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, message) : renderError("openai", "validation", {
|
|
1007
1015
|
message,
|
|
1008
1016
|
type: "invalid_request_error",
|
|
1009
1017
|
param: null,
|
|
1010
|
-
code: null
|
|
1011
|
-
|
|
1018
|
+
code: null,
|
|
1019
|
+
includeParam: true
|
|
1020
|
+
}).body);
|
|
1012
1021
|
return;
|
|
1013
1022
|
}
|
|
1014
1023
|
if (new URL(req.url ?? COMPLETIONS_PATH, "http://localhost").pathname === COMPLETIONS_PATH) {
|
|
@@ -1028,10 +1037,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1028
1037
|
fixture: null
|
|
1029
1038
|
}
|
|
1030
1039
|
});
|
|
1031
|
-
writeErrorResponse(res, 400,
|
|
1040
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
1032
1041
|
message: toolsError,
|
|
1033
1042
|
type: "invalid_request_error"
|
|
1034
|
-
}
|
|
1043
|
+
}).body);
|
|
1035
1044
|
return;
|
|
1036
1045
|
}
|
|
1037
1046
|
}
|
|
@@ -1041,7 +1050,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1041
1050
|
body._endpointType = "chat";
|
|
1042
1051
|
body._context = getContext(req);
|
|
1043
1052
|
const testId = getTestId(req);
|
|
1044
|
-
const matchOptions =
|
|
1053
|
+
const matchOptions = {
|
|
1054
|
+
...recordMatchOptions(!!(defaults.record && providerKey), defaults.logger),
|
|
1055
|
+
wire: "openai-chat"
|
|
1056
|
+
};
|
|
1045
1057
|
let fixture = null;
|
|
1046
1058
|
let skippedBySequenceOrTurn = 0;
|
|
1047
1059
|
let preResolvedResponse = null;
|
|
@@ -1071,10 +1083,11 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1071
1083
|
skippedBySequenceOrTurn = Math.max(skippedBySequenceOrTurn, attempt.skippedBySequenceOrTurn);
|
|
1072
1084
|
if (!attempt.fixture) return null;
|
|
1073
1085
|
const response = await resolveResponse(attempt.fixture, probe);
|
|
1086
|
+
const isError = isErrorResponse(response) || isRawResponse(response) && (response.raw.status ?? 200) >= 400;
|
|
1074
1087
|
return {
|
|
1075
1088
|
fixture: attempt.fixture,
|
|
1076
1089
|
response,
|
|
1077
|
-
isError
|
|
1090
|
+
isError
|
|
1078
1091
|
};
|
|
1079
1092
|
};
|
|
1080
1093
|
for (const candidate of candidates) {
|
|
@@ -1088,7 +1101,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1088
1101
|
matchCounts = journal.getFixtureMatchCountsForTest(testId);
|
|
1089
1102
|
responseModel = candidate;
|
|
1090
1103
|
if (outcome.isError) {
|
|
1091
|
-
const errorFallsThrough = outcome.response.fallthrough !== false;
|
|
1104
|
+
const errorFallsThrough = !isErrorResponse(outcome.response) || outcome.response.fallthrough !== false;
|
|
1092
1105
|
if (allowFallbacks && errorFallsThrough) {
|
|
1093
1106
|
lastErrorFixture = outcome.fixture;
|
|
1094
1107
|
lastErrorResponse = outcome.response;
|
|
@@ -1131,8 +1144,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1131
1144
|
headers: flatHeaders,
|
|
1132
1145
|
body
|
|
1133
1146
|
};
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1147
|
+
const missDecisions = resolveRequestDecisions(defaults.strict, req.headers);
|
|
1148
|
+
const effectiveStrict = missDecisions.fixtureMiss;
|
|
1149
|
+
const missWouldProxy = wouldProxyMiss(!missDecisions.upstream, defaults.record, providerKey);
|
|
1136
1150
|
const noFixtureSource = missWouldProxy ? "proxy" : "internal";
|
|
1137
1151
|
const recordBypass = (reason) => {
|
|
1138
1152
|
const entry = getRequestEntry();
|
|
@@ -1179,12 +1193,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1179
1193
|
...strictOverrideField(defaults.strict, req.headers)
|
|
1180
1194
|
}
|
|
1181
1195
|
});
|
|
1182
|
-
writeErrorResponse(res, strictStatus, openRouter ? serializeOpenRouterError(strictStatus, strictMessage) :
|
|
1196
|
+
writeErrorResponse(res, strictStatus, openRouter ? serializeOpenRouterError(strictStatus, strictMessage) : renderError("openai", "strict-miss", {
|
|
1183
1197
|
message: strictMessage,
|
|
1184
1198
|
type: "invalid_request_error",
|
|
1185
1199
|
param: null,
|
|
1186
|
-
code: "no_fixture_match"
|
|
1187
|
-
|
|
1200
|
+
code: "no_fixture_match",
|
|
1201
|
+
includeParam: true
|
|
1202
|
+
}).body);
|
|
1188
1203
|
return;
|
|
1189
1204
|
}
|
|
1190
1205
|
if (defaults.record && providerKey) {
|
|
@@ -1203,22 +1218,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1203
1218
|
});
|
|
1204
1219
|
}
|
|
1205
1220
|
} : void 0;
|
|
1206
|
-
|
|
1207
|
-
const noteClientHangUp = () => {
|
|
1208
|
-
if (!res.writableEnded) clientHungUp = true;
|
|
1209
|
-
};
|
|
1210
|
-
req.socket?.on("end", noteClientHangUp);
|
|
1211
|
-
req.socket?.on("error", noteClientHangUp);
|
|
1212
|
-
let outcome;
|
|
1213
|
-
try {
|
|
1214
|
-
outcome = await proxyAndRecord(req, res, body, providerKey, req.url ?? COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions);
|
|
1215
|
-
} finally {
|
|
1216
|
-
req.socket?.off("end", noteClientHangUp);
|
|
1217
|
-
req.socket?.off("error", noteClientHangUp);
|
|
1218
|
-
}
|
|
1221
|
+
const { outcome, clientHungUp } = await withHangUpDetection(req, res, () => proxyAndRecord(req, res, body, providerKey, req.url ?? COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions));
|
|
1219
1222
|
if (outcome === "handled_by_hook") return;
|
|
1220
1223
|
if (outcome !== "not_configured") {
|
|
1221
|
-
const destroyedMidStream = res.destroyed && !res.writableEnded;
|
|
1222
1224
|
journal.add({
|
|
1223
1225
|
method: req.method ?? "POST",
|
|
1224
1226
|
path: req.url ?? COMPLETIONS_PATH,
|
|
@@ -1228,10 +1230,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1228
1230
|
status: res.statusCode ?? 200,
|
|
1229
1231
|
fixture: null,
|
|
1230
1232
|
source: "proxy",
|
|
1231
|
-
...
|
|
1232
|
-
interrupted: true,
|
|
1233
|
-
interruptReason: clientHungUp ? "client aborted" : "proxy stream destroyed"
|
|
1234
|
-
} : {}
|
|
1233
|
+
...proxyInterruption(res, clientHungUp)
|
|
1235
1234
|
}
|
|
1236
1235
|
});
|
|
1237
1236
|
recordBypass("proxied");
|
|
@@ -1254,12 +1253,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1254
1253
|
...strictOverrideField(defaults.strict, req.headers)
|
|
1255
1254
|
}
|
|
1256
1255
|
});
|
|
1257
|
-
writeErrorResponse(res, 404, openRouter ? serializeOpenRouterError(404, "No fixture matched") :
|
|
1256
|
+
writeErrorResponse(res, 404, openRouter ? serializeOpenRouterError(404, "No fixture matched") : renderError("openai", "fixture-miss", {
|
|
1258
1257
|
message: "No fixture matched",
|
|
1259
1258
|
type: "invalid_request_error",
|
|
1260
1259
|
param: null,
|
|
1261
|
-
code: "no_fixture_match"
|
|
1262
|
-
|
|
1260
|
+
code: "no_fixture_match",
|
|
1261
|
+
includeParam: true
|
|
1262
|
+
}).body);
|
|
1263
1263
|
return;
|
|
1264
1264
|
}
|
|
1265
1265
|
const resolvedResponse = preResolvedResponse ?? await resolveResponse(fixture, body);
|
|
@@ -1290,12 +1290,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1290
1290
|
testId
|
|
1291
1291
|
});
|
|
1292
1292
|
if (!evaluation.summary?.evaluations.some((row) => row.outcome === "error")) defaults.logger.error(`${evaluation.code}: ${evaluation.message}`);
|
|
1293
|
-
writeErrorResponse(res, evaluation.status, openRouter ? serializeOpenRouterError(evaluation.status, `${evaluation.code}: ${evaluation.message}`) :
|
|
1293
|
+
writeErrorResponse(res, evaluation.status, openRouter ? serializeOpenRouterError(evaluation.status, `${evaluation.code}: ${evaluation.message}`) : renderError("openai", "misbehavior", {
|
|
1294
1294
|
message: evaluation.message,
|
|
1295
1295
|
type: "invalid_request_error",
|
|
1296
1296
|
param: null,
|
|
1297
|
-
code: evaluation.code
|
|
1298
|
-
|
|
1297
|
+
code: evaluation.code,
|
|
1298
|
+
includeParam: true
|
|
1299
|
+
}).body);
|
|
1299
1300
|
return;
|
|
1300
1301
|
}
|
|
1301
1302
|
const appliedPlan = evaluation.kind === "applied" ? prepareOpenAIChatMisbehavior(evaluation) : void 0;
|
|
@@ -1309,21 +1310,30 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1309
1310
|
testId
|
|
1310
1311
|
});
|
|
1311
1312
|
};
|
|
1312
|
-
const appliedReasoning = appliedPlan && appliedPlan.reasoning === void 0 && (isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response)) ? resolveReasoningForModel(response.reasoning, responseModel,
|
|
1313
|
+
const appliedReasoning = appliedPlan && appliedPlan.reasoning === void 0 && (isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response)) ? resolveReasoningForModel(response.reasoning, responseModel, resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression, defaults.logger) : void 0;
|
|
1313
1314
|
const appliedUsage = appliedPlan ? resolveOpenAIChatMisbehaviorUsage(appliedPlan, body, appliedPlan.reasoning === void 0 ? appliedReasoning !== void 0 : openRouter) : void 0;
|
|
1314
1315
|
const latency = fixture.latency ?? defaults.latency;
|
|
1315
1316
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
1316
1317
|
const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
|
|
1317
1318
|
const streamingPromptText = body.messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("");
|
|
1318
1319
|
const resolveStreamingUsageTokens = (overrides, completionText) => appliedUsage ?? resolveUsage(overrides, streamingPromptText, completionText);
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1320
|
+
const nonStreamingOpts = (payloadLength) => ({
|
|
1321
|
+
surface: "openai-chat",
|
|
1322
|
+
payloadLength,
|
|
1323
|
+
chunkSize,
|
|
1324
|
+
recordedTimings: fixture.recordedTimings,
|
|
1325
|
+
duplicateId: !!appliedPlan?.duplicateId,
|
|
1326
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
|
|
1327
|
+
});
|
|
1328
|
+
const streamTiming = (frameCount) => ({
|
|
1329
|
+
latency,
|
|
1330
|
+
streamingProfile: fixture.streamingProfile,
|
|
1331
|
+
recordedTimings: extendRecordedTimings(fixture.recordedTimings, frameCount, !!appliedPlan?.duplicateId),
|
|
1332
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
|
|
1333
|
+
});
|
|
1334
|
+
const commitNonStream = async (entry, reply, payloadLength) => {
|
|
1335
|
+
recordOutcome(entry);
|
|
1336
|
+
await deliverJson(res, entry, fixture, () => reply, nonStreamingOpts(payloadLength));
|
|
1327
1337
|
};
|
|
1328
1338
|
const shapeORCompletion = (completion, overrides) => {
|
|
1329
1339
|
if (appliedUsage) completion.usage = appliedUsage;
|
|
@@ -1337,6 +1347,24 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1337
1347
|
if (usageChunk) shapeOpenRouterChunks([usageChunk], shaping, overrides?.id !== void 0);
|
|
1338
1348
|
};
|
|
1339
1349
|
const openRouterProcessing = !!(openRouter && fixture.openRouterProcessing);
|
|
1350
|
+
if (isRawResponse(response)) {
|
|
1351
|
+
await serveRawResponse({
|
|
1352
|
+
wire: "openai-chat",
|
|
1353
|
+
stream: body.stream === true,
|
|
1354
|
+
req,
|
|
1355
|
+
res,
|
|
1356
|
+
fixture,
|
|
1357
|
+
response,
|
|
1358
|
+
body,
|
|
1359
|
+
journal,
|
|
1360
|
+
defaults,
|
|
1361
|
+
testId,
|
|
1362
|
+
misbehaviorSummary: evaluation.summary,
|
|
1363
|
+
errorWire: "openai",
|
|
1364
|
+
openRouter
|
|
1365
|
+
});
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1340
1368
|
if (isErrorResponse(response)) {
|
|
1341
1369
|
const status = response.status ?? 500;
|
|
1342
1370
|
recordOutcome(journal.add({
|
|
@@ -1363,10 +1391,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1363
1391
|
fixture
|
|
1364
1392
|
}
|
|
1365
1393
|
}));
|
|
1366
|
-
writeErrorResponse(res, 422, openRouter ? serializeOpenRouterError(422, "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.") :
|
|
1394
|
+
writeErrorResponse(res, 422, openRouter ? serializeOpenRouterError(422, "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.") : renderError("openai", "validation", {
|
|
1367
1395
|
message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
|
|
1368
1396
|
type: "invalid_request_error"
|
|
1369
|
-
}
|
|
1397
|
+
}).body);
|
|
1370
1398
|
return;
|
|
1371
1399
|
}
|
|
1372
1400
|
if (appliedPlan && isTextResponse(response) && (appliedPlan.stop === "refusal" || appliedPlan.stop === "content_filter" || appliedPlan.reasoning !== void 0)) {
|
|
@@ -1381,38 +1409,25 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1381
1409
|
fixture
|
|
1382
1410
|
}
|
|
1383
1411
|
});
|
|
1384
|
-
|
|
1385
|
-
if (body.stream !== true)
|
|
1386
|
-
|
|
1387
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1388
|
-
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1389
|
-
} else {
|
|
1412
|
+
rejectGeneratedFiles(response, wire);
|
|
1413
|
+
if (body.stream !== true) await commitNonStream(entry, shapeORCompletion(appliedPlan.stop === "refusal" ? buildOpenAIRefusalCompletion(appliedPlan.refusal ?? "", responseModel, overrides, body.messages) : appliedPlan.stop === "content_filter" ? buildOpenAIContentFilterCompletion(responseModel, overrides, body.messages) : buildOpenAIReasoningCompletion(appliedPlan.reasoning ?? "", responseModel, openRouter, overrides, body.messages), overrides), misbehaviorPayloadLength(appliedPlan, appliedReasoning));
|
|
1414
|
+
else {
|
|
1390
1415
|
const chunks = appliedPlan.stop === "refusal" ? buildOpenAIRefusalChunks(appliedPlan.refusal ?? "", responseModel, chunkSize, overrides) : appliedPlan.stop === "content_filter" ? buildOpenAIContentFilterChunks(responseModel, overrides) : buildOpenAIReasoningChunks(appliedPlan.reasoning ?? "", responseModel, chunkSize, openRouter, overrides);
|
|
1391
1416
|
const usageChunk = emitStreamingUsage ? buildUsageChunk(chunks[0].id, overrides?.model ?? responseModel, chunks[0].created, resolveStreamingUsageTokens(overrides, ""), overrides?.systemFingerprint) : void 0;
|
|
1392
1417
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
streamingProfile: fixture.streamingProfile,
|
|
1397
|
-
signal: interruption?.signal,
|
|
1398
|
-
onChunkSent: interruption?.tick,
|
|
1418
|
+
recordOutcome(entry);
|
|
1419
|
+
await deliverStream(res, entry, fixture, sseCodec({
|
|
1420
|
+
done: true,
|
|
1399
1421
|
usageChunk,
|
|
1400
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1401
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1402
1422
|
openRouterProcessing
|
|
1403
|
-
}))
|
|
1404
|
-
if (!res.writableEnded) res.destroy();
|
|
1405
|
-
entry.response.interrupted = true;
|
|
1406
|
-
entry.response.interruptReason = interruption?.reason();
|
|
1407
|
-
}
|
|
1408
|
-
interruption?.cleanup();
|
|
1423
|
+
}), chunks, streamTiming(chunks.length));
|
|
1409
1424
|
}
|
|
1410
1425
|
return;
|
|
1411
1426
|
}
|
|
1412
1427
|
if (isCombinedFixtureResponse(response)) {
|
|
1413
1428
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1414
1429
|
const overrides = extractOverrides(response);
|
|
1415
|
-
const effectiveStrict =
|
|
1430
|
+
const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
1416
1431
|
const effReasoning = appliedPlan ? appliedReasoning : resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1417
1432
|
const streaming = body.stream === true;
|
|
1418
1433
|
const blockOutcome = response.blocks && response.blocks.length > 0 ? resolveFixtureBlockOutcome(response.blocks) : void 0;
|
|
@@ -1426,43 +1441,33 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1426
1441
|
fixture
|
|
1427
1442
|
}
|
|
1428
1443
|
});
|
|
1429
|
-
|
|
1444
|
+
rejectGeneratedFiles(response, wire);
|
|
1430
1445
|
const toolCalls = requireEmittedFunctionToolCalls(response, wire);
|
|
1446
|
+
const completionText = blockOutcome ? blockOutcome.content + blockOutcome.toolCalls.map((tc) => tc.name + tc.arguments).join("") : (response.content ?? "") + toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1431
1447
|
if (!streaming) {
|
|
1432
1448
|
const completion = buildContentWithToolCallsCompletion(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ?? toolCalls, responseModel, effReasoning, blockOutcome ? {
|
|
1433
1449
|
...overrides,
|
|
1434
1450
|
finishReason: overrides?.finishReason ?? (blockOutcome.hasToolCalls ? "tool_calls" : "stop")
|
|
1435
1451
|
} : overrides, body.messages);
|
|
1436
|
-
|
|
1437
|
-
|
|
1452
|
+
const contentLength = (effReasoning?.length ?? 0) + completionText.length;
|
|
1453
|
+
await commitNonStream(journalEntry, shapeORCompletion(completion, overrides), contentLength);
|
|
1438
1454
|
} else {
|
|
1439
1455
|
const chunks = buildContentWithToolCallsChunks(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ?? toolCalls, responseModel, chunkSize, effReasoning, overrides, blockOutcome?.ordered);
|
|
1440
|
-
const completionText = blockOutcome ? blockOutcome.content + blockOutcome.toolCalls.map((tc) => tc.name + tc.arguments).join("") : (response.content ?? "") + toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1441
1456
|
const usageChunk = emitStreamingUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
|
|
1442
1457
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
streamingProfile: fixture.streamingProfile,
|
|
1447
|
-
signal: interruption?.signal,
|
|
1448
|
-
onChunkSent: interruption?.tick,
|
|
1458
|
+
recordOutcome(journalEntry);
|
|
1459
|
+
await deliverStream(res, journalEntry, fixture, sseCodec({
|
|
1460
|
+
done: true,
|
|
1449
1461
|
usageChunk,
|
|
1450
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1451
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1452
1462
|
openRouterProcessing
|
|
1453
|
-
}))
|
|
1454
|
-
if (!res.writableEnded) res.destroy();
|
|
1455
|
-
journalEntry.response.interrupted = true;
|
|
1456
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1457
|
-
}
|
|
1458
|
-
interruption?.cleanup();
|
|
1463
|
+
}), chunks, streamTiming(chunks.length));
|
|
1459
1464
|
}
|
|
1460
1465
|
return;
|
|
1461
1466
|
}
|
|
1462
1467
|
if (isTextResponse(response)) {
|
|
1463
1468
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1464
1469
|
const overrides = extractOverrides(response);
|
|
1465
|
-
const effectiveStrict =
|
|
1470
|
+
const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
1466
1471
|
const effReasoning = appliedPlan ? appliedReasoning : resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1467
1472
|
const journalEntry = journal.add({
|
|
1468
1473
|
method: req.method ?? "POST",
|
|
@@ -1474,38 +1479,28 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1474
1479
|
fixture
|
|
1475
1480
|
}
|
|
1476
1481
|
});
|
|
1477
|
-
|
|
1482
|
+
rejectGeneratedFiles(response, wire);
|
|
1478
1483
|
if (body.stream !== true) {
|
|
1479
1484
|
const completion = buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
|
|
1480
|
-
|
|
1481
|
-
|
|
1485
|
+
const contentLength = (effReasoning?.length ?? 0) + response.content.length;
|
|
1486
|
+
await commitNonStream(journalEntry, shapeORCompletion(completion, overrides), contentLength);
|
|
1482
1487
|
} else {
|
|
1483
1488
|
const chunks = buildTextChunks(response.content, responseModel, chunkSize, effReasoning, overrides);
|
|
1484
1489
|
const usageChunk = emitStreamingUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, response.content), overrides?.systemFingerprint) : void 0;
|
|
1485
1490
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
streamingProfile: fixture.streamingProfile,
|
|
1490
|
-
signal: interruption?.signal,
|
|
1491
|
-
onChunkSent: interruption?.tick,
|
|
1491
|
+
recordOutcome(journalEntry);
|
|
1492
|
+
await deliverStream(res, journalEntry, fixture, sseCodec({
|
|
1493
|
+
done: true,
|
|
1492
1494
|
usageChunk,
|
|
1493
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1494
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1495
1495
|
openRouterProcessing
|
|
1496
|
-
}))
|
|
1497
|
-
if (!res.writableEnded) res.destroy();
|
|
1498
|
-
journalEntry.response.interrupted = true;
|
|
1499
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1500
|
-
}
|
|
1501
|
-
interruption?.cleanup();
|
|
1496
|
+
}), chunks, streamTiming(chunks.length));
|
|
1502
1497
|
}
|
|
1503
1498
|
return;
|
|
1504
1499
|
}
|
|
1505
1500
|
if (isToolCallResponse(response)) {
|
|
1506
1501
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1507
1502
|
const overrides = extractOverrides(response);
|
|
1508
|
-
const effectiveStrict =
|
|
1503
|
+
const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
1509
1504
|
const effReasoning = appliedPlan ? appliedReasoning : resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1510
1505
|
const journalEntry = journal.add({
|
|
1511
1506
|
method: req.method ?? "POST",
|
|
@@ -1517,33 +1512,24 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1517
1512
|
fixture
|
|
1518
1513
|
}
|
|
1519
1514
|
});
|
|
1520
|
-
|
|
1515
|
+
rejectGeneratedFiles(response, wire);
|
|
1521
1516
|
const toolCalls = requireServedFunctionToolCalls(response, wire);
|
|
1522
1517
|
if (body.stream !== true) {
|
|
1518
|
+
const toolCallText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1523
1519
|
const completion = buildToolCallCompletion(toolCalls, responseModel, effReasoning, overrides, body.messages);
|
|
1524
|
-
|
|
1525
|
-
|
|
1520
|
+
const contentLength = (effReasoning?.length ?? 0) + toolCallText.length;
|
|
1521
|
+
await commitNonStream(journalEntry, shapeORCompletion(completion, overrides), contentLength);
|
|
1526
1522
|
} else {
|
|
1527
1523
|
const chunks = buildToolCallChunks(toolCalls, responseModel, chunkSize, effReasoning, overrides);
|
|
1528
1524
|
const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1529
1525
|
const usageChunk = emitStreamingUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
|
|
1530
1526
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
streamingProfile: fixture.streamingProfile,
|
|
1535
|
-
signal: interruption?.signal,
|
|
1536
|
-
onChunkSent: interruption?.tick,
|
|
1527
|
+
recordOutcome(journalEntry);
|
|
1528
|
+
await deliverStream(res, journalEntry, fixture, sseCodec({
|
|
1529
|
+
done: true,
|
|
1537
1530
|
usageChunk,
|
|
1538
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1539
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1540
1531
|
openRouterProcessing
|
|
1541
|
-
}))
|
|
1542
|
-
if (!res.writableEnded) res.destroy();
|
|
1543
|
-
journalEntry.response.interrupted = true;
|
|
1544
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1545
|
-
}
|
|
1546
|
-
interruption?.cleanup();
|
|
1532
|
+
}), chunks, streamTiming(chunks.length));
|
|
1547
1533
|
}
|
|
1548
1534
|
return;
|
|
1549
1535
|
}
|
|
@@ -1557,10 +1543,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1557
1543
|
fixture
|
|
1558
1544
|
}
|
|
1559
1545
|
}));
|
|
1560
|
-
writeErrorResponse(res, 500, openRouter ? serializeOpenRouterError(500, "Fixture response did not match any known type") :
|
|
1546
|
+
writeErrorResponse(res, 500, openRouter ? serializeOpenRouterError(500, "Fixture response did not match any known type") : renderError("openai", "unknown-response", {
|
|
1561
1547
|
message: "Fixture response did not match any known type",
|
|
1562
1548
|
type: "server_error"
|
|
1563
|
-
}
|
|
1549
|
+
}).body);
|
|
1564
1550
|
}
|
|
1565
1551
|
async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
1566
1552
|
return createServerWithResolvedAuth(fixtures, options, resolveInboundAuth({
|
|
@@ -1629,6 +1615,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1629
1615
|
const port = options?.port ?? 0;
|
|
1630
1616
|
const registry = options?.metrics ? createMetricsRegistry() : void 0;
|
|
1631
1617
|
const serverOptions = options ?? {};
|
|
1618
|
+
parsePolicy(serverOptions.policy, (message) => new Logger("warn").warn(message));
|
|
1632
1619
|
const validResponsesTools = (mode) => mode === "legacy" || mode === "extended";
|
|
1633
1620
|
if (serverOptions.responsesTools !== void 0 && !validResponsesTools(serverOptions.responsesTools)) new Logger("warn").warn(`Ignoring responsesTools because it must be "legacy" or "extended", got ${JSON.stringify(serverOptions.responsesTools)}. Using "legacy".`);
|
|
1634
1621
|
fixtures = createServerFixtureList(callerFixtures, () => serverOptions.responsesTools, responsesToolsExtendedView);
|
|
@@ -1687,6 +1674,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1687
1674
|
get strict() {
|
|
1688
1675
|
return serverOptions.strict;
|
|
1689
1676
|
},
|
|
1677
|
+
get policy() {
|
|
1678
|
+
return serverOptions.policy;
|
|
1679
|
+
},
|
|
1690
1680
|
get strictToolArguments() {
|
|
1691
1681
|
return serverOptions.strictToolArguments;
|
|
1692
1682
|
},
|
|
@@ -1799,10 +1789,12 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1799
1789
|
return status >= 400 && status < 500 ? "invalid_request_error" : "server_error";
|
|
1800
1790
|
}
|
|
1801
1791
|
/**
|
|
1802
|
-
* Envelope for a fixture
|
|
1803
|
-
*
|
|
1804
|
-
*
|
|
1805
|
-
*
|
|
1792
|
+
* Envelope for a fixture error (see isFixtureToolCallError: invalid JSON
|
|
1793
|
+
* arguments on an object wire, a custom tool call on a non-Responses wire, a
|
|
1794
|
+
* malformed fixture tool call, generated `files` on a wire that cannot carry
|
|
1795
|
+
* them, or a malformed fixture `citations` or `files` value). It is the
|
|
1796
|
+
* provider envelope aimock emits for that route, with the aimock `code` in
|
|
1797
|
+
* it: Google's
|
|
1806
1798
|
* `google.rpc.ErrorInfo` detail on Gemini and Vertex AI, a string
|
|
1807
1799
|
* `error.code` on Gemini Interactions and Anthropic Messages, a `reason`
|
|
1808
1800
|
* member on Bedrock (a `code` member would replace `__type` as the AWS SDK's
|
|
@@ -1812,70 +1804,63 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1812
1804
|
* `toolCallEnvelope` instead of using this one.
|
|
1813
1805
|
*/
|
|
1814
1806
|
function invalidToolArgumentsEnvelope(pathname, message, code) {
|
|
1815
|
-
if (pathname === OLLAMA_CHAT_PATH) return
|
|
1816
|
-
|
|
1807
|
+
if (pathname === OLLAMA_CHAT_PATH) return renderError("ollama", "tool-argument", {
|
|
1808
|
+
message,
|
|
1817
1809
|
code
|
|
1818
|
-
});
|
|
1819
|
-
if (pathname === GEMINI_INTERACTIONS_PATH) return
|
|
1810
|
+
}).body;
|
|
1811
|
+
if (pathname === GEMINI_INTERACTIONS_PATH) return renderError("interactions", "tool-argument", {
|
|
1812
|
+
message,
|
|
1813
|
+
code
|
|
1814
|
+
}).body;
|
|
1815
|
+
if (GEMINI_PATH_RE.test(pathname) || VERTEX_AI_RE.test(pathname)) return renderError("gemini", "tool-argument", {
|
|
1816
|
+
message,
|
|
1820
1817
|
code,
|
|
1821
|
-
|
|
1822
|
-
}
|
|
1823
|
-
if (
|
|
1824
|
-
code: 500,
|
|
1818
|
+
googleDetails: googleFixtureToolCallErrorDetails(code)
|
|
1819
|
+
}).body;
|
|
1820
|
+
if (pathname === MESSAGES_PATH) return renderError("messages", "tool-argument", {
|
|
1825
1821
|
message,
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
if (pathname === MESSAGES_PATH) return JSON.stringify({
|
|
1830
|
-
type: "error",
|
|
1831
|
-
error: {
|
|
1832
|
-
type: "api_error",
|
|
1833
|
-
code,
|
|
1834
|
-
message
|
|
1835
|
-
}
|
|
1836
|
-
});
|
|
1837
|
-
if (BEDROCK_INVOKE_RE.test(pathname) || BEDROCK_STREAM_RE.test(pathname) || BEDROCK_CONVERSE_RE.test(pathname) || BEDROCK_CONVERSE_STREAM_RE.test(pathname)) return JSON.stringify({
|
|
1838
|
-
__type: "InternalServerException",
|
|
1822
|
+
code
|
|
1823
|
+
}).body;
|
|
1824
|
+
if (BEDROCK_INVOKE_RE.test(pathname) || BEDROCK_STREAM_RE.test(pathname) || BEDROCK_CONVERSE_RE.test(pathname) || BEDROCK_CONVERSE_STREAM_RE.test(pathname)) return renderError("bedrock", "tool-argument", {
|
|
1839
1825
|
message,
|
|
1840
|
-
|
|
1841
|
-
});
|
|
1842
|
-
return
|
|
1826
|
+
code
|
|
1827
|
+
}).body;
|
|
1828
|
+
return renderError("openai", "tool-argument", {
|
|
1843
1829
|
message,
|
|
1844
|
-
type: "server_error",
|
|
1845
1830
|
code
|
|
1846
|
-
}
|
|
1831
|
+
}).body;
|
|
1847
1832
|
}
|
|
1848
1833
|
function invalidMisbehaviorHeaderEnvelope(pathname, detail) {
|
|
1849
1834
|
const code = "aimock_misbehavior_invalid";
|
|
1850
1835
|
const message = `${code}: ${detail}`;
|
|
1851
|
-
if (pathname === OLLAMA_CHAT_PATH || pathname === OLLAMA_GENERATE_PATH) return
|
|
1852
|
-
if (pathname === COHERE_CHAT_PATH) return JSON.stringify({ message });
|
|
1853
|
-
if (pathname === GEMINI_INTERACTIONS_PATH) return JSON.stringify({ error: {
|
|
1854
|
-
code,
|
|
1855
|
-
message
|
|
1856
|
-
} });
|
|
1857
|
-
if (GEMINI_PATH_RE.test(pathname) || VERTEX_AI_RE.test(pathname)) return JSON.stringify({ error: {
|
|
1858
|
-
code: 400,
|
|
1836
|
+
if (pathname === OLLAMA_CHAT_PATH || pathname === OLLAMA_GENERATE_PATH) return renderError("ollama", "invalid-misbehavior-header", {
|
|
1859
1837
|
message,
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
if (pathname ===
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
});
|
|
1870
|
-
if (
|
|
1871
|
-
__type: "ValidationException",
|
|
1872
|
-
message
|
|
1873
|
-
});
|
|
1874
|
-
return JSON.stringify({ error: {
|
|
1838
|
+
code
|
|
1839
|
+
}).body;
|
|
1840
|
+
if (pathname === COHERE_CHAT_PATH) return renderError("cohere", "invalid-misbehavior-header", {
|
|
1841
|
+
message,
|
|
1842
|
+
code
|
|
1843
|
+
}).body;
|
|
1844
|
+
if (pathname === GEMINI_INTERACTIONS_PATH) return renderError("interactions", "invalid-misbehavior-header", {
|
|
1845
|
+
message,
|
|
1846
|
+
code
|
|
1847
|
+
}).body;
|
|
1848
|
+
if (GEMINI_PATH_RE.test(pathname) || VERTEX_AI_RE.test(pathname)) return renderError("gemini", "invalid-misbehavior-header", {
|
|
1875
1849
|
message,
|
|
1876
|
-
type: "invalid_request_error",
|
|
1877
1850
|
code
|
|
1878
|
-
}
|
|
1851
|
+
}).body;
|
|
1852
|
+
if (pathname === MESSAGES_PATH) return renderError("messages", "invalid-misbehavior-header", {
|
|
1853
|
+
message,
|
|
1854
|
+
code
|
|
1855
|
+
}).body;
|
|
1856
|
+
if (BEDROCK_INVOKE_RE.test(pathname) || BEDROCK_STREAM_RE.test(pathname) || BEDROCK_CONVERSE_RE.test(pathname) || BEDROCK_CONVERSE_STREAM_RE.test(pathname)) return renderError("bedrock", "invalid-misbehavior-header", {
|
|
1857
|
+
message,
|
|
1858
|
+
code
|
|
1859
|
+
}).body;
|
|
1860
|
+
return renderError("openai", "invalid-misbehavior-header", {
|
|
1861
|
+
message,
|
|
1862
|
+
code
|
|
1863
|
+
}).body;
|
|
1879
1864
|
}
|
|
1880
1865
|
/**
|
|
1881
1866
|
* The newest journal entry this request already produced, or null.
|
|
@@ -1939,7 +1924,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1939
1924
|
const existing = lastJournalEntryFor(req);
|
|
1940
1925
|
const bodyLimitDisconnect = clientFault && (req.destroyed || res.destroyed);
|
|
1941
1926
|
if ((clientAbort || bodyLimitDisconnect) && !res.headersSent) {
|
|
1942
|
-
const interruptReason = bodyLimitDisconnect ? "request body exceeded size limit" :
|
|
1927
|
+
const interruptReason = bodyLimitDisconnect ? "request body exceeded size limit" : CLIENT_ABORTED;
|
|
1943
1928
|
if (existing) {
|
|
1944
1929
|
if (!existing.response.interrupted) {
|
|
1945
1930
|
existing.response.status = 0;
|
|
@@ -1990,12 +1975,12 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1990
1975
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
1991
1976
|
}
|
|
1992
1977
|
setCorsHeaders(res);
|
|
1993
|
-
writeErrorResponse(res, status, invalidToolArguments ? (opts?.toolCallEnvelope ?? ((m, c) => invalidToolArgumentsEnvelope(pathname, m, c)))(msg, fixtureToolCallErrorCode(err)) : opts?.envelope ? opts.envelope(msg, status) :
|
|
1978
|
+
writeErrorResponse(res, status, invalidToolArguments ? (opts?.toolCallEnvelope ?? ((m, c) => invalidToolArgumentsEnvelope(pathname, m, c)))(msg, fixtureToolCallErrorCode(err)) : opts?.envelope ? opts.envelope(msg, status) : renderError("openai", "route-failure", {
|
|
1994
1979
|
message: msg,
|
|
1995
1980
|
type: errorTypeForStatus(status)
|
|
1996
|
-
}
|
|
1981
|
+
}).body);
|
|
1997
1982
|
} else {
|
|
1998
|
-
const midStreamReason = clientAbort ?
|
|
1983
|
+
const midStreamReason = clientAbort ? CLIENT_ABORTED : "handler crashed mid-stream";
|
|
1999
1984
|
if (existing) {
|
|
2000
1985
|
if (res.writableEnded) {
|
|
2001
1986
|
if (existing.response.error === void 0) existing.response.error = msg;
|
|
@@ -2110,10 +2095,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2110
2095
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
2111
2096
|
}
|
|
2112
2097
|
setCorsHeaders(res);
|
|
2113
|
-
writeErrorResponse(res, 400,
|
|
2098
|
+
writeErrorResponse(res, 400, renderError("openai", "route-failure", {
|
|
2114
2099
|
message: "Invalid request target",
|
|
2115
2100
|
type: "invalid_request_error"
|
|
2116
|
-
}
|
|
2101
|
+
}).body);
|
|
2117
2102
|
return;
|
|
2118
2103
|
}
|
|
2119
2104
|
pathname = parsedUrl.pathname;
|
|
@@ -2126,7 +2111,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2126
2111
|
}
|
|
2127
2112
|
if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname === METRICS_PATH))) {
|
|
2128
2113
|
if (!validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
2129
|
-
writeApiKeyHttpRejection(res, setCorsHeaders);
|
|
2114
|
+
writeApiKeyHttpRejection(res, setCorsHeaders, pathname, resolvePolicy(defaults, req.headers).authErrors);
|
|
2130
2115
|
return;
|
|
2131
2116
|
}
|
|
2132
2117
|
markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
@@ -2193,18 +2178,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2193
2178
|
}
|
|
2194
2179
|
if (pathname === OLLAMA_TAGS_PATH && req.method === "GET") {
|
|
2195
2180
|
setCorsHeaders(res);
|
|
2196
|
-
const
|
|
2197
|
-
for (const f of fixtures) if (f.match.model && typeof f.match.model === "string") modelIds.add(f.match.model);
|
|
2198
|
-
const models = (modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS).map((name) => ({
|
|
2199
|
-
name,
|
|
2200
|
-
model: name,
|
|
2201
|
-
modified_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2202
|
-
size: 0,
|
|
2203
|
-
digest: "",
|
|
2204
|
-
details: {}
|
|
2205
|
-
}));
|
|
2181
|
+
const body = renderOllamaTags(collect(fixtures, "all").ids, () => (/* @__PURE__ */ new Date()).toISOString());
|
|
2206
2182
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2207
|
-
res.end(JSON.stringify(
|
|
2183
|
+
res.end(JSON.stringify(body));
|
|
2208
2184
|
return;
|
|
2209
2185
|
}
|
|
2210
2186
|
const openRouterVideoContentMatch = pathname.match(OPENROUTER_VIDEO_CONTENT_RE);
|
|
@@ -2325,21 +2301,70 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2325
2301
|
res.end(registry.serialize());
|
|
2326
2302
|
return;
|
|
2327
2303
|
}
|
|
2328
|
-
|
|
2304
|
+
const modelDetailMatch = pathname.match(MODELS_DETAIL_RE);
|
|
2305
|
+
const geminiModelDetailMatch = pathname.match(GEMINI_MODEL_DETAIL_RE);
|
|
2306
|
+
if (req.method === "GET" && (pathname === MODELS_PATH || pathname === GEMINI_MODELS_PATH || modelDetailMatch || geminiModelDetailMatch)) {
|
|
2329
2307
|
setCorsHeaders(res);
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2308
|
+
const google = pathname === GEMINI_MODELS_PATH || geminiModelDetailMatch !== null;
|
|
2309
|
+
const policy = resolvePolicy(defaults, req.headers);
|
|
2310
|
+
const anthropic = !google && (modelDetailMatch !== null || policy.modelCatalog === "native") && isAnthropicModelRequest(req.headers);
|
|
2311
|
+
const missingHeader = anthropic && policy.requiredHeaders === "enforce" ? missingRequiredHeader("messages", req.headers) : void 0;
|
|
2312
|
+
if (missingHeader) {
|
|
2313
|
+
writeErrorResponse(res, 400, renderError("messages", "thinking", {
|
|
2314
|
+
message: `${missingHeader}: header is required`,
|
|
2315
|
+
requestId
|
|
2316
|
+
}).body);
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
const ids = coreModelIds(collect(fixtures, "all").ids);
|
|
2320
|
+
const visible = google ? idsFor(ids, "google") : anthropic ? idsFor(ids, "anthropic") : ids;
|
|
2321
|
+
const catalogError = (status, message) => {
|
|
2322
|
+
const rendered = google ? renderError("gemini", "fixture", {
|
|
2323
|
+
bodyCode: status,
|
|
2324
|
+
status: status === 404 ? "NOT_FOUND" : "INVALID_ARGUMENT",
|
|
2325
|
+
message
|
|
2326
|
+
}) : anthropic ? renderError("messages", "thinking", {
|
|
2327
|
+
type: status === 404 ? "not_found_error" : "invalid_request_error",
|
|
2328
|
+
message
|
|
2329
|
+
}) : renderError("openai", "validation", { message });
|
|
2330
|
+
res.writeHead(status, {
|
|
2331
|
+
"Content-Type": "application/json",
|
|
2332
|
+
...rendered.headers
|
|
2333
|
+
});
|
|
2334
|
+
res.end(rendered.body);
|
|
2335
|
+
};
|
|
2336
|
+
let body;
|
|
2337
|
+
const detail = modelDetailMatch ?? geminiModelDetailMatch;
|
|
2338
|
+
if (detail) {
|
|
2339
|
+
let id;
|
|
2340
|
+
try {
|
|
2341
|
+
id = decodeURIComponent(detail[1]);
|
|
2342
|
+
} catch {
|
|
2343
|
+
catalogError(404, "Model not found");
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
if (!visible.includes(id)) {
|
|
2347
|
+
catalogError(404, `Model not found: ${id}`);
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
body = google ? renderGeminiModel(id) : anthropic ? renderAnthropicModel(id) : renderOpenAIModel(id);
|
|
2351
|
+
} else if (google) {
|
|
2352
|
+
const result = geminiPage(visible, parsedUrl.searchParams);
|
|
2353
|
+
if (result.error !== void 0) {
|
|
2354
|
+
catalogError(400, result.error);
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
body = renderGeminiModels(result.page);
|
|
2358
|
+
} else if (anthropic) {
|
|
2359
|
+
const result = anthropicPage(visible, parsedUrl.searchParams);
|
|
2360
|
+
if (result.error !== void 0) {
|
|
2361
|
+
catalogError(400, result.error);
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
body = renderAnthropicModels(result.page);
|
|
2365
|
+
} else body = renderOpenAIModels(ids);
|
|
2338
2366
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2339
|
-
res.end(JSON.stringify(
|
|
2340
|
-
object: "list",
|
|
2341
|
-
data
|
|
2342
|
-
}));
|
|
2367
|
+
res.end(JSON.stringify(body));
|
|
2343
2368
|
return;
|
|
2344
2369
|
}
|
|
2345
2370
|
const batchesCancelMatch = pathname.match(BATCHES_CANCEL_RE);
|
|
@@ -2597,10 +2622,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2597
2622
|
if (limitParam) {
|
|
2598
2623
|
const limit = Number(limitParam);
|
|
2599
2624
|
if (!/^\+?\d+$/.test(limitParam.trim()) || !Number.isSafeInteger(limit) || limit <= 0) {
|
|
2600
|
-
writeErrorResponse(res, 400,
|
|
2625
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
2601
2626
|
message: `Invalid limit parameter: "${limitParam}"`,
|
|
2602
2627
|
type: "invalid_request_error"
|
|
2603
|
-
}
|
|
2628
|
+
}).body);
|
|
2604
2629
|
return;
|
|
2605
2630
|
}
|
|
2606
2631
|
opts = { limit };
|
|
@@ -2625,7 +2650,27 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2625
2650
|
ensureRawFixturePositions();
|
|
2626
2651
|
await handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2627
2652
|
} catch (err) {
|
|
2628
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2653
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2654
|
+
}
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2657
|
+
const geminiCountMatch = pathname.match(GEMINI_COUNT_RE);
|
|
2658
|
+
if (req.method === "POST" && (pathname === MESSAGES_COUNT_PATH || geminiCountMatch)) {
|
|
2659
|
+
const google = geminiCountMatch !== null;
|
|
2660
|
+
try {
|
|
2661
|
+
handleCountTokens(req, res, await readBody(req), google, geminiCountMatch?.[1] ?? "", journal, defaults, setCorsHeaders);
|
|
2662
|
+
} catch (err) {
|
|
2663
|
+
routeError(req, res, err, pathname, {
|
|
2664
|
+
service: google ? "gemini" : "anthropic",
|
|
2665
|
+
envelope: (message, status) => google ? renderError("gemini", "fixture", {
|
|
2666
|
+
message,
|
|
2667
|
+
bodyCode: status,
|
|
2668
|
+
status: status === 400 ? "INVALID_ARGUMENT" : "INTERNAL"
|
|
2669
|
+
}).body : renderError("messages", "thinking", {
|
|
2670
|
+
message,
|
|
2671
|
+
type: status === 400 ? "invalid_request_error" : "api_error"
|
|
2672
|
+
}).body
|
|
2673
|
+
});
|
|
2629
2674
|
}
|
|
2630
2675
|
return;
|
|
2631
2676
|
}
|
|
@@ -2635,7 +2680,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2635
2680
|
ensureRawFixturePositions();
|
|
2636
2681
|
await handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2637
2682
|
} catch (err) {
|
|
2638
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2683
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2639
2684
|
}
|
|
2640
2685
|
return;
|
|
2641
2686
|
}
|
|
@@ -2645,7 +2690,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2645
2690
|
ensureRawFixturePositions();
|
|
2646
2691
|
await handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2647
2692
|
} catch (err) {
|
|
2648
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2693
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2649
2694
|
}
|
|
2650
2695
|
return;
|
|
2651
2696
|
}
|
|
@@ -2811,7 +2856,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2811
2856
|
ensureRawFixturePositions();
|
|
2812
2857
|
await handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2813
2858
|
} catch (err) {
|
|
2814
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2859
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2815
2860
|
}
|
|
2816
2861
|
return;
|
|
2817
2862
|
}
|
|
@@ -2836,7 +2881,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2836
2881
|
ensureRawFixturePositions();
|
|
2837
2882
|
await handleGemini(req, res, raw, geminiModel, streaming, fixtures, journal, defaults, setCorsHeaders);
|
|
2838
2883
|
} catch (err) {
|
|
2839
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2884
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2840
2885
|
}
|
|
2841
2886
|
return;
|
|
2842
2887
|
}
|
|
@@ -2849,7 +2894,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2849
2894
|
ensureRawFixturePositions();
|
|
2850
2895
|
await handleGemini(req, res, raw, vertexModel, streaming, fixtures, journal, defaults, setCorsHeaders, "vertexai");
|
|
2851
2896
|
} catch (err) {
|
|
2852
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2897
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2853
2898
|
}
|
|
2854
2899
|
return;
|
|
2855
2900
|
}
|
|
@@ -2965,10 +3010,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2965
3010
|
try {
|
|
2966
3011
|
voiceId = decodeURIComponent(elevenLabsVoiceMatch[1]);
|
|
2967
3012
|
} catch {
|
|
2968
|
-
writeErrorResponse(res, 400,
|
|
3013
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
2969
3014
|
message: `Invalid voice id '${elevenLabsVoiceMatch[1]}': malformed percent-encoding`,
|
|
2970
3015
|
type: "invalid_request_error"
|
|
2971
|
-
}
|
|
3016
|
+
}).body);
|
|
2972
3017
|
journal.add({
|
|
2973
3018
|
method: req.method ?? "GET",
|
|
2974
3019
|
path: req.url ?? pathname,
|
|
@@ -3118,15 +3163,15 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
3118
3163
|
await handleCompletions(req, res, fixtures, journal, defaults, () => lastJournalEntryFor(req), azureDeploymentId, completionsProvider, isOpenRouter, ensureRawFixturePositions, originalPathname.startsWith("/api/v3/"));
|
|
3119
3164
|
} catch (err) {
|
|
3120
3165
|
routeError(req, res, err, pathname, {
|
|
3121
|
-
envelope: (m, status) => isOpenRouter ? serializeOpenRouterError(status, m) :
|
|
3166
|
+
envelope: (m, status) => isOpenRouter ? serializeOpenRouterError(status, m) : renderError("openai", "route-failure", {
|
|
3122
3167
|
message: m,
|
|
3123
3168
|
type: errorTypeForStatus(status)
|
|
3124
|
-
}
|
|
3169
|
+
}).body,
|
|
3125
3170
|
...isOpenRouter ? { toolCallEnvelope: (m, code) => serializeOpenRouterError(500, m, { reason: code }) } : {},
|
|
3126
|
-
streamEvent: (m, status) => `data: ${isOpenRouter ? serializeOpenRouterError(status, m) :
|
|
3171
|
+
streamEvent: (m, status) => `data: ${isOpenRouter ? serializeOpenRouterError(status, m) : renderError("openai", "route-failure", {
|
|
3127
3172
|
message: m,
|
|
3128
3173
|
type: errorTypeForStatus(status)
|
|
3129
|
-
}
|
|
3174
|
+
}).body}\n\n`
|
|
3130
3175
|
});
|
|
3131
3176
|
}
|
|
3132
3177
|
}
|