@copilotkit/aimock 1.43.0 → 1.45.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 +46 -0
- package/README.md +14 -2
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +31 -5
- 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 +31 -5
- package/dist/aimock-cli.js.map +1 -1
- package/dist/bedrock-converse.cjs +344 -43
- 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 +345 -44
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +195 -42
- 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 +196 -43
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +9 -5
- 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 +10 -7
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +238 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +242 -13
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +159 -72
- 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 +160 -73
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +63 -5
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +37 -4
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +37 -4
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +63 -5
- package/dist/config-loader.js.map +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +6 -2
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/echo-text.cjs +65 -0
- package/dist/echo-text.cjs.map +1 -0
- package/dist/echo-text.d.cts +22 -0
- package/dist/echo-text.d.cts.map +1 -0
- package/dist/echo-text.d.ts +22 -0
- package/dist/echo-text.d.ts.map +1 -0
- package/dist/echo-text.js +65 -0
- package/dist/echo-text.js.map +1 -0
- package/dist/embeddings.cjs +18 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +18 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/fakes-plugin.cjs +131 -0
- package/dist/fakes-plugin.cjs.map +1 -0
- package/dist/fakes-plugin.d.cts +23 -0
- package/dist/fakes-plugin.d.cts.map +1 -0
- package/dist/fakes-plugin.d.ts +23 -0
- package/dist/fakes-plugin.d.ts.map +1 -0
- package/dist/fakes-plugin.js +127 -0
- package/dist/fakes-plugin.js.map +1 -0
- package/dist/files.cjs +9 -11
- 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 +10 -13
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +1 -19
- 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 -20
- package/dist/fine-tuning.js.map +1 -1
- package/dist/fixture-loader-services.cjs +116 -0
- package/dist/fixture-loader-services.cjs.map +1 -0
- package/dist/fixture-loader-services.d.cts +48 -0
- package/dist/fixture-loader-services.d.cts.map +1 -0
- package/dist/fixture-loader-services.d.ts +48 -0
- package/dist/fixture-loader-services.d.ts.map +1 -0
- package/dist/fixture-loader-services.js +114 -0
- package/dist/fixture-loader-services.js.map +1 -0
- package/dist/fixture-loader.cjs +552 -29
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +140 -2
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +140 -2
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +542 -31
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +42 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +43 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +288 -52
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +289 -53
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +234 -38
- 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 +235 -39
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +1157 -12
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +85 -4
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +85 -4
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +1113 -14
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +3 -2
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +3 -2
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +39 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +11 -4
- package/dist/jest.cjs +43 -14
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +47 -5
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +47 -5
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +45 -16
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +155 -13
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +54 -4
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +54 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +155 -13
- package/dist/journal.js.map +1 -1
- package/dist/live-sanitize.cjs +4 -32
- package/dist/live-sanitize.cjs.map +1 -1
- package/dist/live-sanitize.js +4 -32
- package/dist/live-sanitize.js.map +1 -1
- package/dist/llmock.cjs +204 -39
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +51 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +51 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +206 -41
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +217 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -0
- package/dist/mcp-fakes-mount.js +211 -0
- package/dist/mcp-fakes-mount.js.map +1 -0
- package/dist/mcp-fakes-report.cjs +111 -0
- package/dist/mcp-fakes-report.cjs.map +1 -0
- package/dist/mcp-fakes-report.d.cts +40 -0
- package/dist/mcp-fakes-report.d.cts.map +1 -0
- package/dist/mcp-fakes-report.d.ts +40 -0
- package/dist/mcp-fakes-report.d.ts.map +1 -0
- package/dist/mcp-fakes-report.js +107 -0
- package/dist/mcp-fakes-report.js.map +1 -0
- package/dist/mcp-fakes.cjs +1942 -0
- package/dist/mcp-fakes.cjs.map +1 -0
- package/dist/mcp-fakes.d.cts +504 -0
- package/dist/mcp-fakes.d.cts.map +1 -0
- package/dist/mcp-fakes.d.ts +504 -0
- package/dist/mcp-fakes.d.ts.map +1 -0
- package/dist/mcp-fakes.js +1937 -0
- package/dist/mcp-fakes.js.map +1 -0
- package/dist/mcp-handler.cjs +534 -27
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.d.cts +49 -0
- package/dist/mcp-handler.d.cts.map +1 -0
- package/dist/mcp-handler.d.ts +49 -0
- package/dist/mcp-handler.d.ts.map +1 -0
- package/dist/mcp-handler.js +530 -28
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +326 -47
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +97 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +97 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +327 -48
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-recorder.cjs +1224 -0
- package/dist/mcp-recorder.cjs.map +1 -0
- package/dist/mcp-recorder.d.cts +108 -0
- package/dist/mcp-recorder.d.cts.map +1 -0
- package/dist/mcp-recorder.d.ts +108 -0
- package/dist/mcp-recorder.d.ts.map +1 -0
- package/dist/mcp-recorder.js +1215 -0
- package/dist/mcp-recorder.js.map +1 -0
- package/dist/mcp-stub.cjs +5 -1
- package/dist/mcp-stub.d.cts +3 -1
- package/dist/mcp-stub.d.ts +3 -1
- package/dist/mcp-stub.js +3 -1
- package/dist/mcp-types.d.cts +6 -0
- package/dist/mcp-types.d.ts +6 -0
- package/dist/message-text.cjs +192 -0
- package/dist/message-text.cjs.map +1 -0
- package/dist/message-text.d.cts +31 -0
- package/dist/message-text.d.cts.map +1 -0
- package/dist/message-text.d.ts +31 -0
- package/dist/message-text.d.ts.map +1 -0
- package/dist/message-text.js +184 -0
- package/dist/message-text.js.map +1 -0
- package/dist/messages.cjs +400 -56
- 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 +401 -58
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +46 -66
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +14 -6
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +14 -6
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +32 -40
- package/dist/metrics.js.map +1 -1
- package/dist/misbehavior.cjs +1523 -0
- package/dist/misbehavior.cjs.map +1 -0
- package/dist/misbehavior.d.cts +43 -0
- package/dist/misbehavior.d.cts.map +1 -0
- package/dist/misbehavior.d.ts +43 -0
- package/dist/misbehavior.d.ts.map +1 -0
- package/dist/misbehavior.js +1511 -0
- package/dist/misbehavior.js.map +1 -0
- package/dist/ollama.cjs +315 -113
- 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 +316 -114
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +3405 -0
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +30 -0
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +3404 -0
- package/dist/openapi.js.map +1 -0
- package/dist/openrouter-video.cjs +4 -4
- 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 +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/record-sanitize.cjs +231 -0
- package/dist/record-sanitize.cjs.map +1 -0
- package/dist/record-sanitize.js +224 -0
- package/dist/record-sanitize.js.map +1 -0
- package/dist/recorder.cjs +115 -6
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +2 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +2 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +116 -8
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +701 -166
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +5 -13
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +5 -13
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +699 -169
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +951 -0
- package/dist/route-registry.cjs.map +1 -0
- package/dist/route-registry.d.cts +33 -0
- package/dist/route-registry.d.cts.map +1 -0
- package/dist/route-registry.d.ts +33 -0
- package/dist/route-registry.d.ts.map +1 -0
- package/dist/route-registry.js +866 -0
- package/dist/route-registry.js.map +1 -0
- package/dist/router.cjs +24 -2
- 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 +25 -4
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +1159 -264
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +4 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1067 -172
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +4 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +4 -2
- package/dist/speech.js.map +1 -1
- package/dist/stream-collapse.cjs +236 -34
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +39 -4
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +39 -4
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +235 -35
- package/dist/stream-collapse.js.map +1 -1
- package/dist/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +2 -2
- 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 +2 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +516 -5
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +516 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-cli.cjs +123 -33
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +125 -35
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-stores.cjs +1180 -0
- package/dist/vector-stores.cjs.map +1 -0
- package/dist/vector-stores.d.cts +106 -0
- package/dist/vector-stores.d.cts.map +1 -0
- package/dist/vector-stores.d.ts +106 -0
- package/dist/vector-stores.d.ts.map +1 -0
- package/dist/vector-stores.js +1164 -0
- package/dist/vector-stores.js.map +1 -0
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/version.cjs +45 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.js +44 -0
- package/dist/version.js.map +1 -0
- package/dist/video.cjs +19 -0
- 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 +19 -0
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +77 -16
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +38 -5
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +38 -5
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +78 -17
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +6 -2
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +6 -2
- package/dist/watcher.js.map +1 -1
- package/dist/ws-gemini-live.cjs +210 -101
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +9 -3
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +9 -3
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +211 -102
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +175 -62
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +8 -3
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +8 -3
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +176 -63
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +121 -16
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts +8 -12
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts +8 -12
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +123 -18
- package/dist/ws-responses.js.map +1 -1
- package/package.json +13 -2
- package/skills/write-fixtures/SKILL.md +179 -21
package/dist/server.cjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_constants = require('./constants.cjs');
|
|
3
|
+
const require_message_text = require('./message-text.cjs');
|
|
3
4
|
const require_api_key_auth = require('./api-key-auth.cjs');
|
|
4
5
|
const require_helpers = require('./helpers.cjs');
|
|
5
6
|
const require_live_fixture = require('./live-fixture.cjs');
|
|
6
7
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
7
8
|
const require_chaos = require('./chaos.cjs');
|
|
9
|
+
const require_misbehavior = require('./misbehavior.cjs');
|
|
8
10
|
const require_fixture_loader = require('./fixture-loader.cjs');
|
|
9
11
|
const require_router = require('./router.cjs');
|
|
10
12
|
const require_recorder = require('./recorder.cjs');
|
|
@@ -38,6 +40,7 @@ const require_search = require('./search.cjs');
|
|
|
38
40
|
const require_rerank = require('./rerank.cjs');
|
|
39
41
|
const require_moderation = require('./moderation.cjs');
|
|
40
42
|
const require_fine_tuning = require('./fine-tuning.cjs');
|
|
43
|
+
const require_vector_stores = require('./vector-stores.cjs');
|
|
41
44
|
const require_files = require('./files.cjs');
|
|
42
45
|
const require_batches = require('./batches.cjs');
|
|
43
46
|
const require_ws_framing = require('./ws-framing.cjs');
|
|
@@ -45,42 +48,19 @@ const require_ws_live = require('./ws-live.cjs');
|
|
|
45
48
|
const require_ws_responses = require('./ws-responses.cjs');
|
|
46
49
|
const require_ws_realtime = require('./ws-realtime.cjs');
|
|
47
50
|
const require_ws_gemini_live = require('./ws-gemini-live.cjs');
|
|
51
|
+
const require_route_registry = require('./route-registry.cjs');
|
|
52
|
+
const require_openapi = require('./openapi.cjs');
|
|
48
53
|
const require_metrics = require('./metrics.cjs');
|
|
54
|
+
const require_mcp_fakes = require('./mcp-fakes.cjs');
|
|
55
|
+
const require_mcp_mock = require('./mcp-mock.cjs');
|
|
56
|
+
const require_mcp_fakes_mount = require('./mcp-fakes-mount.cjs');
|
|
57
|
+
const require_mcp_fakes_report = require('./mcp-fakes-report.cjs');
|
|
49
58
|
let node_http = require("node:http");
|
|
50
59
|
node_http = require_runtime.__toESM(node_http);
|
|
51
60
|
let node_async_hooks = require("node:async_hooks");
|
|
52
61
|
|
|
53
62
|
//#region src/server.ts
|
|
54
63
|
const liveClosers = /* @__PURE__ */ new WeakMap();
|
|
55
|
-
const COMPLETIONS_PATH = "/v1/chat/completions";
|
|
56
|
-
const RESPONSES_PATH = "/v1/responses";
|
|
57
|
-
const LIVE_PATH = "/v1/live/sessions";
|
|
58
|
-
const REALTIME_PATH = "/v1/realtime";
|
|
59
|
-
const GEMINI_LIVE_PATH = "/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
|
|
60
|
-
const MESSAGES_PATH = "/v1/messages";
|
|
61
|
-
const EMBEDDINGS_PATH = "/v1/embeddings";
|
|
62
|
-
const COHERE_CHAT_PATH = "/v2/chat";
|
|
63
|
-
const COHERE_EMBED_PATH = "/v2/embed";
|
|
64
|
-
const SEARCH_PATH = "/search";
|
|
65
|
-
const RERANK_PATH = "/v2/rerank";
|
|
66
|
-
const MODERATIONS_PATH = "/v1/moderations";
|
|
67
|
-
const IMAGES_PATH = "/v1/images/generations";
|
|
68
|
-
const IMAGES_EDIT_PATH = "/v1/images/edits";
|
|
69
|
-
const IMAGES_VARIATIONS_PATH = "/v1/images/variations";
|
|
70
|
-
const SPEECH_PATH = "/v1/audio/speech";
|
|
71
|
-
const TRANSCRIPTIONS_PATH = "/v1/audio/transcriptions";
|
|
72
|
-
const TRANSLATIONS_PATH = "/v1/audio/translations";
|
|
73
|
-
const VIDEOS_PATH = "/v1/videos";
|
|
74
|
-
const GEMINI_PREDICT_RE = /^\/v1beta\/models\/([^:]+):predict$/;
|
|
75
|
-
const ELEVENLABS_SOUND_GENERATION_PATH = "/v1/sound-generation";
|
|
76
|
-
const ELEVENLABS_TTS_RE = /^\/v1\/text-to-speech\/([^/]+)$/;
|
|
77
|
-
const ELEVENLABS_MUSIC_RE = /^\/v1\/music(?:\/(.+))?$/;
|
|
78
|
-
const ELEVENLABS_VOICE_DESIGN_PATH = "/v1/text-to-voice/design";
|
|
79
|
-
const ELEVENLABS_VOICE_CREATE_PATH = "/v1/text-to-voice";
|
|
80
|
-
const ELEVENLABS_VOICE_RE = /^\/v1\/voices\/([^/]+)$/;
|
|
81
|
-
const FAL_QUEUE_SUBMIT_RE = /^\/fal\/queue\/submit\/(.+)$/;
|
|
82
|
-
const FAL_QUEUE_REQUESTS_RE = /^\/fal\/queue\/requests\/(.+)$/;
|
|
83
|
-
const FAL_RUN_RE = /^\/fal\/run\/(.+)$/;
|
|
84
64
|
const DEFAULT_CHUNK_SIZE = 20;
|
|
85
65
|
const COMPAT_SUFFIXES = [
|
|
86
66
|
"/chat/completions",
|
|
@@ -109,34 +89,6 @@ function normalizeCompatPath(pathname, logger) {
|
|
|
109
89
|
}
|
|
110
90
|
return pathname;
|
|
111
91
|
}
|
|
112
|
-
const GEMINI_INTERACTIONS_PATH = "/v1beta/interactions";
|
|
113
|
-
const GEMINI_PATH_RE = /^\/v1beta\/models\/([^:]+):(generateContent|streamGenerateContent)$/;
|
|
114
|
-
const GEMINI_EMBED_RE = /^\/v1beta\/models\/([^:]+):embedContent$/;
|
|
115
|
-
const AZURE_DEPLOYMENT_RE = /^\/openai\/deployments\/([^/]+)\/(chat\/completions|embeddings)$/;
|
|
116
|
-
const BEDROCK_INVOKE_RE = /^\/model\/([^/]+)\/invoke$/;
|
|
117
|
-
const BEDROCK_STREAM_RE = /^\/model\/([^/]+)\/invoke-with-response-stream$/;
|
|
118
|
-
const BEDROCK_CONVERSE_RE = /^\/model\/([^/]+)\/converse$/;
|
|
119
|
-
const BEDROCK_CONVERSE_STREAM_RE = /^\/model\/([^/]+)\/converse-stream$/;
|
|
120
|
-
const VERTEX_AI_RE = /^\/v1\/projects\/[^/]+\/locations\/[^/]+\/publishers\/google\/models\/([^/:]+):(generateContent|streamGenerateContent)$/;
|
|
121
|
-
const OLLAMA_CHAT_PATH = "/api/chat";
|
|
122
|
-
const OLLAMA_GENERATE_PATH = "/api/generate";
|
|
123
|
-
const OLLAMA_EMBEDDINGS_PATH = "/api/embeddings";
|
|
124
|
-
const OLLAMA_EMBED_PATH = "/api/embed";
|
|
125
|
-
const OLLAMA_TAGS_PATH = "/api/tags";
|
|
126
|
-
const OPENROUTER_VIDEOS_PATH = "/api/v1/videos";
|
|
127
|
-
const OPENROUTER_VIDEO_MODELS_PATH = "/api/v1/videos/models";
|
|
128
|
-
const HEALTH_PATH = "/health";
|
|
129
|
-
const READY_PATH = "/ready";
|
|
130
|
-
const MODELS_PATH = "/v1/models";
|
|
131
|
-
const REQUESTS_PATH = "/v1/_requests";
|
|
132
|
-
const BATCHES_PATH = "/v1/batches";
|
|
133
|
-
const BATCHES_ID_RE = /^\/v1\/batches\/([^/]+)$/;
|
|
134
|
-
const BATCHES_CANCEL_RE = /^\/v1\/batches\/([^/]+)\/cancel$/;
|
|
135
|
-
const FILES_PATH = "/v1/files";
|
|
136
|
-
const FINE_TUNING_JOBS_PATH = "/v1/fine_tuning/jobs";
|
|
137
|
-
const FINE_TUNING_ID_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)$/;
|
|
138
|
-
const FINE_TUNING_CANCEL_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)\/cancel$/;
|
|
139
|
-
const FINE_TUNING_EVENTS_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)\/events$/;
|
|
140
92
|
const DEFAULT_MODELS = [
|
|
141
93
|
"gpt-4",
|
|
142
94
|
"gpt-4o",
|
|
@@ -146,7 +98,7 @@ const DEFAULT_MODELS = [
|
|
|
146
98
|
];
|
|
147
99
|
const CORS_HEADERS = {
|
|
148
100
|
"Access-Control-Allow-Origin": "*",
|
|
149
|
-
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
101
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
150
102
|
"Access-Control-Allow-Headers": "*",
|
|
151
103
|
"Access-Control-Expose-Headers": "X-Total-Count, X-Request-Id"
|
|
152
104
|
};
|
|
@@ -165,7 +117,6 @@ function handleNotFound(res, message) {
|
|
|
165
117
|
type: "not_found"
|
|
166
118
|
} }));
|
|
167
119
|
}
|
|
168
|
-
const CONTROL_PREFIX = "/__aimock";
|
|
169
120
|
/** The complete `GET /__aimock/fixtures` query-param vocabulary; anything else 400s. */
|
|
170
121
|
const FIXTURES_PARAMS = new Set(["include"]);
|
|
171
122
|
/** The complete `GET /__aimock/journal` query-param vocabulary; anything else 400s. */
|
|
@@ -204,6 +155,7 @@ function performFullReset(fixtures, targets) {
|
|
|
204
155
|
require_elevenlabs_voice.clearElevenLabsVoices();
|
|
205
156
|
require_files.clearFileStore();
|
|
206
157
|
require_fine_tuning.clearFineTuningStore();
|
|
158
|
+
require_vector_stores.clearVectorStoreStore();
|
|
207
159
|
require_gemini_interactions.resetInteractionCounter();
|
|
208
160
|
require_gemini_interactions.resetEventIdCounter();
|
|
209
161
|
if (!targets) return;
|
|
@@ -211,11 +163,13 @@ function performFullReset(fixtures, targets) {
|
|
|
211
163
|
targets.journal.clear();
|
|
212
164
|
require_chaos.resetChaosWarnings(targets.defaults.logger);
|
|
213
165
|
targets.defaults.chaos = void 0;
|
|
166
|
+
targets.defaults.misbehavior?.byTestId.clear();
|
|
214
167
|
targets.videoStates.clear();
|
|
215
168
|
targets.openRouterVideoJobs.clear();
|
|
216
169
|
targets.veoVideoJobs.clear();
|
|
217
170
|
targets.grokVideoJobs.clear();
|
|
218
171
|
targets.bytePlusVideoJobs.clear();
|
|
172
|
+
for (const { handler } of targets.mounts ?? []) handler.clearMcpFakes?.();
|
|
219
173
|
if (targets.defaults.registry) targets.defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
220
174
|
}
|
|
221
175
|
/**
|
|
@@ -255,7 +209,7 @@ function fixtureResponseKind(response) {
|
|
|
255
209
|
if (require_helpers.isImageResponse(response)) return "image";
|
|
256
210
|
if (require_helpers.isEmbeddingResponse(response)) return "embedding";
|
|
257
211
|
if (require_helpers.isJSONResponse(response)) return "json";
|
|
258
|
-
if (require_helpers.
|
|
212
|
+
if (require_helpers.isCombinedFixtureResponse(response)) return "contentWithToolCalls";
|
|
259
213
|
if (require_helpers.isToolCallResponse(response)) return "toolCalls";
|
|
260
214
|
if (require_helpers.isTextResponse(response)) return "text";
|
|
261
215
|
return "unknown";
|
|
@@ -270,9 +224,14 @@ function fixtureResponseKind(response) {
|
|
|
270
224
|
* scoping path does not also do. (Splitting on "," additionally broke every
|
|
271
225
|
* legitimately comma-bearing id.) A raw `path.includes("testId=t1")` both
|
|
272
226
|
* prefix-collides with `t10` and matches unrelated params like `notTestId`.
|
|
227
|
+
*
|
|
228
|
+
* An MCP mount records the test id it resolved on the entry (I9, B3): a
|
|
229
|
+
* decoded header or query value, or the one bound to the session at
|
|
230
|
+
* `initialize`. That value wins; LLM entries carry none and are attributed as
|
|
231
|
+
* before.
|
|
273
232
|
*/
|
|
274
233
|
function journalEntryTestId(entry) {
|
|
275
|
-
return require_helpers.resolveTestId(entry.headers, entry.path);
|
|
234
|
+
return entry.testId ?? require_helpers.resolveTestId(entry.headers, entry.path);
|
|
276
235
|
}
|
|
277
236
|
/**
|
|
278
237
|
* The testId a chaos override is stored under, and read back with — resolved by
|
|
@@ -311,19 +270,188 @@ function effectiveChaos(defaults, scopeId) {
|
|
|
311
270
|
if (!current) return {};
|
|
312
271
|
return require_chaos.isChaosScope(current) ? current.base ?? {} : current;
|
|
313
272
|
}
|
|
273
|
+
/** The `GET /__aimock/mcp/fakes` query-param vocabulary; anything else 400s. */
|
|
274
|
+
const MCP_FAKES_PARAMS = new Set([
|
|
275
|
+
"testId",
|
|
276
|
+
"context",
|
|
277
|
+
"mount"
|
|
278
|
+
]);
|
|
279
|
+
/** A block's `mount` path, read without running getters; the add validates it. */
|
|
280
|
+
function controlFakeMountOf(raw) {
|
|
281
|
+
if (typeof raw !== "object" || raw === null) return require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT;
|
|
282
|
+
const value = Object.getOwnPropertyDescriptor(raw, "mount")?.value;
|
|
283
|
+
return typeof value === "string" ? value : require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Check one `POST /__aimock/fixtures` `mcpFakes` value against the live
|
|
287
|
+
* mounts (F10, W6) without changing anything. Returns every error in input
|
|
288
|
+
* order (an empty list when the value can be added), and `apply`, which adds
|
|
289
|
+
* the blocks (auto-mounting an MCPMock where no mount serves a block's path)
|
|
290
|
+
* and returns the number of blocks added.
|
|
291
|
+
*
|
|
292
|
+
* The whole value is checked before any mount is touched, and the check
|
|
293
|
+
* never adds to a real mount: each group is added to a throwaway store,
|
|
294
|
+
* whose errors are the ones reported (it numbers the add `control-api#1`, as
|
|
295
|
+
* a new mount would; for an existing MCPMock the rejected group is re-run on
|
|
296
|
+
* its atomic add, so the errors name that mount's next `<n>`, I7). A group
|
|
297
|
+
* whose path no request to it can reach (a mount that is not an MCPMock
|
|
298
|
+
* serves it, an MCPMock answers it as its root, or it is under the control
|
|
299
|
+
* prefix) is a mount conflict (one error per block).
|
|
300
|
+
*/
|
|
301
|
+
function planControlApiFakes(raw, mounts) {
|
|
302
|
+
const items = Array.isArray(raw) ? raw.map((block, position) => ({
|
|
303
|
+
position,
|
|
304
|
+
source: {
|
|
305
|
+
source: "control-api",
|
|
306
|
+
blockIndex: position,
|
|
307
|
+
raw: block
|
|
308
|
+
}
|
|
309
|
+
})) : [{
|
|
310
|
+
position: 0,
|
|
311
|
+
source: {
|
|
312
|
+
source: "control-api",
|
|
313
|
+
blockIndex: null,
|
|
314
|
+
raw
|
|
315
|
+
}
|
|
316
|
+
}];
|
|
317
|
+
const groups = /* @__PURE__ */ new Map();
|
|
318
|
+
if (items.length === 0) groups.set(require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT, {
|
|
319
|
+
path: require_mcp_fakes.MCP_FAKES_DEFAULT_MOUNT,
|
|
320
|
+
positions: [],
|
|
321
|
+
sources: []
|
|
322
|
+
});
|
|
323
|
+
for (const { position, source } of items) {
|
|
324
|
+
const path = controlFakeMountOf(source.raw);
|
|
325
|
+
const group = groups.get(path) ?? {
|
|
326
|
+
path,
|
|
327
|
+
positions: [],
|
|
328
|
+
sources: []
|
|
329
|
+
};
|
|
330
|
+
group.positions.push(position);
|
|
331
|
+
group.sources.push(source);
|
|
332
|
+
groups.set(path, group);
|
|
333
|
+
}
|
|
334
|
+
const found = [];
|
|
335
|
+
/** The paths `apply` auto-mounts, in order. */
|
|
336
|
+
const pending = [];
|
|
337
|
+
for (const group of groups.values()) {
|
|
338
|
+
const target = require_mcp_fakes_mount.findFakeMount(mounts, group.path, pending);
|
|
339
|
+
if (target.kind === "conflict") {
|
|
340
|
+
group.positions.forEach((position) => {
|
|
341
|
+
const where = Array.isArray(raw) ? require_message_text.msg`mcpFakes[${position}]` : require_message_text.msg`mcpFakes`;
|
|
342
|
+
found.push({
|
|
343
|
+
position,
|
|
344
|
+
error: new require_fixture_loader.FixtureLoadError({
|
|
345
|
+
rule: "mcp-fakes/mount-conflict",
|
|
346
|
+
file: null,
|
|
347
|
+
blockId: null,
|
|
348
|
+
entryId: null,
|
|
349
|
+
detail: require_message_text.msg`${where}: ${require_mcp_fakes_mount.mountConflictDetail(group.path, target)}`
|
|
350
|
+
})
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
let failure = addFailure(() => new require_mcp_fakes.McpFakeStore().add(group.sources, { kind: "control-api" }));
|
|
356
|
+
if (target.kind === "none") pending.push(group.path);
|
|
357
|
+
if (!failure) continue;
|
|
358
|
+
if (target.kind === "mcp" && target.handler.addMcpFakes === require_mcp_mock.MCPMock.prototype.addMcpFakes) {
|
|
359
|
+
const handler = target.handler;
|
|
360
|
+
failure = addFailure(() => handler.addMcpFakes?.(group.sources, { kind: "control-api" })) ?? failure;
|
|
361
|
+
}
|
|
362
|
+
for (const error of failure.errors) found.push({
|
|
363
|
+
position: positionOf(error, group),
|
|
364
|
+
error
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
found.sort((a, b) => a.position - b.position);
|
|
368
|
+
return {
|
|
369
|
+
errors: found.map((f) => f.error),
|
|
370
|
+
apply(wiring) {
|
|
371
|
+
let added = 0;
|
|
372
|
+
for (const group of groups.values()) {
|
|
373
|
+
const target = require_mcp_fakes_mount.ensureFakeMount(mounts, group.path, {
|
|
374
|
+
journal: wiring.journal,
|
|
375
|
+
registry: wiring.defaults.registry,
|
|
376
|
+
logger: wiring.defaults.logger,
|
|
377
|
+
replaySpeed: wiring.defaults.replaySpeed
|
|
378
|
+
}, wiring.defaults.logger);
|
|
379
|
+
if ("conflict" in target) continue;
|
|
380
|
+
const result = target.handler.addMcpFakes?.(group.sources, { kind: "control-api" });
|
|
381
|
+
for (const warning of result?.warnings ?? []) wiring.defaults.logger.warn(warning.message);
|
|
382
|
+
added += group.sources.length;
|
|
383
|
+
}
|
|
384
|
+
return added;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
/** The `McpFakesAddError` that `add` throws, or `null` when it adds. */
|
|
389
|
+
function addFailure(add) {
|
|
390
|
+
try {
|
|
391
|
+
add();
|
|
392
|
+
return null;
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (err instanceof require_mcp_fakes.McpFakesAddError) return err;
|
|
395
|
+
throw err;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The request position of the block an add error names. The add numbers a
|
|
400
|
+
* run-time group's blocks by their place in the group (`[<j>]`), or gives no
|
|
401
|
+
* index to a single-object value; an error tied to no block sorts with the
|
|
402
|
+
* group's first block.
|
|
403
|
+
*/
|
|
404
|
+
function positionOf(error, group) {
|
|
405
|
+
const first = group.positions[0] ?? 0;
|
|
406
|
+
if (error.blockId == null || error.file === null) return first;
|
|
407
|
+
const at = group.positions.findIndex((_, j) => error.blockId === require_mcp_fakes.blockIdOf(error.file ?? "", "", j));
|
|
408
|
+
return at === -1 ? first : group.positions[at];
|
|
409
|
+
}
|
|
410
|
+
function hasFakesSnapshot(handler) {
|
|
411
|
+
return "fakesSnapshot" in handler && typeof handler.fakesSnapshot === "function";
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* `GET /__aimock/mcp/fakes` (spec 6.5, I9): for each MCP mount (or the one
|
|
415
|
+
* named by `mount`), the blocks that apply to the requested test id and
|
|
416
|
+
* context, with every entry id and its consumed state for that test id.
|
|
417
|
+
* A mount is listed when it takes fakes (it implements `addMcpFakes`, the
|
|
418
|
+
* check every other fakes path uses); one with no `fakesSnapshot` lists no
|
|
419
|
+
* blocks.
|
|
420
|
+
*/
|
|
421
|
+
function listControlApiFakes(mounts, testId, context, mount) {
|
|
422
|
+
const listed = mounts.flatMap(({ path, handler }) => require_mcp_fakes_mount.isFakeMount(handler) && (mount === null || path === mount) ? [{
|
|
423
|
+
mount: path,
|
|
424
|
+
blocks: hasFakesSnapshot(handler) ? handler.fakesSnapshot(testId, context) : []
|
|
425
|
+
}] : []);
|
|
426
|
+
return mount !== null && listed.length === 0 ? null : listed;
|
|
427
|
+
}
|
|
428
|
+
const controlFixtureAdditions = /* @__PURE__ */ new WeakMap();
|
|
314
429
|
/**
|
|
315
430
|
* Handle requests under `/__aimock/`. Returns `true` if the request was
|
|
316
431
|
* handled, `false` if the path doesn't match the control prefix.
|
|
317
432
|
*/
|
|
318
|
-
async function handleControlAPI(req, res, pathname, searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults) {
|
|
319
|
-
if (!pathname.startsWith(CONTROL_PREFIX)) return false;
|
|
320
|
-
const subPath = pathname.slice(
|
|
433
|
+
async function handleControlAPI(req, res, pathname, searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults, mounts) {
|
|
434
|
+
if (!pathname.startsWith(require_route_registry.CONTROL_PREFIX)) return false;
|
|
435
|
+
const subPath = pathname.slice(require_route_registry.CONTROL_PREFIX.length);
|
|
321
436
|
setCorsHeaders(res);
|
|
322
437
|
if (subPath === "/health" && req.method === "GET") {
|
|
323
438
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
324
439
|
res.end(JSON.stringify({ status: "ok" }));
|
|
325
440
|
return true;
|
|
326
441
|
}
|
|
442
|
+
if (subPath === "/openapi.json" && req.method === "GET") {
|
|
443
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
444
|
+
res.end(JSON.stringify(require_openapi.buildOpenApiDocument()));
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
if (subPath === "/routes" && req.method === "GET") {
|
|
448
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
449
|
+
res.end(JSON.stringify({
|
|
450
|
+
count: require_openapi.CATALOG_ROUTES.length,
|
|
451
|
+
routes: require_openapi.CATALOG_ROUTES
|
|
452
|
+
}));
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
327
455
|
if (subPath === "/journal" && req.method === "GET") {
|
|
328
456
|
const bad = (message) => {
|
|
329
457
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
@@ -369,7 +497,7 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
369
497
|
"Content-Type": "application/json",
|
|
370
498
|
"X-Total-Count": String(total)
|
|
371
499
|
});
|
|
372
|
-
res.end(
|
|
500
|
+
res.end(journalJson(entries, journal));
|
|
373
501
|
return true;
|
|
374
502
|
}
|
|
375
503
|
if (subPath === "/fixtures" && req.method === "GET") {
|
|
@@ -389,19 +517,70 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
389
517
|
res.end(JSON.stringify({ error: "Invalid 'include': expected 'fixtures'" }));
|
|
390
518
|
return true;
|
|
391
519
|
}
|
|
520
|
+
const extendedTools = require_helpers.isExtendedResponsesToolsList(fixtures);
|
|
392
521
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
393
522
|
res.end(JSON.stringify({
|
|
394
523
|
count: fixtures.length,
|
|
395
524
|
fixtures: fixtures.map((fixture, index) => ({
|
|
396
525
|
index,
|
|
397
526
|
match: redactForInspection(fixture.match),
|
|
398
|
-
responseKind: require_fixture_loader.isOneShotError(fixture) ? "error" : fixtureResponseKind(fixture.response),
|
|
527
|
+
responseKind: require_fixture_loader.isOneShotError(fixture) ? "error" : fixtureResponseKind(extendedTools || typeof fixture.response === "function" ? fixture.response : require_helpers.withoutResponsesToolKeys(fixture.response)),
|
|
399
528
|
...fixture.latency !== void 0 ? { latency: fixture.latency } : {},
|
|
400
529
|
...fixture.chaos !== void 0 ? { chaos: fixture.chaos } : {}
|
|
401
530
|
}))
|
|
402
531
|
}));
|
|
403
532
|
return true;
|
|
404
533
|
}
|
|
534
|
+
if (subPath === "/misbehavior/catalog" && req.method === "GET") {
|
|
535
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
536
|
+
res.end(JSON.stringify({
|
|
537
|
+
catalog: require_misbehavior.MISBEHAVIOR_CATALOG,
|
|
538
|
+
WIRE_SUPPORT: require_misbehavior.WIRE_SUPPORT
|
|
539
|
+
}));
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
if (subPath === "/misbehavior" && (req.method === "GET" || req.method === "POST" || req.method === "DELETE")) {
|
|
543
|
+
const scopeId = chaosScopeId(req);
|
|
544
|
+
if (scopeId === null) return writeBlankTestId(res);
|
|
545
|
+
const reply = (status, body) => {
|
|
546
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
547
|
+
res.end(JSON.stringify(body));
|
|
548
|
+
return true;
|
|
549
|
+
};
|
|
550
|
+
const scope = defaults.misbehavior;
|
|
551
|
+
if (!scope && req.method !== "GET") return reply(409, { error: "misbehavior is not enabled: start aimock with enableMisbehavior: true (CLI: --misbehavior)" });
|
|
552
|
+
if (req.method === "POST") {
|
|
553
|
+
let raw;
|
|
554
|
+
try {
|
|
555
|
+
raw = await require_helpers.readBody(req);
|
|
556
|
+
} catch (err) {
|
|
557
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
558
|
+
defaults.logger.error(`POST /__aimock/misbehavior: failed to read body: ${msg}`);
|
|
559
|
+
return reply(400, { error: `Failed to read request body: ${msg}` });
|
|
560
|
+
}
|
|
561
|
+
let input;
|
|
562
|
+
try {
|
|
563
|
+
input = JSON.parse(raw);
|
|
564
|
+
} catch (err) {
|
|
565
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
566
|
+
defaults.logger.error(`POST /__aimock/misbehavior: invalid JSON: ${msg}`);
|
|
567
|
+
return reply(400, { error: `Invalid JSON: ${msg}` });
|
|
568
|
+
}
|
|
569
|
+
if (require_helpers.isJsonObject(input) && Object.keys(input).length === 0) input = { faults: [] };
|
|
570
|
+
const parsed = require_misbehavior.parseMisbehavior(input);
|
|
571
|
+
if (!parsed.ok) return reply(400, {
|
|
572
|
+
error: "Validation failed",
|
|
573
|
+
rule: parsed.issue.rule,
|
|
574
|
+
message: parsed.issue.message
|
|
575
|
+
});
|
|
576
|
+
const config = parsed.config.seed === "random" ? {
|
|
577
|
+
...parsed.config,
|
|
578
|
+
seed: require_misbehavior.resolveMisbehaviorSeed(parsed.config.seed, defaults.logger)
|
|
579
|
+
} : parsed.config;
|
|
580
|
+
scope?.byTestId.set(scopeId, config);
|
|
581
|
+
} else if (req.method === "DELETE") scope?.byTestId.delete(scopeId);
|
|
582
|
+
return reply(200, { misbehavior: scope?.byTestId.get(scopeId) ?? scope?.baseline ?? { faults: [] } });
|
|
583
|
+
}
|
|
405
584
|
if (subPath === "/chaos" && req.method === "GET") {
|
|
406
585
|
const scopeId = chaosScopeId(req);
|
|
407
586
|
if (scopeId === null) return writeBlankTestId(res);
|
|
@@ -474,6 +653,25 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
474
653
|
res.end(JSON.stringify({ chaos: effectiveChaos(defaults, scopeId) }));
|
|
475
654
|
return true;
|
|
476
655
|
}
|
|
656
|
+
if (subPath === "/mcp/fakes" && req.method === "GET") {
|
|
657
|
+
const reply = (status, body) => {
|
|
658
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
659
|
+
res.end(JSON.stringify(body));
|
|
660
|
+
return true;
|
|
661
|
+
};
|
|
662
|
+
for (const key of searchParams.keys()) if (!MCP_FAKES_PARAMS.has(key)) return reply(400, { error: `Unknown query parameter: '${key}'. Supported: ${[...MCP_FAKES_PARAMS].join(", ")}` });
|
|
663
|
+
const testId = searchParams.get("testId") || null;
|
|
664
|
+
const context = searchParams.get("context") || null;
|
|
665
|
+
const mount = searchParams.get("mount") || null;
|
|
666
|
+
const listed = listControlApiFakes(mounts, testId, context, mount);
|
|
667
|
+
if (listed === null) return reply(404, { error: require_message_text.build(require_message_text.msg`No MCP mount at ${require_message_text.quote(mount ?? "")}`) });
|
|
668
|
+
return reply(200, {
|
|
669
|
+
testId,
|
|
670
|
+
context,
|
|
671
|
+
mounts: listed
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
if (subPath === "/mcp/fakes/report" && req.method === "GET") return require_mcp_fakes_report.handleFakesReportRoute(res, searchParams, mounts);
|
|
477
675
|
if (subPath === "/fixtures" && req.method === "POST") {
|
|
478
676
|
let raw;
|
|
479
677
|
try {
|
|
@@ -495,29 +693,82 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
495
693
|
res.end(JSON.stringify({ error: `Invalid JSON: ${msg}` }));
|
|
496
694
|
return true;
|
|
497
695
|
}
|
|
498
|
-
|
|
696
|
+
const withFakes = typeof parsed === "object" && parsed !== null && Object.hasOwn(parsed, "mcpFakes");
|
|
697
|
+
if (parsed === null || !Array.isArray(parsed.fixtures) && !(withFakes && parsed.fixtures === void 0)) {
|
|
499
698
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
500
699
|
res.end(JSON.stringify({ error: "Missing or invalid \"fixtures\" array" }));
|
|
501
700
|
return true;
|
|
502
701
|
}
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
if (
|
|
702
|
+
const entries = Array.isArray(parsed.fixtures) ? parsed.fixtures : [];
|
|
703
|
+
const missingMatchIndex = entries.findIndex((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.match == null);
|
|
704
|
+
if (missingMatchIndex !== -1) {
|
|
705
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
706
|
+
res.end(JSON.stringify({ error: `Fixture at index ${missingMatchIndex} is missing match` }));
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
const invalidMatchIndex = entries.findIndex((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.match != null && (typeof entry.match !== "object" || Array.isArray(entry.match)));
|
|
710
|
+
if (invalidMatchIndex !== -1) {
|
|
711
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
712
|
+
res.end(JSON.stringify({ error: `Fixture at index ${invalidMatchIndex}: match must be an object` }));
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
const extendedResponsesTools = defaults.responsesTools === "extended";
|
|
716
|
+
const markInPlace = extendedResponsesTools && require_helpers.isServerOwnedFixtures(fixtures);
|
|
717
|
+
const addition = controlFixtureAdditions.get(defaults) ?? 0;
|
|
718
|
+
controlFixtureAdditions.set(defaults, addition + 1);
|
|
719
|
+
const converted = [];
|
|
720
|
+
const loadErrors = [];
|
|
721
|
+
for (const [index, entry] of entries.entries()) try {
|
|
722
|
+
const fixture = require_fixture_loader.entryToFixture(entry, void 0, void 0, {
|
|
723
|
+
file: `control-api#${addition}`,
|
|
724
|
+
index
|
|
725
|
+
});
|
|
726
|
+
if (defaults.misbehavior) require_fixture_loader.enableHeldFixtureMisbehavior(fixture);
|
|
727
|
+
if (markInPlace) require_fixture_loader.markFixtureResponsesToolsExtended(fixture);
|
|
728
|
+
converted.push(fixture);
|
|
729
|
+
} catch (error) {
|
|
730
|
+
if (!(error instanceof require_fixture_loader.FixtureLoadError || error instanceof require_fixture_loader.MisbehaviorConfigError)) throw error;
|
|
731
|
+
loadErrors.push(error);
|
|
732
|
+
}
|
|
733
|
+
const errors = (loadErrors.length === 0 ? require_fixture_loader.validateFixtures(extendedResponsesTools ? converted.map(require_fixture_loader.responsesToolsExtendedView) : converted) : []).filter((i) => i.severity === "error");
|
|
734
|
+
const fakes = withFakes ? planControlApiFakes(parsed.mcpFakes, mounts) : null;
|
|
735
|
+
const fakeErrors = fakes?.errors ?? [];
|
|
736
|
+
const validationFailed = (details) => {
|
|
506
737
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
507
738
|
res.end(JSON.stringify({
|
|
508
739
|
error: "Validation failed",
|
|
509
|
-
details
|
|
740
|
+
details
|
|
510
741
|
}));
|
|
511
742
|
return true;
|
|
743
|
+
};
|
|
744
|
+
if (loadErrors.length > 0 || errors.length > 0 || fakeErrors.length > 0) return validationFailed([
|
|
745
|
+
...loadErrors.map((e) => e.toJSON()),
|
|
746
|
+
...errors,
|
|
747
|
+
...fakeErrors.map((e) => e.toJSON())
|
|
748
|
+
]);
|
|
749
|
+
let mcpFakesAdded = 0;
|
|
750
|
+
if (fakes) try {
|
|
751
|
+
mcpFakesAdded = fakes.apply({
|
|
752
|
+
journal,
|
|
753
|
+
defaults
|
|
754
|
+
});
|
|
755
|
+
} catch (err) {
|
|
756
|
+
if (!(err instanceof require_mcp_fakes.McpFakesAddError)) throw err;
|
|
757
|
+
return validationFailed(err.errors.map((e) => e.toJSON()));
|
|
512
758
|
}
|
|
513
759
|
fixtures.push(...converted);
|
|
514
760
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
515
761
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
516
|
-
res.end(JSON.stringify(
|
|
762
|
+
res.end(JSON.stringify(withFakes ? {
|
|
763
|
+
added: converted.length,
|
|
764
|
+
mcpFakesAdded
|
|
765
|
+
} : { added: converted.length }));
|
|
517
766
|
return true;
|
|
518
767
|
}
|
|
519
768
|
if (subPath === "/fixtures" && req.method === "DELETE") {
|
|
520
769
|
require_fixture_loader.clearFixtureQueue(fixtures);
|
|
770
|
+
journal.clearMisbehaviorCounters();
|
|
771
|
+
for (const { handler } of mounts) handler.clearMcpFakes?.();
|
|
521
772
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
522
773
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
523
774
|
res.end(JSON.stringify({ cleared: true }));
|
|
@@ -530,7 +781,8 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
530
781
|
veoVideoJobs,
|
|
531
782
|
grokVideoJobs,
|
|
532
783
|
bytePlusVideoJobs,
|
|
533
|
-
defaults
|
|
784
|
+
defaults,
|
|
785
|
+
mounts
|
|
534
786
|
});
|
|
535
787
|
if (subPath === "/reset" && req.method === "POST") {
|
|
536
788
|
performFullReset(fixtures, resetTargets());
|
|
@@ -632,15 +884,16 @@ function clientAbortedMidBody(req, res, err) {
|
|
|
632
884
|
if (err instanceof require_helpers.RequestBodyTooLargeError) return false;
|
|
633
885
|
return res.destroyed && !req.complete;
|
|
634
886
|
}
|
|
635
|
-
async function handleCompletions(req, res, fixtures, journal, defaults, modelFallback, providerKey, openRouter = false) {
|
|
887
|
+
async function handleCompletions(req, res, fixtures, journal, defaults, getRequestEntry, modelFallback, providerKey, openRouter = false, beforeFixtureSelection, bytePlusPath = false) {
|
|
636
888
|
setCorsHeaders(res);
|
|
889
|
+
const wire = providerKey === "azure" ? "Azure OpenAI Chat Completions" : providerKey === "openrouter" ? "OpenRouter Chat Completions" : providerKey === "byteplus" || bytePlusPath ? "BytePlus ModelArk Chat Completions" : "OpenAI Chat Completions";
|
|
637
890
|
let raw;
|
|
638
891
|
try {
|
|
639
892
|
raw = await require_helpers.readBody(req);
|
|
640
893
|
} catch (err) {
|
|
641
894
|
const msg = err instanceof Error ? err.message : "Failed to read request body";
|
|
642
895
|
const clientFault = err instanceof require_helpers.RequestBodyTooLargeError;
|
|
643
|
-
const route = `${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`;
|
|
896
|
+
const route = `${req.method ?? "POST"} ${req.url ?? require_route_registry.COMPLETIONS_PATH}`;
|
|
644
897
|
const clientAbort = clientAbortedMidBody(req, res, err);
|
|
645
898
|
const bodyLimitDisconnect = clientFault && (req.destroyed || res.destroyed);
|
|
646
899
|
if (clientAbort || bodyLimitDisconnect) {
|
|
@@ -648,7 +901,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
648
901
|
defaults.logger.warn(bodyLimitDisconnect ? `${route}: ${msg}` : `${route}: client aborted mid-body (${msg})`);
|
|
649
902
|
journal.add({
|
|
650
903
|
method: req.method ?? "POST",
|
|
651
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
904
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
652
905
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
653
906
|
body: null,
|
|
654
907
|
response: {
|
|
@@ -665,7 +918,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
665
918
|
else defaults.logger.error(`${route}: ${msg}`);
|
|
666
919
|
journal.add({
|
|
667
920
|
method: req.method ?? "POST",
|
|
668
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
921
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
669
922
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
670
923
|
body: null,
|
|
671
924
|
response: {
|
|
@@ -686,7 +939,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
686
939
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown parse error";
|
|
687
940
|
journal.add({
|
|
688
941
|
method: req.method ?? "POST",
|
|
689
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
942
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
690
943
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
691
944
|
body: null,
|
|
692
945
|
response: {
|
|
@@ -705,7 +958,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
705
958
|
if (!require_helpers.isJsonObject(body)) {
|
|
706
959
|
journal.add({
|
|
707
960
|
method: req.method ?? "POST",
|
|
708
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
961
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
709
962
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
710
963
|
body: null,
|
|
711
964
|
response: {
|
|
@@ -723,7 +976,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
723
976
|
if (!Array.isArray(body.messages)) {
|
|
724
977
|
journal.add({
|
|
725
978
|
method: req.method ?? "POST",
|
|
726
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
979
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
727
980
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
728
981
|
body: null,
|
|
729
982
|
response: {
|
|
@@ -739,8 +992,53 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
739
992
|
} }));
|
|
740
993
|
return;
|
|
741
994
|
}
|
|
995
|
+
const nullMessageIndex = body.messages.findIndex((message) => message === null);
|
|
996
|
+
if (nullMessageIndex !== -1) {
|
|
997
|
+
const message = `Invalid request: messages[${nullMessageIndex}] must be an object`;
|
|
998
|
+
journal.add({
|
|
999
|
+
method: req.method ?? "POST",
|
|
1000
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1001
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1002
|
+
body: null,
|
|
1003
|
+
response: {
|
|
1004
|
+
status: 400,
|
|
1005
|
+
fixture: null
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, message) : JSON.stringify({ error: {
|
|
1009
|
+
message,
|
|
1010
|
+
type: "invalid_request_error",
|
|
1011
|
+
param: null,
|
|
1012
|
+
code: null
|
|
1013
|
+
} }));
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (new URL(req.url ?? require_route_registry.COMPLETIONS_PATH, "http://localhost").pathname === require_route_registry.COMPLETIONS_PATH) {
|
|
1017
|
+
let toolsError = require_helpers.validateToolsField(body.tools);
|
|
1018
|
+
if (!toolsError && Array.isArray(body.tools)) {
|
|
1019
|
+
const nullFunctionIndex = body.tools.findIndex((tool) => tool?.function === null);
|
|
1020
|
+
if (nullFunctionIndex !== -1) toolsError = `tools[${nullFunctionIndex}].function must not be null`;
|
|
1021
|
+
}
|
|
1022
|
+
if (toolsError) {
|
|
1023
|
+
journal.add({
|
|
1024
|
+
method: req.method ?? "POST",
|
|
1025
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1026
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1027
|
+
body: null,
|
|
1028
|
+
response: {
|
|
1029
|
+
status: 400,
|
|
1030
|
+
fixture: null
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
1034
|
+
message: toolsError,
|
|
1035
|
+
type: "invalid_request_error"
|
|
1036
|
+
} }));
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
742
1040
|
const method = req.method ?? "POST";
|
|
743
|
-
const path = req.url ?? COMPLETIONS_PATH;
|
|
1041
|
+
const path = req.url ?? require_route_registry.COMPLETIONS_PATH;
|
|
744
1042
|
const flatHeaders = require_helpers.flattenHeaders(req.headers);
|
|
745
1043
|
body._endpointType = "chat";
|
|
746
1044
|
body._context = require_helpers.getContext(req);
|
|
@@ -754,6 +1052,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
754
1052
|
const openRouterFallback = openRouter && Array.isArray(body.models) && body.models.length > 0;
|
|
755
1053
|
const selectFixture = (probe, counts) => {
|
|
756
1054
|
for (;;) {
|
|
1055
|
+
beforeFixtureSelection?.();
|
|
757
1056
|
const attempt = require_router.matchFixtureDiagnostic(fixtures, probe, counts, defaults.requestTransform, matchOptions);
|
|
758
1057
|
if (attempt.fixture && require_fixture_loader.isOneShotError(attempt.fixture) && !require_fixture_loader.claimOneShotError(fixtures, attempt.fixture)) continue;
|
|
759
1058
|
return attempt;
|
|
@@ -837,13 +1136,32 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
837
1136
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
838
1137
|
const missWouldProxy = require_helpers.wouldProxyMiss(effectiveStrict, defaults.record, providerKey);
|
|
839
1138
|
const noFixtureSource = missWouldProxy ? "proxy" : "internal";
|
|
1139
|
+
const recordBypass = (reason) => {
|
|
1140
|
+
const entry = getRequestEntry();
|
|
1141
|
+
if (!entry) return;
|
|
1142
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1143
|
+
entry,
|
|
1144
|
+
summary: require_misbehavior.resolveMisbehaviorShortCircuit({
|
|
1145
|
+
wire: "openai-chat",
|
|
1146
|
+
fixture: fixture ?? void 0,
|
|
1147
|
+
defaults,
|
|
1148
|
+
rawHeaders: req.headers,
|
|
1149
|
+
url: req.url,
|
|
1150
|
+
reason
|
|
1151
|
+
}),
|
|
1152
|
+
defaults,
|
|
1153
|
+
testId
|
|
1154
|
+
});
|
|
1155
|
+
};
|
|
840
1156
|
if (chaosAction === "drop" || chaosAction === "disconnect" || chaosAction === "rateLimit") {
|
|
841
1157
|
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, fixture ? "fixture" : noFixtureSource, defaults.registry, defaults.logger);
|
|
1158
|
+
recordBypass("chaos-fired");
|
|
842
1159
|
require_fixture_loader.releaseOneShotError(fixtures, fixture);
|
|
843
1160
|
return;
|
|
844
1161
|
}
|
|
845
1162
|
if (chaosAction === "malformed" && (fixture || !missWouldProxy)) {
|
|
846
1163
|
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, fixture ? "fixture" : "internal", defaults.registry, defaults.logger);
|
|
1164
|
+
recordBypass("chaos-fired");
|
|
847
1165
|
require_fixture_loader.releaseOneShotError(fixtures, fixture);
|
|
848
1166
|
return;
|
|
849
1167
|
}
|
|
@@ -851,10 +1169,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
851
1169
|
if (effectiveStrict) {
|
|
852
1170
|
const strictStatus = 503;
|
|
853
1171
|
const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
|
|
854
|
-
defaults.logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? COMPLETIONS_PATH, skippedBySequenceOrTurn));
|
|
1172
|
+
defaults.logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? require_route_registry.COMPLETIONS_PATH, skippedBySequenceOrTurn));
|
|
855
1173
|
journal.add({
|
|
856
1174
|
method: req.method ?? "POST",
|
|
857
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1175
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
858
1176
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
859
1177
|
body,
|
|
860
1178
|
response: {
|
|
@@ -875,6 +1193,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
875
1193
|
const hookOptions = chaosAction === "malformed" ? {
|
|
876
1194
|
beforeWriteResponse: () => {
|
|
877
1195
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, chaosContext, "proxy", defaults.registry, defaults.logger);
|
|
1196
|
+
recordBypass("chaos-fired");
|
|
878
1197
|
return true;
|
|
879
1198
|
},
|
|
880
1199
|
onHookBypassed: (reason) => {
|
|
@@ -894,7 +1213,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
894
1213
|
req.socket?.on("error", noteClientHangUp);
|
|
895
1214
|
let outcome;
|
|
896
1215
|
try {
|
|
897
|
-
outcome = await require_recorder.proxyAndRecord(req, res, body, providerKey, req.url ?? COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions);
|
|
1216
|
+
outcome = await require_recorder.proxyAndRecord(req, res, body, providerKey, req.url ?? require_route_registry.COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions);
|
|
898
1217
|
} finally {
|
|
899
1218
|
req.socket?.off("end", noteClientHangUp);
|
|
900
1219
|
req.socket?.off("error", noteClientHangUp);
|
|
@@ -904,7 +1223,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
904
1223
|
const destroyedMidStream = res.destroyed && !res.writableEnded;
|
|
905
1224
|
journal.add({
|
|
906
1225
|
method: req.method ?? "POST",
|
|
907
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1226
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
908
1227
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
909
1228
|
body,
|
|
910
1229
|
response: {
|
|
@@ -917,16 +1236,18 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
917
1236
|
} : {}
|
|
918
1237
|
}
|
|
919
1238
|
});
|
|
1239
|
+
recordBypass("proxied");
|
|
920
1240
|
return;
|
|
921
1241
|
}
|
|
922
1242
|
if (chaosAction === "malformed") {
|
|
923
1243
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, chaosContext, "internal", defaults.registry, defaults.logger);
|
|
1244
|
+
recordBypass("chaos-fired");
|
|
924
1245
|
return;
|
|
925
1246
|
}
|
|
926
1247
|
}
|
|
927
1248
|
journal.add({
|
|
928
1249
|
method: req.method ?? "POST",
|
|
929
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1250
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
930
1251
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
931
1252
|
body,
|
|
932
1253
|
response: {
|
|
@@ -943,13 +1264,71 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
943
1264
|
} }));
|
|
944
1265
|
return;
|
|
945
1266
|
}
|
|
946
|
-
const
|
|
1267
|
+
const resolvedResponse = preResolvedResponse ?? await require_helpers.resolveResponse(fixture, body);
|
|
1268
|
+
const evaluation = require_misbehavior.planMisbehavior({
|
|
1269
|
+
wire: "openai-chat",
|
|
1270
|
+
fixture,
|
|
1271
|
+
response: resolvedResponse,
|
|
1272
|
+
request: body,
|
|
1273
|
+
stream: body.stream === true,
|
|
1274
|
+
defaults,
|
|
1275
|
+
rawHeaders: req.headers,
|
|
1276
|
+
url: req.url
|
|
1277
|
+
});
|
|
1278
|
+
if (evaluation.kind === "error") {
|
|
1279
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1280
|
+
entry: journal.add({
|
|
1281
|
+
method,
|
|
1282
|
+
path,
|
|
1283
|
+
headers: flatHeaders,
|
|
1284
|
+
body,
|
|
1285
|
+
response: {
|
|
1286
|
+
status: evaluation.status,
|
|
1287
|
+
fixture
|
|
1288
|
+
}
|
|
1289
|
+
}),
|
|
1290
|
+
summary: evaluation.summary,
|
|
1291
|
+
defaults,
|
|
1292
|
+
testId
|
|
1293
|
+
});
|
|
1294
|
+
if (!evaluation.summary?.evaluations.some((row) => row.outcome === "error")) defaults.logger.error(`${evaluation.code}: ${evaluation.message}`);
|
|
1295
|
+
require_sse_writer.writeErrorResponse(res, evaluation.status, openRouter ? require_openrouter_chat.serializeOpenRouterError(evaluation.status, `${evaluation.code}: ${evaluation.message}`) : JSON.stringify({ error: {
|
|
1296
|
+
message: evaluation.message,
|
|
1297
|
+
type: "invalid_request_error",
|
|
1298
|
+
param: null,
|
|
1299
|
+
code: evaluation.code
|
|
1300
|
+
} }));
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
const appliedPlan = evaluation.kind === "applied" ? require_helpers.prepareOpenAIChatMisbehavior(evaluation) : void 0;
|
|
1304
|
+
const response = appliedPlan?.response ?? resolvedResponse;
|
|
1305
|
+
const summary = appliedPlan?.summary ?? evaluation.summary;
|
|
1306
|
+
const recordOutcome = (entry) => {
|
|
1307
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1308
|
+
entry,
|
|
1309
|
+
summary,
|
|
1310
|
+
defaults,
|
|
1311
|
+
testId
|
|
1312
|
+
});
|
|
1313
|
+
};
|
|
1314
|
+
const appliedReasoning = appliedPlan && appliedPlan.reasoning === void 0 && (require_helpers.isTextResponse(response) || require_helpers.isToolCallResponse(response) || require_helpers.isCombinedFixtureResponse(response)) ? require_helpers.resolveReasoningForModel(response.reasoning, responseModel, require_helpers.resolveStrictMode(defaults.strict, req.headers), defaults.logger) : void 0;
|
|
1315
|
+
const appliedUsage = appliedPlan ? require_helpers.resolveOpenAIChatMisbehaviorUsage(appliedPlan, body, appliedPlan.reasoning === void 0 ? appliedReasoning !== void 0 : openRouter) : void 0;
|
|
947
1316
|
const latency = fixture.latency ?? defaults.latency;
|
|
948
1317
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
949
1318
|
const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
|
|
950
1319
|
const streamingPromptText = body.messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("");
|
|
951
|
-
const resolveStreamingUsageTokens = (overrides, completionText) => require_helpers.resolveUsage(overrides, streamingPromptText, completionText);
|
|
1320
|
+
const resolveStreamingUsageTokens = (overrides, completionText) => appliedUsage ?? require_helpers.resolveUsage(overrides, streamingPromptText, completionText);
|
|
1321
|
+
const resolveRecordedTimings = (chunks) => {
|
|
1322
|
+
const timings = fixture.recordedTimings;
|
|
1323
|
+
if (!appliedPlan?.duplicateId || !timings) return timings;
|
|
1324
|
+
const gaps = timings.interChunkDelaysMs;
|
|
1325
|
+
return {
|
|
1326
|
+
...timings,
|
|
1327
|
+
interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, chunks.length - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
|
|
1328
|
+
};
|
|
1329
|
+
};
|
|
952
1330
|
const shapeORCompletion = (completion, overrides) => {
|
|
1331
|
+
if (appliedUsage) completion.usage = appliedUsage;
|
|
953
1332
|
if (!openRouter) return completion;
|
|
954
1333
|
return require_openrouter_chat.shapeOpenRouterCompletion(completion, require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel), overrides?.id !== void 0);
|
|
955
1334
|
};
|
|
@@ -962,46 +1341,86 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
962
1341
|
const openRouterProcessing = !!(openRouter && fixture.openRouterProcessing);
|
|
963
1342
|
if (require_helpers.isErrorResponse(response)) {
|
|
964
1343
|
const status = response.status ?? 500;
|
|
965
|
-
journal.add({
|
|
1344
|
+
recordOutcome(journal.add({
|
|
966
1345
|
method: req.method ?? "POST",
|
|
967
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1346
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
968
1347
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
969
1348
|
body,
|
|
970
1349
|
response: {
|
|
971
1350
|
status,
|
|
972
1351
|
fixture
|
|
973
1352
|
}
|
|
974
|
-
});
|
|
1353
|
+
}));
|
|
975
1354
|
require_sse_writer.writeErrorResponse(res, status, openRouter ? require_openrouter_chat.serializeOpenRouterError(status, response.error.message, response.error.metadata) : require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
|
|
976
1355
|
return;
|
|
977
1356
|
}
|
|
978
1357
|
if (require_helpers.isAudioResponse(response)) {
|
|
979
|
-
journal.add({
|
|
1358
|
+
recordOutcome(journal.add({
|
|
980
1359
|
method: req.method ?? "POST",
|
|
981
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1360
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
982
1361
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
983
1362
|
body,
|
|
984
1363
|
response: {
|
|
985
1364
|
status: 422,
|
|
986
1365
|
fixture
|
|
987
1366
|
}
|
|
988
|
-
});
|
|
1367
|
+
}));
|
|
989
1368
|
require_sse_writer.writeErrorResponse(res, 422, openRouter ? require_openrouter_chat.serializeOpenRouterError(422, "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.") : JSON.stringify({ error: {
|
|
990
1369
|
message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
|
|
991
1370
|
type: "invalid_request_error"
|
|
992
1371
|
} }));
|
|
993
1372
|
return;
|
|
994
1373
|
}
|
|
995
|
-
if (require_helpers.
|
|
1374
|
+
if (appliedPlan && require_helpers.isTextResponse(response) && (appliedPlan.stop === "refusal" || appliedPlan.stop === "content_filter" || appliedPlan.reasoning !== void 0)) {
|
|
1375
|
+
const overrides = require_helpers.extractOverrides(response);
|
|
1376
|
+
const entry = journal.add({
|
|
1377
|
+
method,
|
|
1378
|
+
path,
|
|
1379
|
+
headers: flatHeaders,
|
|
1380
|
+
body,
|
|
1381
|
+
response: {
|
|
1382
|
+
status: 200,
|
|
1383
|
+
fixture
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
recordOutcome(entry);
|
|
1387
|
+
if (body.stream !== true) {
|
|
1388
|
+
const completion = appliedPlan.stop === "refusal" ? require_helpers.buildOpenAIRefusalCompletion(appliedPlan.refusal ?? "", responseModel, overrides, body.messages) : appliedPlan.stop === "content_filter" ? require_helpers.buildOpenAIContentFilterCompletion(responseModel, overrides, body.messages) : require_helpers.buildOpenAIReasoningCompletion(appliedPlan.reasoning ?? "", responseModel, openRouter, overrides, body.messages);
|
|
1389
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1390
|
+
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1391
|
+
} else {
|
|
1392
|
+
const chunks = appliedPlan.stop === "refusal" ? require_helpers.buildOpenAIRefusalChunks(appliedPlan.refusal ?? "", responseModel, chunkSize, overrides) : appliedPlan.stop === "content_filter" ? require_helpers.buildOpenAIContentFilterChunks(responseModel, overrides) : require_helpers.buildOpenAIReasoningChunks(appliedPlan.reasoning ?? "", responseModel, chunkSize, openRouter, overrides);
|
|
1393
|
+
const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0].id, overrides?.model ?? responseModel, chunks[0].created, resolveStreamingUsageTokens(overrides, ""), overrides?.systemFingerprint) : void 0;
|
|
1394
|
+
shapeORChunks(chunks, usageChunk, overrides);
|
|
1395
|
+
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
1396
|
+
if (!await require_sse_writer.writeSSEStream(res, chunks, {
|
|
1397
|
+
latency,
|
|
1398
|
+
streamingProfile: fixture.streamingProfile,
|
|
1399
|
+
signal: interruption?.signal,
|
|
1400
|
+
onChunkSent: interruption?.tick,
|
|
1401
|
+
usageChunk,
|
|
1402
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1403
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1404
|
+
openRouterProcessing
|
|
1405
|
+
})) {
|
|
1406
|
+
if (!res.writableEnded) res.destroy();
|
|
1407
|
+
entry.response.interrupted = true;
|
|
1408
|
+
entry.response.interruptReason = interruption?.reason();
|
|
1409
|
+
}
|
|
1410
|
+
interruption?.cleanup();
|
|
1411
|
+
}
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
if (require_helpers.isCombinedFixtureResponse(response)) {
|
|
996
1415
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
997
1416
|
const overrides = require_helpers.extractOverrides(response);
|
|
998
1417
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
999
|
-
const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1418
|
+
const effReasoning = appliedPlan ? appliedReasoning : require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1000
1419
|
const streaming = body.stream === true;
|
|
1001
1420
|
const blockOutcome = response.blocks && response.blocks.length > 0 ? require_helpers.resolveFixtureBlockOutcome(response.blocks) : void 0;
|
|
1002
1421
|
const journalEntry = journal.add({
|
|
1003
1422
|
method: req.method ?? "POST",
|
|
1004
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1423
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1005
1424
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
1006
1425
|
body,
|
|
1007
1426
|
response: {
|
|
@@ -1009,16 +1428,18 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1009
1428
|
fixture
|
|
1010
1429
|
}
|
|
1011
1430
|
});
|
|
1431
|
+
recordOutcome(journalEntry);
|
|
1432
|
+
const toolCalls = require_helpers.requireEmittedFunctionToolCalls(response, wire);
|
|
1012
1433
|
if (!streaming) {
|
|
1013
|
-
const completion = require_helpers.buildContentWithToolCallsCompletion(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ??
|
|
1434
|
+
const completion = require_helpers.buildContentWithToolCallsCompletion(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ?? toolCalls, responseModel, effReasoning, blockOutcome ? {
|
|
1014
1435
|
...overrides,
|
|
1015
1436
|
finishReason: overrides?.finishReason ?? (blockOutcome.hasToolCalls ? "tool_calls" : "stop")
|
|
1016
1437
|
} : overrides, body.messages);
|
|
1017
1438
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1018
1439
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1019
1440
|
} else {
|
|
1020
|
-
const chunks = require_helpers.buildContentWithToolCallsChunks(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ??
|
|
1021
|
-
const completionText = blockOutcome ? blockOutcome.content + blockOutcome.toolCalls.map((tc) => tc.name + tc.arguments).join("") : (response.content ?? "") +
|
|
1441
|
+
const chunks = require_helpers.buildContentWithToolCallsChunks(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ?? toolCalls, responseModel, chunkSize, effReasoning, overrides, blockOutcome?.ordered);
|
|
1442
|
+
const completionText = blockOutcome ? blockOutcome.content + blockOutcome.toolCalls.map((tc) => tc.name + tc.arguments).join("") : (response.content ?? "") + toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1022
1443
|
const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
|
|
1023
1444
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1024
1445
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
@@ -1028,7 +1449,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1028
1449
|
signal: interruption?.signal,
|
|
1029
1450
|
onChunkSent: interruption?.tick,
|
|
1030
1451
|
usageChunk,
|
|
1031
|
-
recordedTimings:
|
|
1452
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1032
1453
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1033
1454
|
openRouterProcessing
|
|
1034
1455
|
})) {
|
|
@@ -1044,10 +1465,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1044
1465
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1045
1466
|
const overrides = require_helpers.extractOverrides(response);
|
|
1046
1467
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1047
|
-
const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1468
|
+
const effReasoning = appliedPlan ? appliedReasoning : require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1048
1469
|
const journalEntry = journal.add({
|
|
1049
1470
|
method: req.method ?? "POST",
|
|
1050
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1471
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1051
1472
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
1052
1473
|
body,
|
|
1053
1474
|
response: {
|
|
@@ -1055,6 +1476,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1055
1476
|
fixture
|
|
1056
1477
|
}
|
|
1057
1478
|
});
|
|
1479
|
+
recordOutcome(journalEntry);
|
|
1058
1480
|
if (body.stream !== true) {
|
|
1059
1481
|
const completion = require_helpers.buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
|
|
1060
1482
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -1070,7 +1492,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1070
1492
|
signal: interruption?.signal,
|
|
1071
1493
|
onChunkSent: interruption?.tick,
|
|
1072
1494
|
usageChunk,
|
|
1073
|
-
recordedTimings:
|
|
1495
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1074
1496
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1075
1497
|
openRouterProcessing
|
|
1076
1498
|
})) {
|
|
@@ -1086,10 +1508,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1086
1508
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1087
1509
|
const overrides = require_helpers.extractOverrides(response);
|
|
1088
1510
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1089
|
-
const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1511
|
+
const effReasoning = appliedPlan ? appliedReasoning : require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
|
|
1090
1512
|
const journalEntry = journal.add({
|
|
1091
1513
|
method: req.method ?? "POST",
|
|
1092
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1514
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1093
1515
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
1094
1516
|
body,
|
|
1095
1517
|
response: {
|
|
@@ -1097,13 +1519,15 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1097
1519
|
fixture
|
|
1098
1520
|
}
|
|
1099
1521
|
});
|
|
1522
|
+
recordOutcome(journalEntry);
|
|
1523
|
+
const toolCalls = require_helpers.requireServedFunctionToolCalls(response, wire);
|
|
1100
1524
|
if (body.stream !== true) {
|
|
1101
|
-
const completion = require_helpers.buildToolCallCompletion(
|
|
1525
|
+
const completion = require_helpers.buildToolCallCompletion(toolCalls, responseModel, effReasoning, overrides, body.messages);
|
|
1102
1526
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1103
1527
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1104
1528
|
} else {
|
|
1105
|
-
const chunks = require_helpers.buildToolCallChunks(
|
|
1106
|
-
const completionText =
|
|
1529
|
+
const chunks = require_helpers.buildToolCallChunks(toolCalls, responseModel, chunkSize, effReasoning, overrides);
|
|
1530
|
+
const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1107
1531
|
const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
|
|
1108
1532
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1109
1533
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
@@ -1113,7 +1537,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1113
1537
|
signal: interruption?.signal,
|
|
1114
1538
|
onChunkSent: interruption?.tick,
|
|
1115
1539
|
usageChunk,
|
|
1116
|
-
recordedTimings:
|
|
1540
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1117
1541
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1118
1542
|
openRouterProcessing
|
|
1119
1543
|
})) {
|
|
@@ -1125,16 +1549,16 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1125
1549
|
}
|
|
1126
1550
|
return;
|
|
1127
1551
|
}
|
|
1128
|
-
journal.add({
|
|
1552
|
+
recordOutcome(journal.add({
|
|
1129
1553
|
method: req.method ?? "POST",
|
|
1130
|
-
path: req.url ?? COMPLETIONS_PATH,
|
|
1554
|
+
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1131
1555
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
1132
1556
|
body,
|
|
1133
1557
|
response: {
|
|
1134
1558
|
status: 500,
|
|
1135
1559
|
fixture
|
|
1136
1560
|
}
|
|
1137
|
-
});
|
|
1561
|
+
}));
|
|
1138
1562
|
require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, "Fixture response did not match any known type") : JSON.stringify({ error: {
|
|
1139
1563
|
message: "Fixture response did not match any known type",
|
|
1140
1564
|
type: "server_error"
|
|
@@ -1148,18 +1572,101 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1148
1572
|
}
|
|
1149
1573
|
/** @internal Config startup uses this to avoid parsing a selected auth source twice. */
|
|
1150
1574
|
async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mounts, serviceFixtures) {
|
|
1575
|
+
const logger = new require_logger.Logger(options?.logLevel ?? "silent");
|
|
1576
|
+
const mountList = mounts ?? [];
|
|
1577
|
+
const handOff = serviceFixtures?.mcpFakes && serviceFixtures.mcpFakes.length > 0 ? require_mcp_fakes_mount.handOffMcpFakes(mountList, serviceFixtures.mcpFakes, logger) : null;
|
|
1578
|
+
try {
|
|
1579
|
+
return await startServer(fixtures, options, resolvedAuth, mountList, logger, serviceFixtures, () => handOff?.commit());
|
|
1580
|
+
} catch (err) {
|
|
1581
|
+
handOff?.undo();
|
|
1582
|
+
throw err;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* The journal as JSON. An entry names the caller's fixture; the JSON shows
|
|
1587
|
+
* the fixture the request was served from (`Journal.servedFixture`): an
|
|
1588
|
+
* extended server's view, with the held keys applied, or LLMock's
|
|
1589
|
+
* per-addition copy with misbehavior enabled. The journal therefore reads as
|
|
1590
|
+
* it did when the entries held those objects.
|
|
1591
|
+
*/
|
|
1592
|
+
function journalJson(entries, journal) {
|
|
1593
|
+
if (!entries.some((entry) => journal.servedFixture(entry) !== entry.response.fixture)) return JSON.stringify(entries);
|
|
1594
|
+
return JSON.stringify(entries.map((entry) => {
|
|
1595
|
+
const fixture = entry.response.fixture;
|
|
1596
|
+
let served = journal.servedFixture(entry);
|
|
1597
|
+
if (served === fixture || !served) return entry;
|
|
1598
|
+
const viewSource = require_fixture_loader.responsesToolsViewSource(served);
|
|
1599
|
+
if (viewSource !== served) served = require_fixture_loader.responsesToolsExtendedView(viewSource);
|
|
1600
|
+
return {
|
|
1601
|
+
...entry,
|
|
1602
|
+
response: {
|
|
1603
|
+
...entry.response,
|
|
1604
|
+
fixture: served
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
}));
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* The server of `createServerWithResolvedAuth`, after the MCP fakes hand-off.
|
|
1611
|
+
* `commitHandOff` runs once the server listens; if it throws, the server is
|
|
1612
|
+
* closed and start rejects.
|
|
1613
|
+
*/
|
|
1614
|
+
async function startServer(fixtures, options, resolvedAuth, mountList, logger, serviceFixtures, commitHandOff) {
|
|
1615
|
+
const callerFixtures = fixtures;
|
|
1616
|
+
let rawFixtureAddition = 0;
|
|
1617
|
+
function ensureRawFixturePositions() {
|
|
1618
|
+
const existingPositions = new Set(callerFixtures.map(require_misbehavior.getFixtureMisbehaviorPosition).filter((position) => position !== void 0));
|
|
1619
|
+
for (const fixture of callerFixtures) {
|
|
1620
|
+
if (require_misbehavior.getFixtureMisbehaviorPosition(fixture) !== void 0) continue;
|
|
1621
|
+
let position;
|
|
1622
|
+
do
|
|
1623
|
+
position = `createServer#${rawFixtureAddition++}`;
|
|
1624
|
+
while (existingPositions.has(position));
|
|
1625
|
+
require_misbehavior.setFixtureMisbehaviorPosition(fixture, position);
|
|
1626
|
+
existingPositions.add(position);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
ensureRawFixturePositions();
|
|
1151
1630
|
const host = options?.host ?? "127.0.0.1";
|
|
1152
1631
|
const port = options?.port ?? 0;
|
|
1153
|
-
const logger = new require_logger.Logger(options?.logLevel ?? "silent");
|
|
1154
1632
|
const registry = options?.metrics ? require_metrics.createMetricsRegistry() : void 0;
|
|
1155
1633
|
const serverOptions = options ?? {};
|
|
1634
|
+
const validResponsesTools = (mode) => mode === "legacy" || mode === "extended";
|
|
1635
|
+
if (serverOptions.responsesTools !== void 0 && !validResponsesTools(serverOptions.responsesTools)) new require_logger.Logger("warn").warn(`Ignoring responsesTools because it must be "legacy" or "extended", got ${JSON.stringify(serverOptions.responsesTools)}. Using "legacy".`);
|
|
1636
|
+
fixtures = require_helpers.createServerFixtureList(callerFixtures, () => serverOptions.responsesTools, require_fixture_loader.responsesToolsExtendedView);
|
|
1156
1637
|
const chaosByTestId = /* @__PURE__ */ new Map();
|
|
1638
|
+
let misbehaviorBaseline;
|
|
1639
|
+
const misbehavior = {
|
|
1640
|
+
byTestId: /* @__PURE__ */ new Map(),
|
|
1641
|
+
get baseline() {
|
|
1642
|
+
return this.byTestId.get(require_constants.DEFAULT_TEST_ID) ?? misbehaviorBaseline;
|
|
1643
|
+
},
|
|
1644
|
+
set baseline(config) {
|
|
1645
|
+
misbehaviorBaseline = config;
|
|
1646
|
+
if (config === void 0) this.byTestId.delete(require_constants.DEFAULT_TEST_ID);
|
|
1647
|
+
else if (this.byTestId.has(require_constants.DEFAULT_TEST_ID)) this.byTestId.set(require_constants.DEFAULT_TEST_ID, config);
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
const misbehaviorEnabled = serverOptions.enableMisbehavior === true;
|
|
1651
|
+
if (misbehaviorEnabled) callerFixtures.forEach(require_fixture_loader.enableHeldFixtureMisbehavior);
|
|
1652
|
+
if (misbehaviorEnabled && serverOptions.misbehavior !== void 0) {
|
|
1653
|
+
const parsed = require_misbehavior.parseMisbehavior(serverOptions.misbehavior);
|
|
1654
|
+
if (!parsed.ok) throw new TypeError(`${parsed.issue.rule}: ${parsed.issue.message}`);
|
|
1655
|
+
misbehavior.baseline = parsed.config.seed === "random" ? {
|
|
1656
|
+
...parsed.config,
|
|
1657
|
+
seed: require_misbehavior.resolveMisbehaviorSeed(parsed.config.seed, logger)
|
|
1658
|
+
} : parsed.config;
|
|
1659
|
+
}
|
|
1157
1660
|
const defaults = {
|
|
1158
1661
|
latency: serverOptions.latency ?? 0,
|
|
1159
1662
|
chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),
|
|
1160
1663
|
replaySpeed: serverOptions.replaySpeed ?? 1,
|
|
1161
1664
|
logger,
|
|
1162
1665
|
chaosByTestId,
|
|
1666
|
+
misbehavior: misbehaviorEnabled ? misbehavior : void 0,
|
|
1667
|
+
get misbehaviorCounters() {
|
|
1668
|
+
return journal;
|
|
1669
|
+
},
|
|
1163
1670
|
get chaos() {
|
|
1164
1671
|
return {
|
|
1165
1672
|
base: chaosByTestId.get(require_constants.DEFAULT_TEST_ID) ?? serverOptions.chaos,
|
|
@@ -1182,6 +1689,13 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1182
1689
|
get strict() {
|
|
1183
1690
|
return serverOptions.strict;
|
|
1184
1691
|
},
|
|
1692
|
+
get strictToolArguments() {
|
|
1693
|
+
return serverOptions.strictToolArguments;
|
|
1694
|
+
},
|
|
1695
|
+
get responsesTools() {
|
|
1696
|
+
const mode = serverOptions.responsesTools;
|
|
1697
|
+
return validResponsesTools(mode) ? mode : void 0;
|
|
1698
|
+
},
|
|
1185
1699
|
get requestTransform() {
|
|
1186
1700
|
return serverOptions.requestTransform;
|
|
1187
1701
|
},
|
|
@@ -1272,6 +1786,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1272
1786
|
if (req) ownJournalEntry.set(req, entry);
|
|
1273
1787
|
}
|
|
1274
1788
|
});
|
|
1789
|
+
if (serverOptions.responsesTools === "extended") journal.configureFixtureViewSource(require_fixture_loader.responsesToolsViewSource);
|
|
1275
1790
|
const videoStates = new require_video.VideoStateMap();
|
|
1276
1791
|
const openRouterVideoJobs = new require_openrouter_video.OpenRouterVideoJobMap();
|
|
1277
1792
|
const veoVideoJobs = new require_veo_video.VeoVideoJobMap();
|
|
@@ -1286,6 +1801,85 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1286
1801
|
return status >= 400 && status < 500 ? "invalid_request_error" : "server_error";
|
|
1287
1802
|
}
|
|
1288
1803
|
/**
|
|
1804
|
+
* Envelope for a fixture tool-call error (see isFixtureToolCallError: invalid
|
|
1805
|
+
* JSON arguments on an object wire, a custom tool call on a non-Responses
|
|
1806
|
+
* wire, or a malformed fixture tool call). It is the provider envelope aimock
|
|
1807
|
+
* emits for that route, with the aimock `code` in it: Google's
|
|
1808
|
+
* `google.rpc.ErrorInfo` detail on Gemini and Vertex AI, a string
|
|
1809
|
+
* `error.code` on Gemini Interactions and Anthropic Messages, a `reason`
|
|
1810
|
+
* member on Bedrock (a `code` member would replace `__type` as the AWS SDK's
|
|
1811
|
+
* error name), and a sibling `code` on Ollama's bare string error. Every
|
|
1812
|
+
* other route, Cohere included, gets the OpenAI-style
|
|
1813
|
+
* `{error: {message, type, code}}`. OpenRouter routes pass their own
|
|
1814
|
+
* `toolCallEnvelope` instead of using this one.
|
|
1815
|
+
*/
|
|
1816
|
+
function invalidToolArgumentsEnvelope(pathname, message, code) {
|
|
1817
|
+
if (pathname === require_route_registry.OLLAMA_CHAT_PATH) return JSON.stringify({
|
|
1818
|
+
error: message,
|
|
1819
|
+
code
|
|
1820
|
+
});
|
|
1821
|
+
if (pathname === require_route_registry.GEMINI_INTERACTIONS_PATH) return JSON.stringify({ error: {
|
|
1822
|
+
code,
|
|
1823
|
+
message
|
|
1824
|
+
} });
|
|
1825
|
+
if (require_route_registry.GEMINI_PATH_RE.test(pathname) || require_route_registry.VERTEX_AI_RE.test(pathname)) return JSON.stringify({ error: {
|
|
1826
|
+
code: 500,
|
|
1827
|
+
message,
|
|
1828
|
+
status: "INTERNAL",
|
|
1829
|
+
details: require_helpers.googleFixtureToolCallErrorDetails(code)
|
|
1830
|
+
} });
|
|
1831
|
+
if (pathname === require_route_registry.MESSAGES_PATH) return JSON.stringify({
|
|
1832
|
+
type: "error",
|
|
1833
|
+
error: {
|
|
1834
|
+
type: "api_error",
|
|
1835
|
+
code,
|
|
1836
|
+
message
|
|
1837
|
+
}
|
|
1838
|
+
});
|
|
1839
|
+
if (require_route_registry.BEDROCK_INVOKE_RE.test(pathname) || require_route_registry.BEDROCK_STREAM_RE.test(pathname) || require_route_registry.BEDROCK_CONVERSE_RE.test(pathname) || require_route_registry.BEDROCK_CONVERSE_STREAM_RE.test(pathname)) return JSON.stringify({
|
|
1840
|
+
__type: "InternalServerException",
|
|
1841
|
+
message,
|
|
1842
|
+
reason: code
|
|
1843
|
+
});
|
|
1844
|
+
return JSON.stringify({ error: {
|
|
1845
|
+
message,
|
|
1846
|
+
type: "server_error",
|
|
1847
|
+
code
|
|
1848
|
+
} });
|
|
1849
|
+
}
|
|
1850
|
+
function invalidMisbehaviorHeaderEnvelope(pathname, detail) {
|
|
1851
|
+
const code = "aimock_misbehavior_invalid";
|
|
1852
|
+
const message = `${code}: ${detail}`;
|
|
1853
|
+
if (pathname === require_route_registry.OLLAMA_CHAT_PATH || pathname === require_route_registry.OLLAMA_GENERATE_PATH) return JSON.stringify({ error: message });
|
|
1854
|
+
if (pathname === require_route_registry.COHERE_CHAT_PATH) return JSON.stringify({ message });
|
|
1855
|
+
if (pathname === require_route_registry.GEMINI_INTERACTIONS_PATH) return JSON.stringify({ error: {
|
|
1856
|
+
code,
|
|
1857
|
+
message
|
|
1858
|
+
} });
|
|
1859
|
+
if (require_route_registry.GEMINI_PATH_RE.test(pathname) || require_route_registry.VERTEX_AI_RE.test(pathname)) return JSON.stringify({ error: {
|
|
1860
|
+
code: 400,
|
|
1861
|
+
message,
|
|
1862
|
+
status: "INVALID_ARGUMENT"
|
|
1863
|
+
} });
|
|
1864
|
+
if (pathname === require_route_registry.MESSAGES_PATH) return JSON.stringify({
|
|
1865
|
+
type: "error",
|
|
1866
|
+
error: {
|
|
1867
|
+
type: "invalid_request_error",
|
|
1868
|
+
code,
|
|
1869
|
+
message
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1872
|
+
if (require_route_registry.BEDROCK_INVOKE_RE.test(pathname) || require_route_registry.BEDROCK_STREAM_RE.test(pathname) || require_route_registry.BEDROCK_CONVERSE_RE.test(pathname) || require_route_registry.BEDROCK_CONVERSE_STREAM_RE.test(pathname)) return JSON.stringify({
|
|
1873
|
+
__type: "ValidationException",
|
|
1874
|
+
message
|
|
1875
|
+
});
|
|
1876
|
+
return JSON.stringify({ error: {
|
|
1877
|
+
message,
|
|
1878
|
+
type: "invalid_request_error",
|
|
1879
|
+
code
|
|
1880
|
+
} });
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1289
1883
|
* The newest journal entry this request already produced, or null.
|
|
1290
1884
|
*
|
|
1291
1885
|
* Attribution is by the identity of the `IncomingMessage` the entry was
|
|
@@ -1336,6 +1930,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1336
1930
|
function routeError(req, res, err, pathname, opts) {
|
|
1337
1931
|
const route = `${req.method ?? "?"} ${pathname}`;
|
|
1338
1932
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
1933
|
+
const invalidToolArguments = require_helpers.isFixtureToolCallError(err);
|
|
1339
1934
|
const clientFault = err instanceof require_helpers.RequestBodyTooLargeError;
|
|
1340
1935
|
const clientAbort = clientAbortedMidBody(req, res, err);
|
|
1341
1936
|
const status = clientFault ? 400 : 500;
|
|
@@ -1376,8 +1971,10 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1376
1971
|
return;
|
|
1377
1972
|
}
|
|
1378
1973
|
if (!res.headersSent) {
|
|
1379
|
-
if (existing)
|
|
1380
|
-
|
|
1974
|
+
if (existing) {
|
|
1975
|
+
existing.response.status = status;
|
|
1976
|
+
if (invalidToolArguments) existing.response.error = msg;
|
|
1977
|
+
} else try {
|
|
1381
1978
|
journal.add({
|
|
1382
1979
|
method: req.method ?? "?",
|
|
1383
1980
|
path: req.url ?? "?",
|
|
@@ -1387,14 +1984,15 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1387
1984
|
response: {
|
|
1388
1985
|
status,
|
|
1389
1986
|
fixture: null,
|
|
1390
|
-
source: "internal"
|
|
1987
|
+
source: "internal",
|
|
1988
|
+
...invalidToolArguments ? { error: msg } : {}
|
|
1391
1989
|
}
|
|
1392
1990
|
});
|
|
1393
1991
|
} catch (jErr) {
|
|
1394
1992
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
1395
1993
|
}
|
|
1396
1994
|
setCorsHeaders(res);
|
|
1397
|
-
require_sse_writer.writeErrorResponse(res, status, opts?.envelope ? opts.envelope(msg, status) : JSON.stringify({ error: {
|
|
1995
|
+
require_sse_writer.writeErrorResponse(res, status, invalidToolArguments ? (opts?.toolCallEnvelope ?? ((m, c) => invalidToolArgumentsEnvelope(pathname, m, c)))(msg, require_helpers.fixtureToolCallErrorCode(err)) : opts?.envelope ? opts.envelope(msg, status) : JSON.stringify({ error: {
|
|
1398
1996
|
message: msg,
|
|
1399
1997
|
type: errorTypeForStatus(status)
|
|
1400
1998
|
} }));
|
|
@@ -1439,16 +2037,21 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1439
2037
|
else res.destroy();
|
|
1440
2038
|
}
|
|
1441
2039
|
}
|
|
1442
|
-
|
|
2040
|
+
for (const { handler } of mountList) {
|
|
1443
2041
|
if (handler.setJournal) handler.setJournal(journal);
|
|
1444
2042
|
if (registry && handler.setRegistry) handler.setRegistry(registry);
|
|
2043
|
+
if (handler.setLogger) handler.setLogger(logger);
|
|
2044
|
+
if (handler instanceof require_mcp_mock.MCPMock) handler.setReplaySpeed(defaults.replaySpeed);
|
|
1445
2045
|
}
|
|
1446
2046
|
if (registry) registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
1447
2047
|
const server = node_http.createServer((req, res) => {
|
|
1448
2048
|
requestScope.run(req, () => {
|
|
1449
|
-
|
|
2049
|
+
require_helpers.runWithToolArgumentsScope({
|
|
2050
|
+
strict: defaults.strictToolArguments === true,
|
|
2051
|
+
logger
|
|
2052
|
+
}, () => handleHttpRequest(req, res).catch((err) => {
|
|
1450
2053
|
routeError(req, res, err, req.url ?? "?");
|
|
1451
|
-
});
|
|
2054
|
+
}));
|
|
1452
2055
|
});
|
|
1453
2056
|
});
|
|
1454
2057
|
async function handleHttpRequest(req, res) {
|
|
@@ -1464,7 +2067,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1464
2067
|
if (recorded) return;
|
|
1465
2068
|
recorded = true;
|
|
1466
2069
|
try {
|
|
1467
|
-
const normalizedPath = require_metrics.normalizePathLabel(pathname,
|
|
2070
|
+
const normalizedPath = require_metrics.normalizePathLabel(pathname, mountList.map((m) => m.path));
|
|
1468
2071
|
const method = req.method ?? "UNKNOWN";
|
|
1469
2072
|
registry.incrementCounter("aimock_requests_total", {
|
|
1470
2073
|
method,
|
|
@@ -1485,7 +2088,36 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1485
2088
|
if (!res.writableFinished) recordOutcome(require_metrics.DESTROYED_STATUS_LABEL);
|
|
1486
2089
|
});
|
|
1487
2090
|
}
|
|
1488
|
-
|
|
2091
|
+
let parsedUrl;
|
|
2092
|
+
try {
|
|
2093
|
+
parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
const route = `${req.method ?? "?"} ${req.url ?? "?"}`;
|
|
2096
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
2097
|
+
logger.error(`${route}: ${msg}`);
|
|
2098
|
+
if (err instanceof Error && err.stack) logger.debug(err.stack);
|
|
2099
|
+
try {
|
|
2100
|
+
journal.add({
|
|
2101
|
+
method: req.method ?? "?",
|
|
2102
|
+
path: req.url ?? "?",
|
|
2103
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
2104
|
+
body: null,
|
|
2105
|
+
response: {
|
|
2106
|
+
status: 400,
|
|
2107
|
+
fixture: null,
|
|
2108
|
+
source: "internal"
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
} catch (jErr) {
|
|
2112
|
+
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
2113
|
+
}
|
|
2114
|
+
setCorsHeaders(res);
|
|
2115
|
+
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
2116
|
+
message: "Invalid request target",
|
|
2117
|
+
type: "invalid_request_error"
|
|
2118
|
+
} }));
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
1489
2121
|
pathname = parsedUrl.pathname;
|
|
1490
2122
|
const originalPathname = pathname;
|
|
1491
2123
|
const isOpenRouter = require_openrouter_chat.isOpenRouterPath(originalPathname);
|
|
@@ -1494,48 +2126,74 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1494
2126
|
handleOptions(res);
|
|
1495
2127
|
return;
|
|
1496
2128
|
}
|
|
1497
|
-
if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname ===
|
|
2129
|
+
if (!(req.method === "GET" && (pathname === require_route_registry.HEALTH_PATH || pathname === require_route_registry.READY_PATH || pathname === require_route_registry.METRICS_PATH))) {
|
|
1498
2130
|
if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
1499
2131
|
require_api_key_auth.writeApiKeyHttpRejection(res, setCorsHeaders);
|
|
1500
2132
|
return;
|
|
1501
2133
|
}
|
|
1502
2134
|
require_api_key_auth.markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
1503
2135
|
}
|
|
1504
|
-
if (pathname.startsWith(CONTROL_PREFIX)) {
|
|
1505
|
-
|
|
2136
|
+
if (pathname.startsWith(require_route_registry.CONTROL_PREFIX)) {
|
|
2137
|
+
ensureRawFixturePositions();
|
|
2138
|
+
await handleControlAPI(req, res, pathname, parsedUrl.searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults, mountList);
|
|
1506
2139
|
return;
|
|
1507
2140
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
}
|
|
2141
|
+
const misbehaviorHeader = defaults.misbehavior ? require_misbehavior.parseMisbehaviorHeader(req.headers["x-aimock-misbehavior"]) : void 0;
|
|
2142
|
+
if (misbehaviorHeader && !misbehaviorHeader.ok) {
|
|
2143
|
+
const detail = misbehaviorHeader.issue.message;
|
|
2144
|
+
const message = `aimock_misbehavior_invalid: ${detail}`;
|
|
2145
|
+
logger.error(`${req.method ?? "?"} ${pathname}: ${message}`);
|
|
2146
|
+
journal.add({
|
|
2147
|
+
method: req.method ?? "?",
|
|
2148
|
+
path: req.url ?? pathname,
|
|
2149
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
2150
|
+
body: null,
|
|
2151
|
+
response: {
|
|
2152
|
+
status: 400,
|
|
2153
|
+
fixture: null,
|
|
2154
|
+
source: "internal",
|
|
2155
|
+
error: message
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2158
|
+
setCorsHeaders(res);
|
|
2159
|
+
require_sse_writer.writeErrorResponse(res, 400, invalidMisbehaviorHeaderEnvelope(normalizeCompatPath(pathname), detail));
|
|
2160
|
+
return;
|
|
1513
2161
|
}
|
|
1514
|
-
if (pathname ===
|
|
2162
|
+
for (const { path: mountPath, handler } of mountList) if (pathname === mountPath || pathname.startsWith(mountPath + "/")) {
|
|
2163
|
+
const subPath = pathname.slice(mountPath.length) || "/";
|
|
2164
|
+
if (await handler.handleRequest(req, res, subPath)) return;
|
|
2165
|
+
}
|
|
2166
|
+
if (pathname === require_route_registry.OLLAMA_CHAT_PATH && req.method === "POST") {
|
|
1515
2167
|
try {
|
|
1516
|
-
|
|
2168
|
+
const raw = await require_helpers.readBody(req);
|
|
2169
|
+
ensureRawFixturePositions();
|
|
2170
|
+
await require_ollama.handleOllama(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1517
2171
|
} catch (err) {
|
|
1518
2172
|
routeError(req, res, err, pathname);
|
|
1519
2173
|
}
|
|
1520
2174
|
return;
|
|
1521
2175
|
}
|
|
1522
|
-
if (pathname === OLLAMA_GENERATE_PATH && req.method === "POST") {
|
|
2176
|
+
if (pathname === require_route_registry.OLLAMA_GENERATE_PATH && req.method === "POST") {
|
|
1523
2177
|
try {
|
|
1524
|
-
|
|
2178
|
+
const raw = await require_helpers.readBody(req);
|
|
2179
|
+
ensureRawFixturePositions();
|
|
2180
|
+
await require_ollama.handleOllamaGenerate(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1525
2181
|
} catch (err) {
|
|
1526
2182
|
routeError(req, res, err, pathname);
|
|
1527
2183
|
}
|
|
1528
2184
|
return;
|
|
1529
2185
|
}
|
|
1530
|
-
if ((pathname === OLLAMA_EMBEDDINGS_PATH || pathname === OLLAMA_EMBED_PATH) && req.method === "POST") {
|
|
2186
|
+
if ((pathname === require_route_registry.OLLAMA_EMBEDDINGS_PATH || pathname === require_route_registry.OLLAMA_EMBED_PATH) && req.method === "POST") {
|
|
1531
2187
|
try {
|
|
1532
|
-
|
|
2188
|
+
const raw = await require_helpers.readBody(req);
|
|
2189
|
+
ensureRawFixturePositions();
|
|
2190
|
+
await require_ollama.handleOllamaEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1533
2191
|
} catch (err) {
|
|
1534
2192
|
routeError(req, res, err, pathname);
|
|
1535
2193
|
}
|
|
1536
2194
|
return;
|
|
1537
2195
|
}
|
|
1538
|
-
if (pathname === OLLAMA_TAGS_PATH && req.method === "GET") {
|
|
2196
|
+
if (pathname === require_route_registry.OLLAMA_TAGS_PATH && req.method === "GET") {
|
|
1539
2197
|
setCorsHeaders(res);
|
|
1540
2198
|
const modelIds = /* @__PURE__ */ new Set();
|
|
1541
2199
|
for (const f of fixtures) if (f.match.model && typeof f.match.model === "string") modelIds.add(f.match.model);
|
|
@@ -1551,7 +2209,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1551
2209
|
res.end(JSON.stringify({ models }));
|
|
1552
2210
|
return;
|
|
1553
2211
|
}
|
|
1554
|
-
const openRouterVideoContentMatch = pathname.match(
|
|
2212
|
+
const openRouterVideoContentMatch = pathname.match(require_route_registry.OPENROUTER_VIDEO_CONTENT_RE);
|
|
1555
2213
|
if (openRouterVideoContentMatch && req.method === "GET") {
|
|
1556
2214
|
try {
|
|
1557
2215
|
await require_openrouter_video.handleOpenRouterVideoContent(req, res, openRouterVideoContentMatch[1], journal, defaults, setCorsHeaders, openRouterVideoJobs);
|
|
@@ -1560,77 +2218,85 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1560
2218
|
}
|
|
1561
2219
|
return;
|
|
1562
2220
|
}
|
|
1563
|
-
if (pathname === OPENROUTER_VIDEO_MODELS_PATH && req.method === "GET") {
|
|
2221
|
+
if (pathname === require_route_registry.OPENROUTER_VIDEO_MODELS_PATH && req.method === "GET") {
|
|
1564
2222
|
try {
|
|
2223
|
+
ensureRawFixturePositions();
|
|
1565
2224
|
await require_openrouter_video.handleOpenRouterVideoModels(req, res, fixtures, journal, defaults, setCorsHeaders);
|
|
1566
2225
|
} catch (err) {
|
|
1567
2226
|
routeError(req, res, err, pathname);
|
|
1568
2227
|
}
|
|
1569
2228
|
return;
|
|
1570
2229
|
}
|
|
1571
|
-
const openRouterVideoStatusMatch = pathname.match(
|
|
2230
|
+
const openRouterVideoStatusMatch = pathname.match(require_route_registry.OPENROUTER_VIDEO_STATUS_RE);
|
|
1572
2231
|
if (openRouterVideoStatusMatch && req.method === "GET") {
|
|
1573
2232
|
try {
|
|
2233
|
+
ensureRawFixturePositions();
|
|
1574
2234
|
await require_openrouter_video.handleOpenRouterVideoStatus(req, res, openRouterVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
|
|
1575
2235
|
} catch (err) {
|
|
1576
2236
|
routeError(req, res, err, pathname);
|
|
1577
2237
|
}
|
|
1578
2238
|
return;
|
|
1579
2239
|
}
|
|
1580
|
-
if (pathname === OPENROUTER_VIDEOS_PATH && req.method === "POST") {
|
|
2240
|
+
if (pathname === require_route_registry.OPENROUTER_VIDEOS_PATH && req.method === "POST") {
|
|
1581
2241
|
setCorsHeaders(res);
|
|
1582
2242
|
try {
|
|
1583
|
-
|
|
2243
|
+
const raw = await require_helpers.readBody(req);
|
|
2244
|
+
ensureRawFixturePositions();
|
|
2245
|
+
await require_openrouter_video.handleOpenRouterVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
|
|
1584
2246
|
} catch (err) {
|
|
1585
2247
|
routeError(req, res, err, pathname);
|
|
1586
2248
|
}
|
|
1587
2249
|
return;
|
|
1588
2250
|
}
|
|
1589
|
-
const bytePlusVideoStatusMatch = pathname.match(
|
|
2251
|
+
const bytePlusVideoStatusMatch = pathname.match(require_route_registry.BYTEPLUS_VIDEO_STATUS_RE);
|
|
1590
2252
|
if (bytePlusVideoStatusMatch && req.method === "GET") {
|
|
1591
2253
|
try {
|
|
2254
|
+
ensureRawFixturePositions();
|
|
1592
2255
|
await require_byteplus_video.handleBytePlusVideoStatus(req, res, bytePlusVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, bytePlusVideoJobs);
|
|
1593
2256
|
} catch (err) {
|
|
1594
2257
|
routeError(req, res, err, pathname);
|
|
1595
2258
|
}
|
|
1596
2259
|
return;
|
|
1597
2260
|
}
|
|
1598
|
-
if (
|
|
2261
|
+
if (require_route_registry.BYTEPLUS_VIDEO_SUBMIT_RE.test(pathname) && req.method === "POST") {
|
|
1599
2262
|
setCorsHeaders(res);
|
|
1600
2263
|
try {
|
|
1601
|
-
|
|
2264
|
+
const raw = await require_helpers.readBody(req);
|
|
2265
|
+
ensureRawFixturePositions();
|
|
2266
|
+
await require_byteplus_video.handleBytePlusVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, bytePlusVideoJobs);
|
|
1602
2267
|
} catch (err) {
|
|
1603
2268
|
routeError(req, res, err, pathname);
|
|
1604
2269
|
}
|
|
1605
2270
|
return;
|
|
1606
2271
|
}
|
|
1607
|
-
if (pathname ===
|
|
2272
|
+
if (pathname === require_route_registry.OPENROUTER_MODELS_PATH && req.method === "GET") {
|
|
2273
|
+
ensureRawFixturePositions();
|
|
1608
2274
|
require_openrouter_chat.handleOpenRouterModels(req, res, fixtures, journal, defaults, setCorsHeaders);
|
|
1609
2275
|
return;
|
|
1610
2276
|
}
|
|
1611
|
-
if (pathname ===
|
|
2277
|
+
if (pathname === require_route_registry.OPENROUTER_KEY_PATH && req.method === "GET") {
|
|
1612
2278
|
require_openrouter_chat.handleOpenRouterKey(req, res, journal, setCorsHeaders);
|
|
1613
2279
|
return;
|
|
1614
2280
|
}
|
|
1615
|
-
if (pathname ===
|
|
2281
|
+
if (pathname === require_route_registry.OPENROUTER_CREDITS_PATH && req.method === "GET") {
|
|
1616
2282
|
require_openrouter_chat.handleOpenRouterCredits(req, res, journal, setCorsHeaders);
|
|
1617
2283
|
return;
|
|
1618
2284
|
}
|
|
1619
2285
|
let azureDeploymentId;
|
|
1620
|
-
const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);
|
|
2286
|
+
const azureMatch = pathname.match(require_route_registry.AZURE_DEPLOYMENT_RE);
|
|
1621
2287
|
if (azureMatch && req.method === "POST") {
|
|
1622
2288
|
azureDeploymentId = azureMatch[1];
|
|
1623
2289
|
pathname = `/v1/${azureMatch[2]}`;
|
|
1624
2290
|
}
|
|
1625
2291
|
if (!azureDeploymentId) pathname = normalizeCompatPath(pathname, logger);
|
|
1626
|
-
if (pathname === HEALTH_PATH && req.method === "GET") {
|
|
2292
|
+
if (pathname === require_route_registry.HEALTH_PATH && req.method === "GET") {
|
|
1627
2293
|
setCorsHeaders(res);
|
|
1628
|
-
if (
|
|
2294
|
+
if (mountList.length > 0) {
|
|
1629
2295
|
const services = { llm: {
|
|
1630
2296
|
status: "ok",
|
|
1631
2297
|
fixtures: fixtures.length
|
|
1632
2298
|
} };
|
|
1633
|
-
for (const { path: mountPath, handler } of
|
|
2299
|
+
for (const { path: mountPath, handler } of mountList) if (handler.health) {
|
|
1634
2300
|
const name = mountPath.replace(/^\//, "");
|
|
1635
2301
|
services[name] = handler.health();
|
|
1636
2302
|
}
|
|
@@ -1645,13 +2311,13 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1645
2311
|
}
|
|
1646
2312
|
return;
|
|
1647
2313
|
}
|
|
1648
|
-
if (pathname === READY_PATH && req.method === "GET") {
|
|
2314
|
+
if (pathname === require_route_registry.READY_PATH && req.method === "GET") {
|
|
1649
2315
|
setCorsHeaders(res);
|
|
1650
2316
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1651
2317
|
res.end(JSON.stringify({ status: "ready" }));
|
|
1652
2318
|
return;
|
|
1653
2319
|
}
|
|
1654
|
-
if (pathname ===
|
|
2320
|
+
if (pathname === require_route_registry.METRICS_PATH && req.method === "GET") {
|
|
1655
2321
|
if (!registry) {
|
|
1656
2322
|
handleNotFound(res, "Not found");
|
|
1657
2323
|
return;
|
|
@@ -1661,7 +2327,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1661
2327
|
res.end(registry.serialize());
|
|
1662
2328
|
return;
|
|
1663
2329
|
}
|
|
1664
|
-
if (pathname === MODELS_PATH && req.method === "GET") {
|
|
2330
|
+
if (pathname === require_route_registry.MODELS_PATH && req.method === "GET") {
|
|
1665
2331
|
setCorsHeaders(res);
|
|
1666
2332
|
const modelIds = /* @__PURE__ */ new Set();
|
|
1667
2333
|
for (const f of fixtures) if (f.match.model && typeof f.match.model === "string") modelIds.add(f.match.model);
|
|
@@ -1678,7 +2344,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1678
2344
|
}));
|
|
1679
2345
|
return;
|
|
1680
2346
|
}
|
|
1681
|
-
const batchesCancelMatch = pathname.match(BATCHES_CANCEL_RE);
|
|
2347
|
+
const batchesCancelMatch = pathname.match(require_route_registry.BATCHES_CANCEL_RE);
|
|
1682
2348
|
if (batchesCancelMatch && req.method === "POST") {
|
|
1683
2349
|
try {
|
|
1684
2350
|
await require_helpers.readBody(req);
|
|
@@ -1688,7 +2354,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1688
2354
|
}
|
|
1689
2355
|
return;
|
|
1690
2356
|
}
|
|
1691
|
-
const batchesIdMatch = pathname.match(BATCHES_ID_RE);
|
|
2357
|
+
const batchesIdMatch = pathname.match(require_route_registry.BATCHES_ID_RE);
|
|
1692
2358
|
if (batchesIdMatch && req.method === "GET") {
|
|
1693
2359
|
try {
|
|
1694
2360
|
await require_batches.handleBatchesRetrieve(req, res, batchesIdMatch[1], journal, defaults, setCorsHeaders);
|
|
@@ -1697,7 +2363,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1697
2363
|
}
|
|
1698
2364
|
return;
|
|
1699
2365
|
}
|
|
1700
|
-
if (pathname === BATCHES_PATH && req.method === "GET") {
|
|
2366
|
+
if (pathname === require_route_registry.BATCHES_PATH && req.method === "GET") {
|
|
1701
2367
|
try {
|
|
1702
2368
|
await require_batches.handleBatchesList(req, res, journal, defaults, setCorsHeaders);
|
|
1703
2369
|
} catch (err) {
|
|
@@ -1705,7 +2371,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1705
2371
|
}
|
|
1706
2372
|
return;
|
|
1707
2373
|
}
|
|
1708
|
-
if (pathname === BATCHES_PATH && req.method === "POST") {
|
|
2374
|
+
if (pathname === require_route_registry.BATCHES_PATH && req.method === "POST") {
|
|
1709
2375
|
try {
|
|
1710
2376
|
await require_batches.handleBatchesCreate(req, res, await require_helpers.readBody(req), journal, defaults, setCorsHeaders);
|
|
1711
2377
|
} catch (err) {
|
|
@@ -1713,7 +2379,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1713
2379
|
}
|
|
1714
2380
|
return;
|
|
1715
2381
|
}
|
|
1716
|
-
const filesContentMatch = pathname.match(
|
|
2382
|
+
const filesContentMatch = pathname.match(require_route_registry.FILES_CONTENT_RE);
|
|
1717
2383
|
if (filesContentMatch && req.method === "GET") {
|
|
1718
2384
|
try {
|
|
1719
2385
|
await require_files.handleFilesContent(req, res, filesContentMatch[1], journal, defaults, setCorsHeaders);
|
|
@@ -1722,7 +2388,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1722
2388
|
}
|
|
1723
2389
|
return;
|
|
1724
2390
|
}
|
|
1725
|
-
const filesIdMatch = pathname.match(
|
|
2391
|
+
const filesIdMatch = pathname.match(require_route_registry.FILES_ID_RE);
|
|
1726
2392
|
if (filesIdMatch && req.method === "GET") {
|
|
1727
2393
|
try {
|
|
1728
2394
|
await require_files.handleFilesRetrieve(req, res, filesIdMatch[1], journal, defaults, setCorsHeaders);
|
|
@@ -1739,7 +2405,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1739
2405
|
}
|
|
1740
2406
|
return;
|
|
1741
2407
|
}
|
|
1742
|
-
if (pathname === FILES_PATH && req.method === "GET") {
|
|
2408
|
+
if (pathname === require_route_registry.FILES_PATH && req.method === "GET") {
|
|
1743
2409
|
try {
|
|
1744
2410
|
await require_files.handleFilesList(req, res, journal, defaults, setCorsHeaders);
|
|
1745
2411
|
} catch (err) {
|
|
@@ -1747,7 +2413,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1747
2413
|
}
|
|
1748
2414
|
return;
|
|
1749
2415
|
}
|
|
1750
|
-
if (pathname === FILES_PATH && req.method === "POST") {
|
|
2416
|
+
if (pathname === require_route_registry.FILES_PATH && req.method === "POST") {
|
|
1751
2417
|
try {
|
|
1752
2418
|
await require_files.handleFilesCreate(req, res, (await require_helpers.readBodyBufferBounded(req, require_files.FILES_BODY_MAX_BYTES, require_files.FILES_BODY_DRAIN_MAX_BYTES)).buffer ?? require_files.FILES_BODY_OVERSIZED, journal, defaults, setCorsHeaders);
|
|
1753
2419
|
} catch (err) {
|
|
@@ -1755,7 +2421,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1755
2421
|
}
|
|
1756
2422
|
return;
|
|
1757
2423
|
}
|
|
1758
|
-
const ftCancelMatch = pathname.match(FINE_TUNING_CANCEL_RE);
|
|
2424
|
+
const ftCancelMatch = pathname.match(require_route_registry.FINE_TUNING_CANCEL_RE);
|
|
1759
2425
|
if (ftCancelMatch && req.method === "POST") {
|
|
1760
2426
|
try {
|
|
1761
2427
|
await require_helpers.readBody(req);
|
|
@@ -1765,7 +2431,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1765
2431
|
}
|
|
1766
2432
|
return;
|
|
1767
2433
|
}
|
|
1768
|
-
const ftEventsMatch = pathname.match(FINE_TUNING_EVENTS_RE);
|
|
2434
|
+
const ftEventsMatch = pathname.match(require_route_registry.FINE_TUNING_EVENTS_RE);
|
|
1769
2435
|
if (ftEventsMatch && req.method === "GET") {
|
|
1770
2436
|
try {
|
|
1771
2437
|
await require_fine_tuning.handleFineTuningEvents(req, res, ftEventsMatch[1], journal, defaults, setCorsHeaders);
|
|
@@ -1774,7 +2440,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1774
2440
|
}
|
|
1775
2441
|
return;
|
|
1776
2442
|
}
|
|
1777
|
-
const ftIdMatch = pathname.match(FINE_TUNING_ID_RE);
|
|
2443
|
+
const ftIdMatch = pathname.match(require_route_registry.FINE_TUNING_ID_RE);
|
|
1778
2444
|
if (ftIdMatch && req.method === "GET") {
|
|
1779
2445
|
try {
|
|
1780
2446
|
await require_fine_tuning.handleFineTuningRetrieve(req, res, ftIdMatch[1], journal, defaults, setCorsHeaders);
|
|
@@ -1783,7 +2449,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1783
2449
|
}
|
|
1784
2450
|
return;
|
|
1785
2451
|
}
|
|
1786
|
-
if (pathname === FINE_TUNING_JOBS_PATH && req.method === "GET") {
|
|
2452
|
+
if (pathname === require_route_registry.FINE_TUNING_JOBS_PATH && req.method === "GET") {
|
|
1787
2453
|
try {
|
|
1788
2454
|
await require_fine_tuning.handleFineTuningList(req, res, journal, defaults, setCorsHeaders);
|
|
1789
2455
|
} catch (err) {
|
|
@@ -1791,7 +2457,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1791
2457
|
}
|
|
1792
2458
|
return;
|
|
1793
2459
|
}
|
|
1794
|
-
if (pathname === FINE_TUNING_JOBS_PATH && req.method === "POST") {
|
|
2460
|
+
if (pathname === require_route_registry.FINE_TUNING_JOBS_PATH && req.method === "POST") {
|
|
1795
2461
|
try {
|
|
1796
2462
|
await require_fine_tuning.handleFineTuningCreate(req, res, await require_helpers.readBody(req), journal, defaults, setCorsHeaders);
|
|
1797
2463
|
} catch (err) {
|
|
@@ -1799,14 +2465,140 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1799
2465
|
}
|
|
1800
2466
|
return;
|
|
1801
2467
|
}
|
|
1802
|
-
|
|
2468
|
+
const vsSearchMatch = pathname.match(require_route_registry.VECTOR_STORES_SEARCH_RE);
|
|
2469
|
+
if (vsSearchMatch && req.method === "POST") {
|
|
2470
|
+
try {
|
|
2471
|
+
const raw = await require_helpers.readBody(req);
|
|
2472
|
+
await require_vector_stores.handleVectorStoresSearch(req, res, vsSearchMatch[1], raw, journal, defaults, setCorsHeaders);
|
|
2473
|
+
} catch (err) {
|
|
2474
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2475
|
+
}
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
const vsBatchFilesMatch = pathname.match(require_route_registry.VECTOR_STORES_BATCH_FILES_RE);
|
|
2479
|
+
if (vsBatchFilesMatch && req.method === "GET") {
|
|
2480
|
+
try {
|
|
2481
|
+
await require_vector_stores.handleVectorFileBatchesFiles(req, res, vsBatchFilesMatch[1], vsBatchFilesMatch[2], journal, defaults, setCorsHeaders);
|
|
2482
|
+
} catch (err) {
|
|
2483
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2484
|
+
}
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
const vsBatchCancelMatch = pathname.match(require_route_registry.VECTOR_STORES_BATCH_CANCEL_RE);
|
|
2488
|
+
if (vsBatchCancelMatch && req.method === "POST") {
|
|
2489
|
+
try {
|
|
2490
|
+
await require_helpers.readBody(req);
|
|
2491
|
+
await require_vector_stores.handleVectorFileBatchesCancel(req, res, vsBatchCancelMatch[1], vsBatchCancelMatch[2], journal, defaults, setCorsHeaders);
|
|
2492
|
+
} catch (err) {
|
|
2493
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2494
|
+
}
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
const vsBatchMatch = pathname.match(require_route_registry.VECTOR_STORES_BATCH_RE);
|
|
2498
|
+
if (vsBatchMatch && req.method === "GET") {
|
|
2499
|
+
try {
|
|
2500
|
+
await require_vector_stores.handleVectorFileBatchesRetrieve(req, res, vsBatchMatch[1], vsBatchMatch[2], journal, defaults, setCorsHeaders);
|
|
2501
|
+
} catch (err) {
|
|
2502
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2503
|
+
}
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
const vsFileMatch = pathname.match(require_route_registry.VECTOR_STORES_FILE_RE);
|
|
2507
|
+
if (vsFileMatch && req.method === "GET") {
|
|
2508
|
+
try {
|
|
2509
|
+
await require_vector_stores.handleVectorStoreFilesRetrieve(req, res, vsFileMatch[1], vsFileMatch[2], journal, defaults, setCorsHeaders);
|
|
2510
|
+
} catch (err) {
|
|
2511
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2512
|
+
}
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
if (vsFileMatch && req.method === "DELETE") {
|
|
2516
|
+
try {
|
|
2517
|
+
await require_vector_stores.handleVectorStoreFilesDelete(req, res, vsFileMatch[1], vsFileMatch[2], journal, defaults, setCorsHeaders);
|
|
2518
|
+
} catch (err) {
|
|
2519
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2520
|
+
}
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
const vsIdMatch = pathname.match(require_route_registry.VECTOR_STORES_ID_RE);
|
|
2524
|
+
if (vsIdMatch && req.method === "GET") {
|
|
2525
|
+
try {
|
|
2526
|
+
await require_vector_stores.handleVectorStoresRetrieve(req, res, vsIdMatch[1], journal, defaults, setCorsHeaders);
|
|
2527
|
+
} catch (err) {
|
|
2528
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2529
|
+
}
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
if (vsIdMatch && req.method === "POST") {
|
|
2533
|
+
try {
|
|
2534
|
+
const raw = await require_helpers.readBody(req);
|
|
2535
|
+
await require_vector_stores.handleVectorStoresModify(req, res, vsIdMatch[1], raw, journal, defaults, setCorsHeaders);
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2538
|
+
}
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
if (vsIdMatch && req.method === "DELETE") {
|
|
2542
|
+
try {
|
|
2543
|
+
await require_vector_stores.handleVectorStoresDelete(req, res, vsIdMatch[1], journal, defaults, setCorsHeaders);
|
|
2544
|
+
} catch (err) {
|
|
2545
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2546
|
+
}
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
if (require_route_registry.VECTOR_STORES_FILES_RE.test(pathname) && req.method === "POST") {
|
|
2550
|
+
try {
|
|
2551
|
+
const raw = await require_helpers.readBody(req);
|
|
2552
|
+
const storeId = pathname.split("/")[3];
|
|
2553
|
+
await require_vector_stores.handleVectorStoreFilesCreate(req, res, storeId, raw, journal, defaults, setCorsHeaders);
|
|
2554
|
+
} catch (err) {
|
|
2555
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2556
|
+
}
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
if (require_route_registry.VECTOR_STORES_FILES_RE.test(pathname) && req.method === "GET") {
|
|
2560
|
+
try {
|
|
2561
|
+
const storeId = pathname.split("/")[3];
|
|
2562
|
+
await require_vector_stores.handleVectorStoreFilesList(req, res, storeId, journal, defaults, setCorsHeaders);
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2565
|
+
}
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
if (require_route_registry.VECTOR_STORES_FILE_BATCHES_RE.test(pathname) && req.method === "POST") {
|
|
2569
|
+
try {
|
|
2570
|
+
const raw = await require_helpers.readBody(req);
|
|
2571
|
+
const storeId = pathname.split("/")[3];
|
|
2572
|
+
await require_vector_stores.handleVectorFileBatchesCreate(req, res, storeId, raw, journal, defaults, setCorsHeaders);
|
|
2573
|
+
} catch (err) {
|
|
2574
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2575
|
+
}
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
if (pathname === require_route_registry.VECTOR_STORES_PATH && req.method === "GET") {
|
|
2579
|
+
try {
|
|
2580
|
+
await require_vector_stores.handleVectorStoresList(req, res, journal, defaults, setCorsHeaders);
|
|
2581
|
+
} catch (err) {
|
|
2582
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2583
|
+
}
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
if (pathname === require_route_registry.VECTOR_STORES_PATH && req.method === "POST") {
|
|
2587
|
+
try {
|
|
2588
|
+
await require_vector_stores.handleVectorStoresCreate(req, res, await require_helpers.readBody(req), journal, defaults, setCorsHeaders);
|
|
2589
|
+
} catch (err) {
|
|
2590
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2591
|
+
}
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
if (pathname === require_route_registry.REQUESTS_PATH) {
|
|
1803
2595
|
setCorsHeaders(res);
|
|
1804
2596
|
if (req.method === "GET") {
|
|
1805
2597
|
const limitParam = parsedUrl.searchParams.get("limit");
|
|
1806
2598
|
let opts;
|
|
1807
2599
|
if (limitParam) {
|
|
1808
|
-
const limit =
|
|
1809
|
-
if (Number.
|
|
2600
|
+
const limit = Number(limitParam);
|
|
2601
|
+
if (!/^\+?\d+$/.test(limitParam.trim()) || !Number.isSafeInteger(limit) || limit <= 0) {
|
|
1810
2602
|
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
1811
2603
|
message: `Invalid limit parameter: "${limitParam}"`,
|
|
1812
2604
|
type: "invalid_request_error"
|
|
@@ -1817,7 +2609,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1817
2609
|
}
|
|
1818
2610
|
const entries = journal.getAll(opts);
|
|
1819
2611
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1820
|
-
res.end(
|
|
2612
|
+
res.end(journalJson(entries, journal));
|
|
1821
2613
|
return;
|
|
1822
2614
|
}
|
|
1823
2615
|
if (req.method === "DELETE") {
|
|
@@ -1829,39 +2621,47 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1829
2621
|
handleNotFound(res, "Not found");
|
|
1830
2622
|
return;
|
|
1831
2623
|
}
|
|
1832
|
-
if (pathname === RESPONSES_PATH && req.method === "POST") {
|
|
2624
|
+
if (pathname === require_route_registry.RESPONSES_PATH && req.method === "POST") {
|
|
1833
2625
|
try {
|
|
1834
|
-
|
|
2626
|
+
const raw = await require_helpers.readBody(req);
|
|
2627
|
+
ensureRawFixturePositions();
|
|
2628
|
+
await require_responses.handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1835
2629
|
} catch (err) {
|
|
1836
2630
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
1837
2631
|
}
|
|
1838
2632
|
return;
|
|
1839
2633
|
}
|
|
1840
|
-
if (pathname === MESSAGES_PATH && req.method === "POST") {
|
|
2634
|
+
if (pathname === require_route_registry.MESSAGES_PATH && req.method === "POST") {
|
|
1841
2635
|
try {
|
|
1842
|
-
|
|
2636
|
+
const raw = await require_helpers.readBody(req);
|
|
2637
|
+
ensureRawFixturePositions();
|
|
2638
|
+
await require_messages.handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1843
2639
|
} catch (err) {
|
|
1844
2640
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
1845
2641
|
}
|
|
1846
2642
|
return;
|
|
1847
2643
|
}
|
|
1848
|
-
if (pathname === COHERE_CHAT_PATH && req.method === "POST") {
|
|
2644
|
+
if (pathname === require_route_registry.COHERE_CHAT_PATH && req.method === "POST") {
|
|
1849
2645
|
try {
|
|
1850
|
-
|
|
2646
|
+
const raw = await require_helpers.readBody(req);
|
|
2647
|
+
ensureRawFixturePositions();
|
|
2648
|
+
await require_cohere.handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1851
2649
|
} catch (err) {
|
|
1852
2650
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
1853
2651
|
}
|
|
1854
2652
|
return;
|
|
1855
2653
|
}
|
|
1856
|
-
if (pathname === COHERE_EMBED_PATH && req.method === "POST") {
|
|
2654
|
+
if (pathname === require_route_registry.COHERE_EMBED_PATH && req.method === "POST") {
|
|
1857
2655
|
try {
|
|
1858
|
-
|
|
2656
|
+
const raw = await require_helpers.readBody(req);
|
|
2657
|
+
ensureRawFixturePositions();
|
|
2658
|
+
await require_cohere.handleCohereEmbed(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1859
2659
|
} catch (err) {
|
|
1860
2660
|
routeError(req, res, err, pathname);
|
|
1861
2661
|
}
|
|
1862
2662
|
return;
|
|
1863
2663
|
}
|
|
1864
|
-
if (pathname === EMBEDDINGS_PATH && req.method === "POST") {
|
|
2664
|
+
if (pathname === require_route_registry.EMBEDDINGS_PATH && req.method === "POST") {
|
|
1865
2665
|
try {
|
|
1866
2666
|
const deploymentId = azureDeploymentId;
|
|
1867
2667
|
const embeddingsProvider = azureDeploymentId ? "azure" : "openai";
|
|
@@ -1875,196 +2675,235 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1875
2675
|
} catch (err) {
|
|
1876
2676
|
if (!(err instanceof SyntaxError)) defaults.logger.error(`Unexpected error in Azure model injection: ${err instanceof Error ? err.message : String(err)}`);
|
|
1877
2677
|
}
|
|
1878
|
-
|
|
2678
|
+
ensureRawFixturePositions();
|
|
2679
|
+
await require_embeddings.handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider, originalPathname === require_route_registry.EMBEDDINGS_PATH);
|
|
1879
2680
|
} catch (err) {
|
|
1880
2681
|
routeError(req, res, err, pathname);
|
|
1881
2682
|
}
|
|
1882
2683
|
return;
|
|
1883
2684
|
}
|
|
1884
|
-
if (pathname === IMAGES_PATH && req.method === "POST") {
|
|
2685
|
+
if (pathname === require_route_registry.IMAGES_PATH && req.method === "POST") {
|
|
1885
2686
|
try {
|
|
1886
|
-
|
|
2687
|
+
const raw = await require_helpers.readBody(req);
|
|
2688
|
+
ensureRawFixturePositions();
|
|
2689
|
+
await require_images.handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders, "openai", void 0, isBytePlusArk ? "byteplus" : void 0);
|
|
1887
2690
|
} catch (err) {
|
|
1888
2691
|
routeError(req, res, err, pathname);
|
|
1889
2692
|
}
|
|
1890
2693
|
return;
|
|
1891
2694
|
}
|
|
1892
|
-
if (pathname === IMAGES_EDIT_PATH && req.method === "POST") {
|
|
2695
|
+
if (pathname === require_route_registry.IMAGES_EDIT_PATH && req.method === "POST") {
|
|
1893
2696
|
try {
|
|
1894
|
-
|
|
2697
|
+
const raw = await require_helpers.readBody(req);
|
|
2698
|
+
ensureRawFixturePositions();
|
|
2699
|
+
await require_images.handleImageEdit(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1895
2700
|
} catch (err) {
|
|
1896
2701
|
routeError(req, res, err, pathname);
|
|
1897
2702
|
}
|
|
1898
2703
|
return;
|
|
1899
2704
|
}
|
|
1900
|
-
if (pathname === IMAGES_VARIATIONS_PATH && req.method === "POST") {
|
|
2705
|
+
if (pathname === require_route_registry.IMAGES_VARIATIONS_PATH && req.method === "POST") {
|
|
1901
2706
|
try {
|
|
1902
|
-
|
|
2707
|
+
const raw = await require_helpers.readBody(req);
|
|
2708
|
+
ensureRawFixturePositions();
|
|
2709
|
+
await require_images.handleImageVariations(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1903
2710
|
} catch (err) {
|
|
1904
2711
|
routeError(req, res, err, pathname);
|
|
1905
2712
|
}
|
|
1906
2713
|
return;
|
|
1907
2714
|
}
|
|
1908
|
-
if (pathname === SPEECH_PATH && req.method === "POST") {
|
|
2715
|
+
if (pathname === require_route_registry.SPEECH_PATH && req.method === "POST") {
|
|
1909
2716
|
try {
|
|
1910
|
-
|
|
2717
|
+
const raw = await require_helpers.readBody(req);
|
|
2718
|
+
ensureRawFixturePositions();
|
|
2719
|
+
await require_speech.handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1911
2720
|
} catch (err) {
|
|
1912
2721
|
routeError(req, res, err, pathname);
|
|
1913
2722
|
}
|
|
1914
2723
|
return;
|
|
1915
2724
|
}
|
|
1916
|
-
if (pathname === TRANSCRIPTIONS_PATH && req.method === "POST") {
|
|
2725
|
+
if (pathname === require_route_registry.TRANSCRIPTIONS_PATH && req.method === "POST") {
|
|
1917
2726
|
try {
|
|
1918
|
-
|
|
2727
|
+
const raw = await require_helpers.readBody(req);
|
|
2728
|
+
ensureRawFixturePositions();
|
|
2729
|
+
await require_transcription.handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1919
2730
|
} catch (err) {
|
|
1920
2731
|
routeError(req, res, err, pathname);
|
|
1921
2732
|
}
|
|
1922
2733
|
return;
|
|
1923
2734
|
}
|
|
1924
|
-
if (pathname === TRANSLATIONS_PATH && req.method === "POST") {
|
|
2735
|
+
if (pathname === require_route_registry.TRANSLATIONS_PATH && req.method === "POST") {
|
|
1925
2736
|
try {
|
|
1926
|
-
|
|
2737
|
+
const raw = await require_helpers.readBody(req);
|
|
2738
|
+
ensureRawFixturePositions();
|
|
2739
|
+
await require_transcription.handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders, "translation");
|
|
1927
2740
|
} catch (err) {
|
|
1928
2741
|
routeError(req, res, err, pathname);
|
|
1929
2742
|
}
|
|
1930
2743
|
return;
|
|
1931
2744
|
}
|
|
1932
|
-
if (pathname ===
|
|
2745
|
+
if (pathname === require_route_registry.GROK_VIDEO_SUBMIT_PATH && req.method === "POST") {
|
|
1933
2746
|
setCorsHeaders(res);
|
|
1934
2747
|
try {
|
|
1935
|
-
|
|
2748
|
+
const raw = await require_helpers.readBody(req);
|
|
2749
|
+
ensureRawFixturePositions();
|
|
2750
|
+
await require_grok_video.handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, grokVideoJobs);
|
|
1936
2751
|
} catch (err) {
|
|
1937
2752
|
routeError(req, res, err, pathname);
|
|
1938
2753
|
}
|
|
1939
2754
|
return;
|
|
1940
2755
|
}
|
|
1941
|
-
if (pathname === VIDEOS_PATH && req.method === "POST") {
|
|
2756
|
+
if (pathname === require_route_registry.VIDEOS_PATH && req.method === "POST") {
|
|
1942
2757
|
try {
|
|
1943
|
-
|
|
2758
|
+
const raw = await require_helpers.readBody(req);
|
|
2759
|
+
ensureRawFixturePositions();
|
|
2760
|
+
await require_video.handleVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, videoStates);
|
|
1944
2761
|
} catch (err) {
|
|
1945
2762
|
routeError(req, res, err, pathname);
|
|
1946
2763
|
}
|
|
1947
2764
|
return;
|
|
1948
2765
|
}
|
|
1949
|
-
const grokVideoStatusMatch = pathname.match(
|
|
2766
|
+
const grokVideoStatusMatch = pathname.match(require_route_registry.GROK_VIDEO_STATUS_RE);
|
|
1950
2767
|
if (grokVideoStatusMatch && grokVideoStatusMatch[1] !== "generations" && req.method === "GET") {
|
|
1951
2768
|
try {
|
|
2769
|
+
ensureRawFixturePositions();
|
|
1952
2770
|
await require_grok_video.handleGrokVideoStatus(req, res, grokVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, grokVideoJobs, videoStates);
|
|
1953
2771
|
} catch (err) {
|
|
1954
2772
|
routeError(req, res, err, pathname);
|
|
1955
2773
|
}
|
|
1956
2774
|
return;
|
|
1957
2775
|
}
|
|
1958
|
-
const veoPredictMatch = pathname.match(
|
|
2776
|
+
const veoPredictMatch = pathname.match(require_route_registry.VEO_PREDICT_LRO_RE);
|
|
1959
2777
|
if (veoPredictMatch && req.method === "POST") {
|
|
1960
2778
|
setCorsHeaders(res);
|
|
1961
2779
|
try {
|
|
1962
|
-
|
|
2780
|
+
const raw = await require_helpers.readBody(req);
|
|
2781
|
+
ensureRawFixturePositions();
|
|
2782
|
+
await require_veo_video.handleVeoVideoCreate(req, res, raw, veoPredictMatch[1], fixtures, journal, defaults, setCorsHeaders, veoVideoJobs);
|
|
1963
2783
|
} catch (err) {
|
|
1964
2784
|
routeError(req, res, err, pathname);
|
|
1965
2785
|
}
|
|
1966
2786
|
return;
|
|
1967
2787
|
}
|
|
1968
|
-
const veoOperationMatch = pathname.match(
|
|
2788
|
+
const veoOperationMatch = pathname.match(require_route_registry.VEO_OPERATION_RE);
|
|
1969
2789
|
if (veoOperationMatch && req.method === "GET") {
|
|
1970
2790
|
try {
|
|
2791
|
+
ensureRawFixturePositions();
|
|
1971
2792
|
await require_veo_video.handleVeoVideoStatus(req, res, veoOperationMatch[1], fixtures, journal, defaults, setCorsHeaders, veoVideoJobs);
|
|
1972
2793
|
} catch (err) {
|
|
1973
2794
|
routeError(req, res, err, pathname);
|
|
1974
2795
|
}
|
|
1975
2796
|
return;
|
|
1976
2797
|
}
|
|
1977
|
-
const geminiPredictMatch = pathname.match(GEMINI_PREDICT_RE);
|
|
2798
|
+
const geminiPredictMatch = pathname.match(require_route_registry.GEMINI_PREDICT_RE);
|
|
1978
2799
|
if (geminiPredictMatch && req.method === "POST") {
|
|
1979
2800
|
const predictModel = geminiPredictMatch[1];
|
|
1980
2801
|
try {
|
|
1981
|
-
|
|
2802
|
+
const raw = await require_helpers.readBody(req);
|
|
2803
|
+
ensureRawFixturePositions();
|
|
2804
|
+
await require_images.handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders, "gemini", predictModel);
|
|
1982
2805
|
} catch (err) {
|
|
1983
2806
|
routeError(req, res, err, pathname);
|
|
1984
2807
|
}
|
|
1985
2808
|
return;
|
|
1986
2809
|
}
|
|
1987
|
-
if (pathname === GEMINI_INTERACTIONS_PATH && req.method === "POST") {
|
|
2810
|
+
if (pathname === require_route_registry.GEMINI_INTERACTIONS_PATH && req.method === "POST") {
|
|
1988
2811
|
try {
|
|
1989
|
-
|
|
2812
|
+
const raw = await require_helpers.readBody(req);
|
|
2813
|
+
ensureRawFixturePositions();
|
|
2814
|
+
await require_gemini_interactions.handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1990
2815
|
} catch (err) {
|
|
1991
2816
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
1992
2817
|
}
|
|
1993
2818
|
return;
|
|
1994
2819
|
}
|
|
1995
|
-
const geminiEmbedMatch = pathname.match(GEMINI_EMBED_RE);
|
|
2820
|
+
const geminiEmbedMatch = pathname.match(require_route_registry.GEMINI_EMBED_RE);
|
|
1996
2821
|
if (geminiEmbedMatch && req.method === "POST") {
|
|
1997
2822
|
const embedModel = geminiEmbedMatch[1];
|
|
1998
2823
|
try {
|
|
1999
|
-
|
|
2824
|
+
const raw = await require_helpers.readBody(req);
|
|
2825
|
+
ensureRawFixturePositions();
|
|
2826
|
+
await require_gemini_embeddings.handleGeminiEmbedContent(req, res, raw, embedModel, fixtures, journal, defaults, setCorsHeaders);
|
|
2000
2827
|
} catch (err) {
|
|
2001
2828
|
routeError(req, res, err, pathname);
|
|
2002
2829
|
}
|
|
2003
2830
|
return;
|
|
2004
2831
|
}
|
|
2005
|
-
const geminiMatch = pathname.match(GEMINI_PATH_RE);
|
|
2832
|
+
const geminiMatch = pathname.match(require_route_registry.GEMINI_PATH_RE);
|
|
2006
2833
|
if (geminiMatch && req.method === "POST") {
|
|
2007
2834
|
const geminiModel = geminiMatch[1];
|
|
2008
2835
|
const streaming = geminiMatch[2] === "streamGenerateContent";
|
|
2009
2836
|
try {
|
|
2010
|
-
|
|
2837
|
+
const raw = await require_helpers.readBody(req);
|
|
2838
|
+
ensureRawFixturePositions();
|
|
2839
|
+
await require_gemini.handleGemini(req, res, raw, geminiModel, streaming, fixtures, journal, defaults, setCorsHeaders);
|
|
2011
2840
|
} catch (err) {
|
|
2012
2841
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2013
2842
|
}
|
|
2014
2843
|
return;
|
|
2015
2844
|
}
|
|
2016
|
-
const vertexMatch = pathname.match(VERTEX_AI_RE);
|
|
2845
|
+
const vertexMatch = pathname.match(require_route_registry.VERTEX_AI_RE);
|
|
2017
2846
|
if (vertexMatch && req.method === "POST") {
|
|
2018
2847
|
const vertexModel = vertexMatch[1];
|
|
2019
2848
|
const streaming = vertexMatch[2] === "streamGenerateContent";
|
|
2020
2849
|
try {
|
|
2021
|
-
|
|
2850
|
+
const raw = await require_helpers.readBody(req);
|
|
2851
|
+
ensureRawFixturePositions();
|
|
2852
|
+
await require_gemini.handleGemini(req, res, raw, vertexModel, streaming, fixtures, journal, defaults, setCorsHeaders, "vertexai");
|
|
2022
2853
|
} catch (err) {
|
|
2023
2854
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2024
2855
|
}
|
|
2025
2856
|
return;
|
|
2026
2857
|
}
|
|
2027
|
-
const bedrockMatch = pathname.match(BEDROCK_INVOKE_RE);
|
|
2858
|
+
const bedrockMatch = pathname.match(require_route_registry.BEDROCK_INVOKE_RE);
|
|
2028
2859
|
if (bedrockMatch && req.method === "POST") {
|
|
2029
2860
|
const bedrockModelId = bedrockMatch[1];
|
|
2030
2861
|
try {
|
|
2031
|
-
|
|
2862
|
+
const raw = await require_helpers.readBody(req);
|
|
2863
|
+
ensureRawFixturePositions();
|
|
2864
|
+
await require_bedrock.handleBedrock(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2032
2865
|
} catch (err) {
|
|
2033
2866
|
routeError(req, res, err, pathname);
|
|
2034
2867
|
}
|
|
2035
2868
|
return;
|
|
2036
2869
|
}
|
|
2037
|
-
const bedrockStreamMatch = pathname.match(BEDROCK_STREAM_RE);
|
|
2870
|
+
const bedrockStreamMatch = pathname.match(require_route_registry.BEDROCK_STREAM_RE);
|
|
2038
2871
|
if (bedrockStreamMatch && req.method === "POST") {
|
|
2039
2872
|
const bedrockModelId = bedrockStreamMatch[1];
|
|
2040
2873
|
try {
|
|
2041
|
-
|
|
2874
|
+
const raw = await require_helpers.readBody(req);
|
|
2875
|
+
ensureRawFixturePositions();
|
|
2876
|
+
await require_bedrock.handleBedrockStream(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2042
2877
|
} catch (err) {
|
|
2043
2878
|
routeError(req, res, err, pathname);
|
|
2044
2879
|
}
|
|
2045
2880
|
return;
|
|
2046
2881
|
}
|
|
2047
|
-
const converseMatch = pathname.match(BEDROCK_CONVERSE_RE);
|
|
2882
|
+
const converseMatch = pathname.match(require_route_registry.BEDROCK_CONVERSE_RE);
|
|
2048
2883
|
if (converseMatch && req.method === "POST") {
|
|
2049
2884
|
const converseModelId = converseMatch[1];
|
|
2050
2885
|
try {
|
|
2051
|
-
|
|
2886
|
+
const raw = await require_helpers.readBody(req);
|
|
2887
|
+
ensureRawFixturePositions();
|
|
2888
|
+
await require_bedrock_converse.handleConverse(req, res, raw, converseModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2052
2889
|
} catch (err) {
|
|
2053
2890
|
routeError(req, res, err, pathname);
|
|
2054
2891
|
}
|
|
2055
2892
|
return;
|
|
2056
2893
|
}
|
|
2057
|
-
const converseStreamMatch = pathname.match(BEDROCK_CONVERSE_STREAM_RE);
|
|
2894
|
+
const converseStreamMatch = pathname.match(require_route_registry.BEDROCK_CONVERSE_STREAM_RE);
|
|
2058
2895
|
if (converseStreamMatch && req.method === "POST") {
|
|
2059
2896
|
const converseStreamModelId = converseStreamMatch[1];
|
|
2060
2897
|
try {
|
|
2061
|
-
|
|
2898
|
+
const raw = await require_helpers.readBody(req);
|
|
2899
|
+
ensureRawFixturePositions();
|
|
2900
|
+
await require_bedrock_converse.handleConverseStream(req, res, raw, converseStreamModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2062
2901
|
} catch (err) {
|
|
2063
2902
|
routeError(req, res, err, pathname);
|
|
2064
2903
|
}
|
|
2065
2904
|
return;
|
|
2066
2905
|
}
|
|
2067
|
-
if (pathname === SEARCH_PATH && req.method === "POST") {
|
|
2906
|
+
if (pathname === require_route_registry.SEARCH_PATH && req.method === "POST") {
|
|
2068
2907
|
try {
|
|
2069
2908
|
await require_search.handleSearch(req, res, await require_helpers.readBody(req), serviceFixtures?.search ?? [], journal, defaults, setCorsHeaders);
|
|
2070
2909
|
} catch (err) {
|
|
@@ -2072,7 +2911,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2072
2911
|
}
|
|
2073
2912
|
return;
|
|
2074
2913
|
}
|
|
2075
|
-
if (pathname === RERANK_PATH && req.method === "POST") {
|
|
2914
|
+
if (pathname === require_route_registry.RERANK_PATH && req.method === "POST") {
|
|
2076
2915
|
try {
|
|
2077
2916
|
await require_rerank.handleRerank(req, res, await require_helpers.readBody(req), serviceFixtures?.rerank ?? [], journal, defaults, setCorsHeaders);
|
|
2078
2917
|
} catch (err) {
|
|
@@ -2080,7 +2919,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2080
2919
|
}
|
|
2081
2920
|
return;
|
|
2082
2921
|
}
|
|
2083
|
-
if (pathname === MODERATIONS_PATH && req.method === "POST") {
|
|
2922
|
+
if (pathname === require_route_registry.MODERATIONS_PATH && req.method === "POST") {
|
|
2084
2923
|
try {
|
|
2085
2924
|
await require_moderation.handleModeration(req, res, await require_helpers.readBody(req), serviceFixtures?.moderation ?? [], journal, defaults, setCorsHeaders);
|
|
2086
2925
|
} catch (err) {
|
|
@@ -2088,34 +2927,40 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2088
2927
|
}
|
|
2089
2928
|
return;
|
|
2090
2929
|
}
|
|
2091
|
-
if (pathname === ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
|
|
2930
|
+
if (pathname === require_route_registry.ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
|
|
2092
2931
|
setCorsHeaders(res);
|
|
2093
2932
|
try {
|
|
2094
|
-
|
|
2933
|
+
const raw = await require_helpers.readBody(req);
|
|
2934
|
+
ensureRawFixturePositions();
|
|
2935
|
+
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, "sound-generation");
|
|
2095
2936
|
} catch (err) {
|
|
2096
2937
|
routeError(req, res, err, pathname);
|
|
2097
2938
|
}
|
|
2098
2939
|
return;
|
|
2099
2940
|
}
|
|
2100
|
-
if (pathname === ELEVENLABS_VOICE_DESIGN_PATH && req.method === "POST") {
|
|
2941
|
+
if (pathname === require_route_registry.ELEVENLABS_VOICE_DESIGN_PATH && req.method === "POST") {
|
|
2101
2942
|
setCorsHeaders(res);
|
|
2102
2943
|
try {
|
|
2103
|
-
|
|
2944
|
+
const raw = await require_helpers.readBody(req);
|
|
2945
|
+
ensureRawFixturePositions();
|
|
2946
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceDesign(req, res, raw, fixtures, defaults, journal);
|
|
2104
2947
|
} catch (err) {
|
|
2105
2948
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2106
2949
|
}
|
|
2107
2950
|
return;
|
|
2108
2951
|
}
|
|
2109
|
-
if (pathname === ELEVENLABS_VOICE_CREATE_PATH && req.method === "POST") {
|
|
2952
|
+
if (pathname === require_route_registry.ELEVENLABS_VOICE_CREATE_PATH && req.method === "POST") {
|
|
2110
2953
|
setCorsHeaders(res);
|
|
2111
2954
|
try {
|
|
2112
|
-
|
|
2955
|
+
const raw = await require_helpers.readBody(req);
|
|
2956
|
+
ensureRawFixturePositions();
|
|
2957
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceCreate(req, res, raw, fixtures, defaults, journal);
|
|
2113
2958
|
} catch (err) {
|
|
2114
2959
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2115
2960
|
}
|
|
2116
2961
|
return;
|
|
2117
2962
|
}
|
|
2118
|
-
const elevenLabsVoiceMatch = pathname.match(ELEVENLABS_VOICE_RE);
|
|
2963
|
+
const elevenLabsVoiceMatch = pathname.match(require_route_registry.ELEVENLABS_VOICE_RE);
|
|
2119
2964
|
if (elevenLabsVoiceMatch && (req.method === "GET" || req.method === "DELETE")) {
|
|
2120
2965
|
setCorsHeaders(res);
|
|
2121
2966
|
let voiceId;
|
|
@@ -2140,37 +2985,46 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2140
2985
|
return;
|
|
2141
2986
|
}
|
|
2142
2987
|
try {
|
|
2143
|
-
if (req.method === "GET")
|
|
2144
|
-
|
|
2988
|
+
if (req.method === "GET") {
|
|
2989
|
+
ensureRawFixturePositions();
|
|
2990
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceGet(req, res, voiceId, fixtures, defaults, journal);
|
|
2991
|
+
} else {
|
|
2992
|
+
ensureRawFixturePositions();
|
|
2993
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceDelete(req, res, voiceId, fixtures, defaults, journal);
|
|
2994
|
+
}
|
|
2145
2995
|
} catch (err) {
|
|
2146
2996
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2147
2997
|
}
|
|
2148
2998
|
return;
|
|
2149
2999
|
}
|
|
2150
|
-
const elevenLabsTTSMatch = pathname.match(ELEVENLABS_TTS_RE);
|
|
3000
|
+
const elevenLabsTTSMatch = pathname.match(require_route_registry.ELEVENLABS_TTS_RE);
|
|
2151
3001
|
if (elevenLabsTTSMatch && req.method === "POST") {
|
|
2152
3002
|
setCorsHeaders(res);
|
|
2153
3003
|
const voiceId = elevenLabsTTSMatch[1];
|
|
2154
3004
|
try {
|
|
2155
|
-
|
|
3005
|
+
const raw = await require_helpers.readBody(req);
|
|
3006
|
+
ensureRawFixturePositions();
|
|
3007
|
+
await require_elevenlabs_audio.handleElevenLabsTTS(req, res, raw, fixtures, defaults, journal, voiceId);
|
|
2156
3008
|
} catch (err) {
|
|
2157
3009
|
routeError(req, res, err, pathname);
|
|
2158
3010
|
}
|
|
2159
3011
|
return;
|
|
2160
3012
|
}
|
|
2161
|
-
const musicMatch = pathname.match(ELEVENLABS_MUSIC_RE);
|
|
3013
|
+
const musicMatch = pathname.match(require_route_registry.ELEVENLABS_MUSIC_RE);
|
|
2162
3014
|
if (musicMatch && req.method === "POST") {
|
|
2163
3015
|
setCorsHeaders(res);
|
|
2164
3016
|
const musicSubType = musicMatch[1] ?? "music";
|
|
2165
3017
|
try {
|
|
2166
|
-
|
|
3018
|
+
const raw = await require_helpers.readBody(req);
|
|
3019
|
+
ensureRawFixturePositions();
|
|
3020
|
+
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, musicSubType);
|
|
2167
3021
|
} catch (err) {
|
|
2168
3022
|
routeError(req, res, err, pathname);
|
|
2169
3023
|
}
|
|
2170
3024
|
return;
|
|
2171
3025
|
}
|
|
2172
3026
|
let falBody;
|
|
2173
|
-
if (
|
|
3027
|
+
if (require_route_registry.FAL_ROUTE_RE.test(pathname) && req.headers["x-fal-target-host"]) {
|
|
2174
3028
|
setCorsHeaders(res);
|
|
2175
3029
|
try {
|
|
2176
3030
|
falBody = req.method === "POST" || req.method === "PUT" ? await require_helpers.readBody(req) : "";
|
|
@@ -2194,22 +3048,25 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2194
3048
|
return;
|
|
2195
3049
|
}
|
|
2196
3050
|
}
|
|
3051
|
+
ensureRawFixturePositions();
|
|
2197
3052
|
if (await require_fal.handleFal(req, res, raw, pathname, fixtures, defaults, journal) === "handled") return;
|
|
2198
3053
|
} catch (err) {
|
|
2199
3054
|
routeError(req, res, err, pathname);
|
|
2200
3055
|
return;
|
|
2201
3056
|
}
|
|
2202
3057
|
}
|
|
2203
|
-
if (pathname.match(FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
|
|
3058
|
+
if (pathname.match(require_route_registry.FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
|
|
2204
3059
|
setCorsHeaders(res);
|
|
2205
3060
|
try {
|
|
2206
|
-
|
|
3061
|
+
const raw = falBody ?? await require_helpers.readBody(req);
|
|
3062
|
+
ensureRawFixturePositions();
|
|
3063
|
+
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2207
3064
|
} catch (err) {
|
|
2208
3065
|
routeError(req, res, err, pathname);
|
|
2209
3066
|
}
|
|
2210
3067
|
return;
|
|
2211
3068
|
}
|
|
2212
|
-
if (pathname.match(FAL_QUEUE_REQUESTS_RE) && (req.method === "GET" || req.method === "POST" || req.method === "PUT")) {
|
|
3069
|
+
if (pathname.match(require_route_registry.FAL_QUEUE_REQUESTS_RE) && (req.method === "GET" || req.method === "POST" || req.method === "PUT")) {
|
|
2213
3070
|
setCorsHeaders(res);
|
|
2214
3071
|
try {
|
|
2215
3072
|
const raw = req.method === "POST" || req.method === "PUT" ? falBody ?? await require_helpers.readBody(req) : "{}";
|
|
@@ -2230,22 +3087,27 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2230
3087
|
}, "internal", defaults.registry, defaults.logger);
|
|
2231
3088
|
return;
|
|
2232
3089
|
}
|
|
3090
|
+
ensureRawFixturePositions();
|
|
2233
3091
|
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2234
3092
|
} catch (err) {
|
|
2235
3093
|
routeError(req, res, err, pathname);
|
|
2236
3094
|
}
|
|
2237
3095
|
return;
|
|
2238
3096
|
}
|
|
2239
|
-
if (pathname.match(FAL_RUN_RE) && req.method === "POST") {
|
|
3097
|
+
if (pathname.match(require_route_registry.FAL_RUN_RE) && req.method === "POST") {
|
|
2240
3098
|
setCorsHeaders(res);
|
|
2241
3099
|
try {
|
|
2242
|
-
|
|
3100
|
+
const raw = falBody ?? await require_helpers.readBody(req);
|
|
3101
|
+
ensureRawFixturePositions();
|
|
3102
|
+
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2243
3103
|
} catch (err) {
|
|
2244
3104
|
routeError(req, res, err, pathname);
|
|
2245
3105
|
}
|
|
2246
3106
|
return;
|
|
2247
3107
|
}
|
|
2248
|
-
if (pathname !== COMPLETIONS_PATH) {
|
|
3108
|
+
if (pathname !== require_route_registry.COMPLETIONS_PATH) {
|
|
3109
|
+
const catalogMatch = require_route_registry.matchRouteDefinition(req.method ?? "", pathname);
|
|
3110
|
+
if (catalogMatch && !(pathname === require_route_registry.METRICS_PATH && !registry)) defaults.logger.warn(`Route drift: ${req.method ?? "?"} ${pathname} matches the route catalog (${catalogMatch.method} ${catalogMatch.path} — ${catalogMatch.description}) but no dispatcher branch served it. The catalog entry or the dispatcher is stale — see route-registry.ts.`);
|
|
2249
3111
|
handleNotFound(res, "Not found");
|
|
2250
3112
|
return;
|
|
2251
3113
|
}
|
|
@@ -2255,13 +3117,14 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2255
3117
|
}
|
|
2256
3118
|
const completionsProvider = azureDeploymentId ? "azure" : isOpenRouter ? "openrouter" : isBytePlusArk ? "byteplus" : "openai";
|
|
2257
3119
|
try {
|
|
2258
|
-
await handleCompletions(req, res, fixtures, journal, defaults, azureDeploymentId, completionsProvider, isOpenRouter);
|
|
3120
|
+
await handleCompletions(req, res, fixtures, journal, defaults, () => lastJournalEntryFor(req), azureDeploymentId, completionsProvider, isOpenRouter, ensureRawFixturePositions, originalPathname.startsWith("/api/v3/"));
|
|
2259
3121
|
} catch (err) {
|
|
2260
3122
|
routeError(req, res, err, pathname, {
|
|
2261
3123
|
envelope: (m, status) => isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(status, m) : JSON.stringify({ error: {
|
|
2262
3124
|
message: m,
|
|
2263
3125
|
type: errorTypeForStatus(status)
|
|
2264
3126
|
} }),
|
|
3127
|
+
...isOpenRouter ? { toolCallEnvelope: (m, code) => require_openrouter_chat.serializeOpenRouterError(500, m, { reason: code }) } : {},
|
|
2265
3128
|
streamEvent: (m, status) => `data: ${isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(status, m) : JSON.stringify({ error: {
|
|
2266
3129
|
message: m,
|
|
2267
3130
|
type: errorTypeForStatus(status)
|
|
@@ -2325,26 +3188,51 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2325
3188
|
server.emit("connection", socket);
|
|
2326
3189
|
return;
|
|
2327
3190
|
}
|
|
2328
|
-
|
|
3191
|
+
let parsedUrl;
|
|
3192
|
+
try {
|
|
3193
|
+
parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
3194
|
+
} catch (err) {
|
|
3195
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
3196
|
+
defaults.logger.warn(`Unhandled upgrade error: ${msg}`);
|
|
3197
|
+
socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
2329
3200
|
let pathname = parsedUrl.pathname;
|
|
2330
3201
|
if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
2331
3202
|
require_api_key_auth.writeApiKeyUpgradeRejection(socket);
|
|
2332
3203
|
return;
|
|
2333
3204
|
}
|
|
2334
|
-
if (
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
if (await handler.handleUpgrade(socket, head, subPath)) return;
|
|
2338
|
-
}
|
|
3205
|
+
for (const { path: mountPath, handler } of mountList) if ((pathname === mountPath || pathname.startsWith(mountPath + "/")) && handler.handleUpgrade) {
|
|
3206
|
+
const subPath = pathname.slice(mountPath.length) || "/";
|
|
3207
|
+
if (await handler.handleUpgrade(socket, head, subPath)) return;
|
|
2339
3208
|
}
|
|
2340
|
-
if (!pathname.match(AZURE_DEPLOYMENT_RE)) pathname = normalizeCompatPath(pathname, logger);
|
|
2341
|
-
if (pathname !== RESPONSES_PATH && pathname !== REALTIME_PATH && pathname !== GEMINI_LIVE_PATH && pathname !== LIVE_PATH) {
|
|
3209
|
+
if (!pathname.match(require_route_registry.AZURE_DEPLOYMENT_RE)) pathname = normalizeCompatPath(pathname, logger);
|
|
3210
|
+
if (pathname !== require_route_registry.RESPONSES_PATH && pathname !== require_route_registry.REALTIME_PATH && pathname !== require_route_registry.GEMINI_LIVE_PATH && pathname !== require_route_registry.LIVE_PATH) {
|
|
2342
3211
|
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
2343
3212
|
socket.destroy();
|
|
2344
3213
|
return;
|
|
2345
3214
|
}
|
|
3215
|
+
if (defaults.misbehavior && pathname !== require_route_registry.LIVE_PATH && req.headers["x-aimock-misbehavior"] !== void 0) {
|
|
3216
|
+
const code = "aimock_misbehavior_invalid";
|
|
3217
|
+
const message = "use the runtime scope: POST /__aimock/misbehavior with X-Test-Id";
|
|
3218
|
+
const body = JSON.stringify({ error: pathname === require_route_registry.GEMINI_LIVE_PATH ? {
|
|
3219
|
+
code: 400,
|
|
3220
|
+
status: "INVALID_ARGUMENT",
|
|
3221
|
+
message: `${code}: ${message}`
|
|
3222
|
+
} : {
|
|
3223
|
+
type: "invalid_request_error",
|
|
3224
|
+
code,
|
|
3225
|
+
message
|
|
3226
|
+
} });
|
|
3227
|
+
socket.end(`HTTP/1.1 400 Bad Request\r
|
|
3228
|
+
Content-Type: application/json\r
|
|
3229
|
+
Content-Length: ${Buffer.byteLength(body)}\r\nConnection: close\r
|
|
3230
|
+
\r
|
|
3231
|
+
` + body);
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
2346
3234
|
let liveId;
|
|
2347
|
-
if (pathname === LIVE_PATH) {
|
|
3235
|
+
if (pathname === require_route_registry.LIVE_PATH) {
|
|
2348
3236
|
if (req.method !== "GET" || parsedUrl.searchParams.has("model")) {
|
|
2349
3237
|
socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
|
2350
3238
|
return;
|
|
@@ -2366,7 +3254,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2366
3254
|
if (head.length > 0) socket.unshift(head);
|
|
2367
3255
|
let ws;
|
|
2368
3256
|
try {
|
|
2369
|
-
ws = require_ws_framing.upgradeToWebSocket(req, socket, pathname === LIVE_PATH ? liveLimits : void 0);
|
|
3257
|
+
ws = require_ws_framing.upgradeToWebSocket(req, socket, pathname === require_route_registry.LIVE_PATH ? liveLimits : void 0);
|
|
2370
3258
|
} catch (err) {
|
|
2371
3259
|
const msg = err instanceof Error ? err.message : "WebSocket upgrade failed";
|
|
2372
3260
|
if (liveId !== void 0) liveSessions.delete(liveId);
|
|
@@ -2383,7 +3271,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2383
3271
|
activeConnections.delete(ws);
|
|
2384
3272
|
});
|
|
2385
3273
|
const wsTestId = require_helpers.getTestId(req);
|
|
2386
|
-
if (pathname === LIVE_PATH && liveId !== void 0) {
|
|
3274
|
+
if (pathname === require_route_registry.LIVE_PATH && liveId !== void 0) {
|
|
2387
3275
|
const sessionId = liveId;
|
|
2388
3276
|
const dispose = require_ws_live.handleLiveSession(ws, fixtures, journal, {
|
|
2389
3277
|
defaults,
|
|
@@ -2400,13 +3288,14 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2400
3288
|
testId: wsTestId,
|
|
2401
3289
|
dispose
|
|
2402
3290
|
});
|
|
2403
|
-
} else if (pathname === RESPONSES_PATH) require_ws_responses.handleWebSocketResponses(ws, fixtures, journal, {
|
|
3291
|
+
} else if (pathname === require_route_registry.RESPONSES_PATH) require_ws_responses.handleWebSocketResponses(ws, fixtures, journal, {
|
|
2404
3292
|
...defaults,
|
|
2405
3293
|
model: "gpt-4",
|
|
2406
3294
|
testId: wsTestId,
|
|
2407
|
-
upgradeHeaders: req.headers
|
|
3295
|
+
upgradeHeaders: req.headers,
|
|
3296
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2408
3297
|
});
|
|
2409
|
-
else if (pathname === REALTIME_PATH) {
|
|
3298
|
+
else if (pathname === require_route_registry.REALTIME_PATH) {
|
|
2410
3299
|
const transcriptionIntent = parsedUrl.searchParams.get("intent") === "transcription";
|
|
2411
3300
|
const model = transcriptionIntent ? "gpt-transcribe" : parsedUrl.searchParams.get("model") ?? "gpt-realtime-2";
|
|
2412
3301
|
require_ws_realtime.handleWebSocketRealtime(ws, fixtures, journal, {
|
|
@@ -2414,13 +3303,15 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2414
3303
|
model,
|
|
2415
3304
|
transcriptionIntent,
|
|
2416
3305
|
testId: wsTestId,
|
|
2417
|
-
upgradeHeaders: req.headers
|
|
3306
|
+
upgradeHeaders: req.headers,
|
|
3307
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2418
3308
|
});
|
|
2419
|
-
} else if (pathname === GEMINI_LIVE_PATH) require_ws_gemini_live.handleWebSocketGeminiLive(ws, fixtures, journal, {
|
|
3309
|
+
} else if (pathname === require_route_registry.GEMINI_LIVE_PATH) require_ws_gemini_live.handleWebSocketGeminiLive(ws, fixtures, journal, {
|
|
2420
3310
|
...defaults,
|
|
2421
3311
|
model: "gemini-2.0-flash",
|
|
2422
3312
|
testId: wsTestId,
|
|
2423
|
-
upgradeHeaders: req.headers
|
|
3313
|
+
upgradeHeaders: req.headers,
|
|
3314
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2424
3315
|
});
|
|
2425
3316
|
}
|
|
2426
3317
|
const originalClose = server.close.bind(server);
|
|
@@ -2445,9 +3336,13 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2445
3336
|
return;
|
|
2446
3337
|
}
|
|
2447
3338
|
const url = `http://${addr.address}:${addr.port}`;
|
|
2448
|
-
|
|
2449
|
-
|
|
3339
|
+
try {
|
|
3340
|
+
commitHandOff();
|
|
3341
|
+
} catch (err) {
|
|
3342
|
+
server.close(() => reject(err));
|
|
3343
|
+
return;
|
|
2450
3344
|
}
|
|
3345
|
+
for (const { path: mountPath, handler } of mountList) if (handler.setBaseUrl) handler.setBaseUrl(url + mountPath);
|
|
2451
3346
|
resolve({
|
|
2452
3347
|
closeLiveSessions,
|
|
2453
3348
|
server,
|