@copilotkit/aimock 1.45.1 → 1.46.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 +13 -0
- package/README.md +7 -1
- 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/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 +61 -80
- 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 +58 -77
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +65 -97
- 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 +62 -94
- 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 +49 -74
- 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 +47 -72
- 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 +37 -42
- 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 +37 -42
- 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 +50 -35
- 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 +49 -34
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +57 -67
- 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 +56 -67
- 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 +11 -68
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +12 -67
- 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.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- 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-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 +111 -96
- 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 +109 -95
- 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/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/ollama.cjs +128 -68
- 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 +124 -64
- 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/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/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/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/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 +45 -58
- 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 +44 -57
- 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 +4 -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 +3 -0
- 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 +222 -146
- 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 +223 -147
- 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 +7 -0
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +7 -1
- 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 +12 -10
- 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 +11 -9
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +62 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +62 -3
- 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-gemini-live.cjs +11 -3
- 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 +10 -2
- 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 +28 -54
- 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 +25 -51
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +8 -6
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +7 -5
- package/dist/ws-responses.js.map +1 -1
- package/package.json +4 -1
- package/skills/write-fixtures/SKILL.md +25 -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, isResponseFactory, isServerOwnedFixtures, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, markMintedRequestId, prepareOpenAIChatMisbehavior, readBody, readBodyBufferBounded, 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 { writeErrorResponse, writeSSEStream } from "./sse-writer.js";
|
|
9
|
+
import { calculateTotalDelay, delay, writeErrorResponse, writeSSEStream } 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";
|
|
@@ -12,10 +15,15 @@ import { proxyAndRecord } from "./recorder.js";
|
|
|
12
15
|
import { Logger } from "./logger.js";
|
|
13
16
|
import { Journal } from "./journal.js";
|
|
14
17
|
import { createInterruptionSignal } from "./interruption.js";
|
|
18
|
+
import { collect, idsFor } from "./response/catalog/collect.js";
|
|
19
|
+
import { anthropicPage } from "./response/catalog/anthropic-page.js";
|
|
20
|
+
import { geminiPage } from "./response/catalog/gemini-page.js";
|
|
21
|
+
import { coreModelIds, renderAnthropicModel, renderAnthropicModels, renderGeminiModel, renderGeminiModels, renderOllamaTags, renderOpenAIModel, renderOpenAIModels } from "./response/catalog/render.js";
|
|
15
22
|
import { buildOpenRouterCandidates, handleOpenRouterCredits, handleOpenRouterKey, handleOpenRouterModels, isOpenRouterPath, resolveOpenRouterShaping, serializeOpenRouterError, shapeOpenRouterChunks, shapeOpenRouterCompletion } from "./openrouter-chat.js";
|
|
16
23
|
import { handleResponses } from "./responses.js";
|
|
17
24
|
import { handleMessages } from "./messages.js";
|
|
18
25
|
import { handleGemini } from "./gemini.js";
|
|
26
|
+
import { handleCountTokens } from "./count-tokens.js";
|
|
19
27
|
import { handleGeminiEmbedContent } from "./gemini-embeddings.js";
|
|
20
28
|
import { handleBedrock, handleBedrockStream } from "./bedrock.js";
|
|
21
29
|
import { handleConverse, handleConverseStream } from "./bedrock-converse.js";
|
|
@@ -47,7 +55,7 @@ import { handleLiveSession } from "./ws-live.js";
|
|
|
47
55
|
import { handleWebSocketResponses } from "./ws-responses.js";
|
|
48
56
|
import { handleWebSocketRealtime } from "./ws-realtime.js";
|
|
49
57
|
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";
|
|
58
|
+
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
59
|
import { CATALOG_ROUTES, buildOpenApiDocument } from "./openapi.js";
|
|
52
60
|
import { DESTROYED_STATUS_LABEL, createMetricsRegistry, normalizePathLabel } from "./metrics.js";
|
|
53
61
|
import { MCP_FAKES_DEFAULT_MOUNT, McpFakeStore, McpFakesAddError, blockIdOf } from "./mcp-fakes.js";
|
|
@@ -87,13 +95,6 @@ function normalizeCompatPath(pathname, logger) {
|
|
|
87
95
|
}
|
|
88
96
|
return pathname;
|
|
89
97
|
}
|
|
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
98
|
const CORS_HEADERS = {
|
|
98
99
|
"Access-Control-Allow-Origin": "*",
|
|
99
100
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
@@ -110,10 +111,10 @@ function handleOptions(res) {
|
|
|
110
111
|
}
|
|
111
112
|
function handleNotFound(res, message) {
|
|
112
113
|
setCorsHeaders(res);
|
|
113
|
-
writeErrorResponse(res, 404,
|
|
114
|
+
writeErrorResponse(res, 404, renderError("openai", "route-failure", {
|
|
114
115
|
message,
|
|
115
116
|
type: "not_found"
|
|
116
|
-
}
|
|
117
|
+
}).body);
|
|
117
118
|
}
|
|
118
119
|
/** The complete `GET /__aimock/fixtures` query-param vocabulary; anything else 400s. */
|
|
119
120
|
const FIXTURES_PARAMS = new Set(["include"]);
|
|
@@ -924,10 +925,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
924
925
|
fixture: null
|
|
925
926
|
}
|
|
926
927
|
});
|
|
927
|
-
writeErrorResponse(res, status, openRouter ? serializeOpenRouterError(status, `Request body read failed: ${msg}`) :
|
|
928
|
+
writeErrorResponse(res, status, openRouter ? serializeOpenRouterError(status, `Request body read failed: ${msg}`) : renderError("openai", "transport-failure", {
|
|
928
929
|
message: `Request body read failed: ${msg}`,
|
|
929
930
|
type: clientFault ? "invalid_request_error" : "server_error"
|
|
930
|
-
}
|
|
931
|
+
}).body);
|
|
931
932
|
return;
|
|
932
933
|
}
|
|
933
934
|
let body;
|
|
@@ -945,12 +946,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
945
946
|
fixture: null
|
|
946
947
|
}
|
|
947
948
|
});
|
|
948
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, `Malformed JSON: ${detail}`) :
|
|
949
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, `Malformed JSON: ${detail}`) : renderError("openai", "validation", {
|
|
949
950
|
message: `Malformed JSON: ${detail}`,
|
|
950
951
|
type: "invalid_request_error",
|
|
951
952
|
param: null,
|
|
952
|
-
code: "invalid_json"
|
|
953
|
-
|
|
953
|
+
code: "invalid_json",
|
|
954
|
+
includeParam: true
|
|
955
|
+
}).body);
|
|
954
956
|
return;
|
|
955
957
|
}
|
|
956
958
|
if (!isJsonObject(body)) {
|
|
@@ -964,10 +966,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
964
966
|
fixture: null
|
|
965
967
|
}
|
|
966
968
|
});
|
|
967
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Request body must be a JSON object") :
|
|
969
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Request body must be a JSON object") : renderError("openai", "validation", {
|
|
968
970
|
message: "Request body must be a JSON object",
|
|
969
971
|
type: "invalid_request_error"
|
|
970
|
-
}
|
|
972
|
+
}).body);
|
|
971
973
|
return;
|
|
972
974
|
}
|
|
973
975
|
if (modelFallback && !body.model) body.model = modelFallback;
|
|
@@ -982,12 +984,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
982
984
|
fixture: null
|
|
983
985
|
}
|
|
984
986
|
});
|
|
985
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Missing required parameter: 'messages'") :
|
|
987
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, "Missing required parameter: 'messages'") : renderError("openai", "validation", {
|
|
986
988
|
message: "Missing required parameter: 'messages'",
|
|
987
989
|
type: "invalid_request_error",
|
|
988
990
|
param: null,
|
|
989
|
-
code: null
|
|
990
|
-
|
|
991
|
+
code: null,
|
|
992
|
+
includeParam: true
|
|
993
|
+
}).body);
|
|
991
994
|
return;
|
|
992
995
|
}
|
|
993
996
|
const nullMessageIndex = body.messages.findIndex((message) => message === null);
|
|
@@ -1003,12 +1006,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1003
1006
|
fixture: null
|
|
1004
1007
|
}
|
|
1005
1008
|
});
|
|
1006
|
-
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, message) :
|
|
1009
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, message) : renderError("openai", "validation", {
|
|
1007
1010
|
message,
|
|
1008
1011
|
type: "invalid_request_error",
|
|
1009
1012
|
param: null,
|
|
1010
|
-
code: null
|
|
1011
|
-
|
|
1013
|
+
code: null,
|
|
1014
|
+
includeParam: true
|
|
1015
|
+
}).body);
|
|
1012
1016
|
return;
|
|
1013
1017
|
}
|
|
1014
1018
|
if (new URL(req.url ?? COMPLETIONS_PATH, "http://localhost").pathname === COMPLETIONS_PATH) {
|
|
@@ -1028,10 +1032,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1028
1032
|
fixture: null
|
|
1029
1033
|
}
|
|
1030
1034
|
});
|
|
1031
|
-
writeErrorResponse(res, 400,
|
|
1035
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
1032
1036
|
message: toolsError,
|
|
1033
1037
|
type: "invalid_request_error"
|
|
1034
|
-
}
|
|
1038
|
+
}).body);
|
|
1035
1039
|
return;
|
|
1036
1040
|
}
|
|
1037
1041
|
}
|
|
@@ -1131,8 +1135,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1131
1135
|
headers: flatHeaders,
|
|
1132
1136
|
body
|
|
1133
1137
|
};
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1138
|
+
const missDecisions = resolveRequestDecisions(defaults.strict, req.headers);
|
|
1139
|
+
const effectiveStrict = missDecisions.fixtureMiss;
|
|
1140
|
+
const missWouldProxy = wouldProxyMiss(!missDecisions.upstream, defaults.record, providerKey);
|
|
1136
1141
|
const noFixtureSource = missWouldProxy ? "proxy" : "internal";
|
|
1137
1142
|
const recordBypass = (reason) => {
|
|
1138
1143
|
const entry = getRequestEntry();
|
|
@@ -1179,12 +1184,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1179
1184
|
...strictOverrideField(defaults.strict, req.headers)
|
|
1180
1185
|
}
|
|
1181
1186
|
});
|
|
1182
|
-
writeErrorResponse(res, strictStatus, openRouter ? serializeOpenRouterError(strictStatus, strictMessage) :
|
|
1187
|
+
writeErrorResponse(res, strictStatus, openRouter ? serializeOpenRouterError(strictStatus, strictMessage) : renderError("openai", "strict-miss", {
|
|
1183
1188
|
message: strictMessage,
|
|
1184
1189
|
type: "invalid_request_error",
|
|
1185
1190
|
param: null,
|
|
1186
|
-
code: "no_fixture_match"
|
|
1187
|
-
|
|
1191
|
+
code: "no_fixture_match",
|
|
1192
|
+
includeParam: true
|
|
1193
|
+
}).body);
|
|
1188
1194
|
return;
|
|
1189
1195
|
}
|
|
1190
1196
|
if (defaults.record && providerKey) {
|
|
@@ -1254,12 +1260,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1254
1260
|
...strictOverrideField(defaults.strict, req.headers)
|
|
1255
1261
|
}
|
|
1256
1262
|
});
|
|
1257
|
-
writeErrorResponse(res, 404, openRouter ? serializeOpenRouterError(404, "No fixture matched") :
|
|
1263
|
+
writeErrorResponse(res, 404, openRouter ? serializeOpenRouterError(404, "No fixture matched") : renderError("openai", "fixture-miss", {
|
|
1258
1264
|
message: "No fixture matched",
|
|
1259
1265
|
type: "invalid_request_error",
|
|
1260
1266
|
param: null,
|
|
1261
|
-
code: "no_fixture_match"
|
|
1262
|
-
|
|
1267
|
+
code: "no_fixture_match",
|
|
1268
|
+
includeParam: true
|
|
1269
|
+
}).body);
|
|
1263
1270
|
return;
|
|
1264
1271
|
}
|
|
1265
1272
|
const resolvedResponse = preResolvedResponse ?? await resolveResponse(fixture, body);
|
|
@@ -1290,12 +1297,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1290
1297
|
testId
|
|
1291
1298
|
});
|
|
1292
1299
|
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}`) :
|
|
1300
|
+
writeErrorResponse(res, evaluation.status, openRouter ? serializeOpenRouterError(evaluation.status, `${evaluation.code}: ${evaluation.message}`) : renderError("openai", "misbehavior", {
|
|
1294
1301
|
message: evaluation.message,
|
|
1295
1302
|
type: "invalid_request_error",
|
|
1296
1303
|
param: null,
|
|
1297
|
-
code: evaluation.code
|
|
1298
|
-
|
|
1304
|
+
code: evaluation.code,
|
|
1305
|
+
includeParam: true
|
|
1306
|
+
}).body);
|
|
1299
1307
|
return;
|
|
1300
1308
|
}
|
|
1301
1309
|
const appliedPlan = evaluation.kind === "applied" ? prepareOpenAIChatMisbehavior(evaluation) : void 0;
|
|
@@ -1309,22 +1317,28 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1309
1317
|
testId
|
|
1310
1318
|
});
|
|
1311
1319
|
};
|
|
1312
|
-
const appliedReasoning = appliedPlan && appliedPlan.reasoning === void 0 && (isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response)) ? resolveReasoningForModel(response.reasoning, responseModel,
|
|
1320
|
+
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
1321
|
const appliedUsage = appliedPlan ? resolveOpenAIChatMisbehaviorUsage(appliedPlan, body, appliedPlan.reasoning === void 0 ? appliedReasoning !== void 0 : openRouter) : void 0;
|
|
1314
1322
|
const latency = fixture.latency ?? defaults.latency;
|
|
1315
1323
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
1316
1324
|
const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
|
|
1317
1325
|
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
1326
|
const resolveStreamingUsageTokens = (overrides, completionText) => appliedUsage ?? resolveUsage(overrides, streamingPromptText, completionText);
|
|
1319
|
-
const resolveRecordedTimings = (
|
|
1327
|
+
const resolveRecordedTimings = (chunkCount) => {
|
|
1320
1328
|
const timings = fixture.recordedTimings;
|
|
1321
1329
|
if (!appliedPlan?.duplicateId || !timings) return timings;
|
|
1322
1330
|
const gaps = timings.interChunkDelaysMs;
|
|
1323
1331
|
return {
|
|
1324
1332
|
...timings,
|
|
1325
|
-
interChunkDelaysMs: Array.from({ length: Math.max(gaps.length,
|
|
1333
|
+
interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, chunkCount - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
|
|
1326
1334
|
};
|
|
1327
1335
|
};
|
|
1336
|
+
const awaitNonStreamingTiming = (contentLength) => {
|
|
1337
|
+
if (fixture.nonStreamingTiming !== true) return Promise.resolve();
|
|
1338
|
+
if (!fixture.streamingProfile && !fixture.recordedTimings) return Promise.resolve();
|
|
1339
|
+
const chunkCount = Math.max(1, Math.ceil(contentLength / chunkSize));
|
|
1340
|
+
return delay(calculateTotalDelay(contentLength, chunkSize, fixture.streamingProfile, resolveRecordedTimings(chunkCount), fixture.replaySpeed ?? defaults.replaySpeed));
|
|
1341
|
+
};
|
|
1328
1342
|
const shapeORCompletion = (completion, overrides) => {
|
|
1329
1343
|
if (appliedUsage) completion.usage = appliedUsage;
|
|
1330
1344
|
if (!openRouter) return completion;
|
|
@@ -1363,10 +1377,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1363
1377
|
fixture
|
|
1364
1378
|
}
|
|
1365
1379
|
}));
|
|
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.") :
|
|
1380
|
+
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
1381
|
message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
|
|
1368
1382
|
type: "invalid_request_error"
|
|
1369
|
-
}
|
|
1383
|
+
}).body);
|
|
1370
1384
|
return;
|
|
1371
1385
|
}
|
|
1372
1386
|
if (appliedPlan && isTextResponse(response) && (appliedPlan.stop === "refusal" || appliedPlan.stop === "content_filter" || appliedPlan.reasoning !== void 0)) {
|
|
@@ -1384,6 +1398,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1384
1398
|
recordOutcome(entry);
|
|
1385
1399
|
if (body.stream !== true) {
|
|
1386
1400
|
const completion = 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);
|
|
1401
|
+
await awaitNonStreamingTiming((appliedPlan.stop === "refusal" ? appliedPlan.refusal ?? "" : appliedPlan.stop === "content_filter" ? "" : appliedPlan.reasoning ?? "").length);
|
|
1387
1402
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1388
1403
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1389
1404
|
} else {
|
|
@@ -1397,7 +1412,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1397
1412
|
signal: interruption?.signal,
|
|
1398
1413
|
onChunkSent: interruption?.tick,
|
|
1399
1414
|
usageChunk,
|
|
1400
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1415
|
+
recordedTimings: resolveRecordedTimings(chunks.length),
|
|
1401
1416
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1402
1417
|
openRouterProcessing
|
|
1403
1418
|
})) {
|
|
@@ -1412,7 +1427,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
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;
|
|
@@ -1428,7 +1443,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1428
1443
|
});
|
|
1429
1444
|
recordOutcome(journalEntry);
|
|
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) {
|
|
1448
|
+
await awaitNonStreamingTiming((effReasoning?.length ?? 0) + completionText.length);
|
|
1432
1449
|
const completion = buildContentWithToolCallsCompletion(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ?? toolCalls, responseModel, effReasoning, blockOutcome ? {
|
|
1433
1450
|
...overrides,
|
|
1434
1451
|
finishReason: overrides?.finishReason ?? (blockOutcome.hasToolCalls ? "tool_calls" : "stop")
|
|
@@ -1437,7 +1454,6 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1437
1454
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1438
1455
|
} else {
|
|
1439
1456
|
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
1457
|
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
1458
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1443
1459
|
const interruption = createInterruptionSignal(fixture);
|
|
@@ -1447,7 +1463,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1447
1463
|
signal: interruption?.signal,
|
|
1448
1464
|
onChunkSent: interruption?.tick,
|
|
1449
1465
|
usageChunk,
|
|
1450
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1466
|
+
recordedTimings: resolveRecordedTimings(chunks.length),
|
|
1451
1467
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1452
1468
|
openRouterProcessing
|
|
1453
1469
|
})) {
|
|
@@ -1462,7 +1478,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1462
1478
|
if (isTextResponse(response)) {
|
|
1463
1479
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1464
1480
|
const overrides = extractOverrides(response);
|
|
1465
|
-
const effectiveStrict =
|
|
1481
|
+
const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
1466
1482
|
const effReasoning = appliedPlan ? appliedReasoning : resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1467
1483
|
const journalEntry = journal.add({
|
|
1468
1484
|
method: req.method ?? "POST",
|
|
@@ -1476,6 +1492,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1476
1492
|
});
|
|
1477
1493
|
recordOutcome(journalEntry);
|
|
1478
1494
|
if (body.stream !== true) {
|
|
1495
|
+
await awaitNonStreamingTiming((effReasoning?.length ?? 0) + response.content.length);
|
|
1479
1496
|
const completion = buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
|
|
1480
1497
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1481
1498
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
@@ -1490,7 +1507,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1490
1507
|
signal: interruption?.signal,
|
|
1491
1508
|
onChunkSent: interruption?.tick,
|
|
1492
1509
|
usageChunk,
|
|
1493
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1510
|
+
recordedTimings: resolveRecordedTimings(chunks.length),
|
|
1494
1511
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1495
1512
|
openRouterProcessing
|
|
1496
1513
|
})) {
|
|
@@ -1505,7 +1522,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1505
1522
|
if (isToolCallResponse(response)) {
|
|
1506
1523
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1507
1524
|
const overrides = extractOverrides(response);
|
|
1508
|
-
const effectiveStrict =
|
|
1525
|
+
const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
1509
1526
|
const effReasoning = appliedPlan ? appliedReasoning : resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1510
1527
|
const journalEntry = journal.add({
|
|
1511
1528
|
method: req.method ?? "POST",
|
|
@@ -1520,6 +1537,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1520
1537
|
recordOutcome(journalEntry);
|
|
1521
1538
|
const toolCalls = requireServedFunctionToolCalls(response, wire);
|
|
1522
1539
|
if (body.stream !== true) {
|
|
1540
|
+
const toolCallText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1541
|
+
await awaitNonStreamingTiming((effReasoning?.length ?? 0) + toolCallText.length);
|
|
1523
1542
|
const completion = buildToolCallCompletion(toolCalls, responseModel, effReasoning, overrides, body.messages);
|
|
1524
1543
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1525
1544
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
@@ -1535,7 +1554,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1535
1554
|
signal: interruption?.signal,
|
|
1536
1555
|
onChunkSent: interruption?.tick,
|
|
1537
1556
|
usageChunk,
|
|
1538
|
-
recordedTimings: resolveRecordedTimings(chunks),
|
|
1557
|
+
recordedTimings: resolveRecordedTimings(chunks.length),
|
|
1539
1558
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1540
1559
|
openRouterProcessing
|
|
1541
1560
|
})) {
|
|
@@ -1557,10 +1576,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, getReque
|
|
|
1557
1576
|
fixture
|
|
1558
1577
|
}
|
|
1559
1578
|
}));
|
|
1560
|
-
writeErrorResponse(res, 500, openRouter ? serializeOpenRouterError(500, "Fixture response did not match any known type") :
|
|
1579
|
+
writeErrorResponse(res, 500, openRouter ? serializeOpenRouterError(500, "Fixture response did not match any known type") : renderError("openai", "unknown-response", {
|
|
1561
1580
|
message: "Fixture response did not match any known type",
|
|
1562
1581
|
type: "server_error"
|
|
1563
|
-
}
|
|
1582
|
+
}).body);
|
|
1564
1583
|
}
|
|
1565
1584
|
async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
1566
1585
|
return createServerWithResolvedAuth(fixtures, options, resolveInboundAuth({
|
|
@@ -1629,6 +1648,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1629
1648
|
const port = options?.port ?? 0;
|
|
1630
1649
|
const registry = options?.metrics ? createMetricsRegistry() : void 0;
|
|
1631
1650
|
const serverOptions = options ?? {};
|
|
1651
|
+
parsePolicy(serverOptions.policy, (message) => new Logger("warn").warn(message));
|
|
1632
1652
|
const validResponsesTools = (mode) => mode === "legacy" || mode === "extended";
|
|
1633
1653
|
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
1654
|
fixtures = createServerFixtureList(callerFixtures, () => serverOptions.responsesTools, responsesToolsExtendedView);
|
|
@@ -1687,6 +1707,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1687
1707
|
get strict() {
|
|
1688
1708
|
return serverOptions.strict;
|
|
1689
1709
|
},
|
|
1710
|
+
get policy() {
|
|
1711
|
+
return serverOptions.policy;
|
|
1712
|
+
},
|
|
1690
1713
|
get strictToolArguments() {
|
|
1691
1714
|
return serverOptions.strictToolArguments;
|
|
1692
1715
|
},
|
|
@@ -1812,70 +1835,63 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1812
1835
|
* `toolCallEnvelope` instead of using this one.
|
|
1813
1836
|
*/
|
|
1814
1837
|
function invalidToolArgumentsEnvelope(pathname, message, code) {
|
|
1815
|
-
if (pathname === OLLAMA_CHAT_PATH) return
|
|
1816
|
-
|
|
1838
|
+
if (pathname === OLLAMA_CHAT_PATH) return renderError("ollama", "tool-argument", {
|
|
1839
|
+
message,
|
|
1817
1840
|
code
|
|
1818
|
-
});
|
|
1819
|
-
if (pathname === GEMINI_INTERACTIONS_PATH) return
|
|
1841
|
+
}).body;
|
|
1842
|
+
if (pathname === GEMINI_INTERACTIONS_PATH) return renderError("interactions", "tool-argument", {
|
|
1843
|
+
message,
|
|
1844
|
+
code
|
|
1845
|
+
}).body;
|
|
1846
|
+
if (GEMINI_PATH_RE.test(pathname) || VERTEX_AI_RE.test(pathname)) return renderError("gemini", "tool-argument", {
|
|
1847
|
+
message,
|
|
1820
1848
|
code,
|
|
1821
|
-
|
|
1822
|
-
}
|
|
1823
|
-
if (
|
|
1824
|
-
code: 500,
|
|
1849
|
+
googleDetails: googleFixtureToolCallErrorDetails(code)
|
|
1850
|
+
}).body;
|
|
1851
|
+
if (pathname === MESSAGES_PATH) return renderError("messages", "tool-argument", {
|
|
1825
1852
|
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",
|
|
1853
|
+
code
|
|
1854
|
+
}).body;
|
|
1855
|
+
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
1856
|
message,
|
|
1840
|
-
|
|
1841
|
-
});
|
|
1842
|
-
return
|
|
1857
|
+
code
|
|
1858
|
+
}).body;
|
|
1859
|
+
return renderError("openai", "tool-argument", {
|
|
1843
1860
|
message,
|
|
1844
|
-
type: "server_error",
|
|
1845
1861
|
code
|
|
1846
|
-
}
|
|
1862
|
+
}).body;
|
|
1847
1863
|
}
|
|
1848
1864
|
function invalidMisbehaviorHeaderEnvelope(pathname, detail) {
|
|
1849
1865
|
const code = "aimock_misbehavior_invalid";
|
|
1850
1866
|
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,
|
|
1867
|
+
if (pathname === OLLAMA_CHAT_PATH || pathname === OLLAMA_GENERATE_PATH) return renderError("ollama", "invalid-misbehavior-header", {
|
|
1859
1868
|
message,
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
if (pathname ===
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
});
|
|
1870
|
-
if (
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
});
|
|
1874
|
-
return
|
|
1869
|
+
code
|
|
1870
|
+
}).body;
|
|
1871
|
+
if (pathname === COHERE_CHAT_PATH) return renderError("cohere", "invalid-misbehavior-header", {
|
|
1872
|
+
message,
|
|
1873
|
+
code
|
|
1874
|
+
}).body;
|
|
1875
|
+
if (pathname === GEMINI_INTERACTIONS_PATH) return renderError("interactions", "invalid-misbehavior-header", {
|
|
1876
|
+
message,
|
|
1877
|
+
code
|
|
1878
|
+
}).body;
|
|
1879
|
+
if (GEMINI_PATH_RE.test(pathname) || VERTEX_AI_RE.test(pathname)) return renderError("gemini", "invalid-misbehavior-header", {
|
|
1880
|
+
message,
|
|
1881
|
+
code
|
|
1882
|
+
}).body;
|
|
1883
|
+
if (pathname === MESSAGES_PATH) return renderError("messages", "invalid-misbehavior-header", {
|
|
1875
1884
|
message,
|
|
1876
|
-
type: "invalid_request_error",
|
|
1877
1885
|
code
|
|
1878
|
-
}
|
|
1886
|
+
}).body;
|
|
1887
|
+
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", {
|
|
1888
|
+
message,
|
|
1889
|
+
code
|
|
1890
|
+
}).body;
|
|
1891
|
+
return renderError("openai", "invalid-misbehavior-header", {
|
|
1892
|
+
message,
|
|
1893
|
+
code
|
|
1894
|
+
}).body;
|
|
1879
1895
|
}
|
|
1880
1896
|
/**
|
|
1881
1897
|
* The newest journal entry this request already produced, or null.
|
|
@@ -1990,10 +2006,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1990
2006
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
1991
2007
|
}
|
|
1992
2008
|
setCorsHeaders(res);
|
|
1993
|
-
writeErrorResponse(res, status, invalidToolArguments ? (opts?.toolCallEnvelope ?? ((m, c) => invalidToolArgumentsEnvelope(pathname, m, c)))(msg, fixtureToolCallErrorCode(err)) : opts?.envelope ? opts.envelope(msg, status) :
|
|
2009
|
+
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
2010
|
message: msg,
|
|
1995
2011
|
type: errorTypeForStatus(status)
|
|
1996
|
-
}
|
|
2012
|
+
}).body);
|
|
1997
2013
|
} else {
|
|
1998
2014
|
const midStreamReason = clientAbort ? "client aborted" : "handler crashed mid-stream";
|
|
1999
2015
|
if (existing) {
|
|
@@ -2110,10 +2126,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2110
2126
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
2111
2127
|
}
|
|
2112
2128
|
setCorsHeaders(res);
|
|
2113
|
-
writeErrorResponse(res, 400,
|
|
2129
|
+
writeErrorResponse(res, 400, renderError("openai", "route-failure", {
|
|
2114
2130
|
message: "Invalid request target",
|
|
2115
2131
|
type: "invalid_request_error"
|
|
2116
|
-
}
|
|
2132
|
+
}).body);
|
|
2117
2133
|
return;
|
|
2118
2134
|
}
|
|
2119
2135
|
pathname = parsedUrl.pathname;
|
|
@@ -2126,7 +2142,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2126
2142
|
}
|
|
2127
2143
|
if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname === METRICS_PATH))) {
|
|
2128
2144
|
if (!validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
2129
|
-
writeApiKeyHttpRejection(res, setCorsHeaders);
|
|
2145
|
+
writeApiKeyHttpRejection(res, setCorsHeaders, pathname, resolvePolicy(defaults, req.headers).authErrors);
|
|
2130
2146
|
return;
|
|
2131
2147
|
}
|
|
2132
2148
|
markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
@@ -2193,18 +2209,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2193
2209
|
}
|
|
2194
2210
|
if (pathname === OLLAMA_TAGS_PATH && req.method === "GET") {
|
|
2195
2211
|
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
|
-
}));
|
|
2212
|
+
const body = renderOllamaTags(collect(fixtures, "all").ids, () => (/* @__PURE__ */ new Date()).toISOString());
|
|
2206
2213
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2207
|
-
res.end(JSON.stringify(
|
|
2214
|
+
res.end(JSON.stringify(body));
|
|
2208
2215
|
return;
|
|
2209
2216
|
}
|
|
2210
2217
|
const openRouterVideoContentMatch = pathname.match(OPENROUTER_VIDEO_CONTENT_RE);
|
|
@@ -2325,21 +2332,70 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2325
2332
|
res.end(registry.serialize());
|
|
2326
2333
|
return;
|
|
2327
2334
|
}
|
|
2328
|
-
|
|
2335
|
+
const modelDetailMatch = pathname.match(MODELS_DETAIL_RE);
|
|
2336
|
+
const geminiModelDetailMatch = pathname.match(GEMINI_MODEL_DETAIL_RE);
|
|
2337
|
+
if (req.method === "GET" && (pathname === MODELS_PATH || pathname === GEMINI_MODELS_PATH || modelDetailMatch || geminiModelDetailMatch)) {
|
|
2329
2338
|
setCorsHeaders(res);
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2339
|
+
const google = pathname === GEMINI_MODELS_PATH || geminiModelDetailMatch !== null;
|
|
2340
|
+
const policy = resolvePolicy(defaults, req.headers);
|
|
2341
|
+
const anthropic = !google && (modelDetailMatch !== null || policy.modelCatalog === "native") && isAnthropicModelRequest(req.headers);
|
|
2342
|
+
const missingHeader = anthropic && policy.requiredHeaders === "enforce" ? missingRequiredHeader("messages", req.headers) : void 0;
|
|
2343
|
+
if (missingHeader) {
|
|
2344
|
+
writeErrorResponse(res, 400, renderError("messages", "thinking", {
|
|
2345
|
+
message: `${missingHeader}: header is required`,
|
|
2346
|
+
requestId
|
|
2347
|
+
}).body);
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
const ids = coreModelIds(collect(fixtures, "all").ids);
|
|
2351
|
+
const visible = google ? idsFor(ids, "google") : anthropic ? idsFor(ids, "anthropic") : ids;
|
|
2352
|
+
const catalogError = (status, message) => {
|
|
2353
|
+
const rendered = google ? renderError("gemini", "fixture", {
|
|
2354
|
+
bodyCode: status,
|
|
2355
|
+
status: status === 404 ? "NOT_FOUND" : "INVALID_ARGUMENT",
|
|
2356
|
+
message
|
|
2357
|
+
}) : anthropic ? renderError("messages", "thinking", {
|
|
2358
|
+
type: status === 404 ? "not_found_error" : "invalid_request_error",
|
|
2359
|
+
message
|
|
2360
|
+
}) : renderError("openai", "validation", { message });
|
|
2361
|
+
res.writeHead(status, {
|
|
2362
|
+
"Content-Type": "application/json",
|
|
2363
|
+
...rendered.headers
|
|
2364
|
+
});
|
|
2365
|
+
res.end(rendered.body);
|
|
2366
|
+
};
|
|
2367
|
+
let body;
|
|
2368
|
+
const detail = modelDetailMatch ?? geminiModelDetailMatch;
|
|
2369
|
+
if (detail) {
|
|
2370
|
+
let id;
|
|
2371
|
+
try {
|
|
2372
|
+
id = decodeURIComponent(detail[1]);
|
|
2373
|
+
} catch {
|
|
2374
|
+
catalogError(404, "Model not found");
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
if (!visible.includes(id)) {
|
|
2378
|
+
catalogError(404, `Model not found: ${id}`);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
body = google ? renderGeminiModel(id) : anthropic ? renderAnthropicModel(id) : renderOpenAIModel(id);
|
|
2382
|
+
} else if (google) {
|
|
2383
|
+
const result = geminiPage(visible, parsedUrl.searchParams);
|
|
2384
|
+
if (result.error !== void 0) {
|
|
2385
|
+
catalogError(400, result.error);
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
body = renderGeminiModels(result.page);
|
|
2389
|
+
} else if (anthropic) {
|
|
2390
|
+
const result = anthropicPage(visible, parsedUrl.searchParams);
|
|
2391
|
+
if (result.error !== void 0) {
|
|
2392
|
+
catalogError(400, result.error);
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
body = renderAnthropicModels(result.page);
|
|
2396
|
+
} else body = renderOpenAIModels(ids);
|
|
2338
2397
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2339
|
-
res.end(JSON.stringify(
|
|
2340
|
-
object: "list",
|
|
2341
|
-
data
|
|
2342
|
-
}));
|
|
2398
|
+
res.end(JSON.stringify(body));
|
|
2343
2399
|
return;
|
|
2344
2400
|
}
|
|
2345
2401
|
const batchesCancelMatch = pathname.match(BATCHES_CANCEL_RE);
|
|
@@ -2597,10 +2653,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2597
2653
|
if (limitParam) {
|
|
2598
2654
|
const limit = Number(limitParam);
|
|
2599
2655
|
if (!/^\+?\d+$/.test(limitParam.trim()) || !Number.isSafeInteger(limit) || limit <= 0) {
|
|
2600
|
-
writeErrorResponse(res, 400,
|
|
2656
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
2601
2657
|
message: `Invalid limit parameter: "${limitParam}"`,
|
|
2602
2658
|
type: "invalid_request_error"
|
|
2603
|
-
}
|
|
2659
|
+
}).body);
|
|
2604
2660
|
return;
|
|
2605
2661
|
}
|
|
2606
2662
|
opts = { limit };
|
|
@@ -2625,7 +2681,27 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2625
2681
|
ensureRawFixturePositions();
|
|
2626
2682
|
await handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2627
2683
|
} catch (err) {
|
|
2628
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2684
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2685
|
+
}
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
const geminiCountMatch = pathname.match(GEMINI_COUNT_RE);
|
|
2689
|
+
if (req.method === "POST" && (pathname === MESSAGES_COUNT_PATH || geminiCountMatch)) {
|
|
2690
|
+
const google = geminiCountMatch !== null;
|
|
2691
|
+
try {
|
|
2692
|
+
handleCountTokens(req, res, await readBody(req), google, geminiCountMatch?.[1] ?? "", journal, defaults, setCorsHeaders);
|
|
2693
|
+
} catch (err) {
|
|
2694
|
+
routeError(req, res, err, pathname, {
|
|
2695
|
+
service: google ? "gemini" : "anthropic",
|
|
2696
|
+
envelope: (message, status) => google ? renderError("gemini", "fixture", {
|
|
2697
|
+
message,
|
|
2698
|
+
bodyCode: status,
|
|
2699
|
+
status: status === 400 ? "INVALID_ARGUMENT" : "INTERNAL"
|
|
2700
|
+
}).body : renderError("messages", "thinking", {
|
|
2701
|
+
message,
|
|
2702
|
+
type: status === 400 ? "invalid_request_error" : "api_error"
|
|
2703
|
+
}).body
|
|
2704
|
+
});
|
|
2629
2705
|
}
|
|
2630
2706
|
return;
|
|
2631
2707
|
}
|
|
@@ -2635,7 +2711,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2635
2711
|
ensureRawFixturePositions();
|
|
2636
2712
|
await handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2637
2713
|
} catch (err) {
|
|
2638
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2714
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2639
2715
|
}
|
|
2640
2716
|
return;
|
|
2641
2717
|
}
|
|
@@ -2645,7 +2721,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2645
2721
|
ensureRawFixturePositions();
|
|
2646
2722
|
await handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2647
2723
|
} catch (err) {
|
|
2648
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${
|
|
2724
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2649
2725
|
}
|
|
2650
2726
|
return;
|
|
2651
2727
|
}
|
|
@@ -2811,7 +2887,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2811
2887
|
ensureRawFixturePositions();
|
|
2812
2888
|
await handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2813
2889
|
} catch (err) {
|
|
2814
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2890
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2815
2891
|
}
|
|
2816
2892
|
return;
|
|
2817
2893
|
}
|
|
@@ -2836,7 +2912,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2836
2912
|
ensureRawFixturePositions();
|
|
2837
2913
|
await handleGemini(req, res, raw, geminiModel, streaming, fixtures, journal, defaults, setCorsHeaders);
|
|
2838
2914
|
} catch (err) {
|
|
2839
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2915
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2840
2916
|
}
|
|
2841
2917
|
return;
|
|
2842
2918
|
}
|
|
@@ -2849,7 +2925,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2849
2925
|
ensureRawFixturePositions();
|
|
2850
2926
|
await handleGemini(req, res, raw, vertexModel, streaming, fixtures, journal, defaults, setCorsHeaders, "vertexai");
|
|
2851
2927
|
} catch (err) {
|
|
2852
|
-
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${
|
|
2928
|
+
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${renderError("openai", "stream-failure", { message: m }).body}\n\n` });
|
|
2853
2929
|
}
|
|
2854
2930
|
return;
|
|
2855
2931
|
}
|
|
@@ -2965,10 +3041,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2965
3041
|
try {
|
|
2966
3042
|
voiceId = decodeURIComponent(elevenLabsVoiceMatch[1]);
|
|
2967
3043
|
} catch {
|
|
2968
|
-
writeErrorResponse(res, 400,
|
|
3044
|
+
writeErrorResponse(res, 400, renderError("openai", "validation", {
|
|
2969
3045
|
message: `Invalid voice id '${elevenLabsVoiceMatch[1]}': malformed percent-encoding`,
|
|
2970
3046
|
type: "invalid_request_error"
|
|
2971
|
-
}
|
|
3047
|
+
}).body);
|
|
2972
3048
|
journal.add({
|
|
2973
3049
|
method: req.method ?? "GET",
|
|
2974
3050
|
path: req.url ?? pathname,
|
|
@@ -3118,15 +3194,15 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
3118
3194
|
await handleCompletions(req, res, fixtures, journal, defaults, () => lastJournalEntryFor(req), azureDeploymentId, completionsProvider, isOpenRouter, ensureRawFixturePositions, originalPathname.startsWith("/api/v3/"));
|
|
3119
3195
|
} catch (err) {
|
|
3120
3196
|
routeError(req, res, err, pathname, {
|
|
3121
|
-
envelope: (m, status) => isOpenRouter ? serializeOpenRouterError(status, m) :
|
|
3197
|
+
envelope: (m, status) => isOpenRouter ? serializeOpenRouterError(status, m) : renderError("openai", "route-failure", {
|
|
3122
3198
|
message: m,
|
|
3123
3199
|
type: errorTypeForStatus(status)
|
|
3124
|
-
}
|
|
3200
|
+
}).body,
|
|
3125
3201
|
...isOpenRouter ? { toolCallEnvelope: (m, code) => serializeOpenRouterError(500, m, { reason: code }) } : {},
|
|
3126
|
-
streamEvent: (m, status) => `data: ${isOpenRouter ? serializeOpenRouterError(status, m) :
|
|
3202
|
+
streamEvent: (m, status) => `data: ${isOpenRouter ? serializeOpenRouterError(status, m) : renderError("openai", "route-failure", {
|
|
3127
3203
|
message: m,
|
|
3128
3204
|
type: errorTypeForStatus(status)
|
|
3129
|
-
}
|
|
3205
|
+
}).body}\n\n`
|
|
3130
3206
|
});
|
|
3131
3207
|
}
|
|
3132
3208
|
}
|