@copilotkit/aimock 1.44.0 → 1.45.1
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 +42 -0
- package/README.md +6 -0
- package/dist/agui-types.d.cts.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 +312 -39
- 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 +313 -40
- 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 +1 -0
- package/dist/chaos.js +1 -1
- package/dist/cli.cjs +152 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +155 -8
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +132 -71
- 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 +133 -72
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +62 -4
- 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 +62 -4
- package/dist/config-loader.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/fixture-loader-services.cjs +1 -1
- package/dist/fixture-loader-services.cjs.map +1 -1
- package/dist/fixture-loader-services.js +1 -1
- package/dist/fixture-loader-services.js.map +1 -1
- package/dist/fixture-loader.cjs +389 -27
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +47 -3
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +47 -3
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +384 -29
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +250 -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 +251 -53
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +226 -37
- 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 +227 -38
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +881 -7
- 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 +839 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -3
- package/dist/jest.cjs +31 -7
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +43 -3
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +43 -3
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +33 -9
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +135 -13
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +38 -4
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +38 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +135 -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 +123 -30
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +16 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +16 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +125 -32
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +1 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -1
- package/dist/mcp-fakes-mount.js +1 -0
- package/dist/mcp-fakes-mount.js.map +1 -1
- 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 +331 -8
- package/dist/mcp-fakes.cjs.map +1 -1
- package/dist/mcp-fakes.d.cts +406 -3
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/mcp-fakes.d.ts +406 -3
- package/dist/mcp-fakes.d.ts.map +1 -1
- package/dist/mcp-fakes.js +331 -8
- package/dist/mcp-fakes.js.map +1 -1
- package/dist/mcp-handler.cjs +216 -17
- 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 +214 -18
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +112 -1
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +33 -2
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +33 -2
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +112 -1
- 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/messages.cjs +362 -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 +363 -58
- package/dist/messages.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 +310 -112
- 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 +311 -113
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +30 -2
- 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 +30 -2
- 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 +106 -5
- 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 +107 -7
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +667 -171
- 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 +665 -174
- package/dist/responses.js.map +1 -1
- 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 +572 -76
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +574 -78
- package/dist/server.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/types.cjs.map +1 -1
- package/dist/types.d.cts +315 -4
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +315 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-cli.cjs +36 -4
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +37 -5
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-types.d.ts.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/vitest.cjs +66 -10
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +35 -4
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +35 -4
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +67 -11
- 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 +109 -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 +111 -18
- package/dist/ws-responses.js.map +1 -1
- package/package.json +10 -2
- package/skills/write-fixtures/SKILL.md +179 -21
package/dist/server.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const require_helpers = require('./helpers.cjs');
|
|
|
6
6
|
const require_live_fixture = require('./live-fixture.cjs');
|
|
7
7
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
8
8
|
const require_chaos = require('./chaos.cjs');
|
|
9
|
+
const require_misbehavior = require('./misbehavior.cjs');
|
|
9
10
|
const require_fixture_loader = require('./fixture-loader.cjs');
|
|
10
11
|
const require_router = require('./router.cjs');
|
|
11
12
|
const require_recorder = require('./recorder.cjs');
|
|
@@ -53,6 +54,7 @@ const require_metrics = require('./metrics.cjs');
|
|
|
53
54
|
const require_mcp_fakes = require('./mcp-fakes.cjs');
|
|
54
55
|
const require_mcp_mock = require('./mcp-mock.cjs');
|
|
55
56
|
const require_mcp_fakes_mount = require('./mcp-fakes-mount.cjs');
|
|
57
|
+
const require_mcp_fakes_report = require('./mcp-fakes-report.cjs');
|
|
56
58
|
let node_http = require("node:http");
|
|
57
59
|
node_http = require_runtime.__toESM(node_http);
|
|
58
60
|
let node_async_hooks = require("node:async_hooks");
|
|
@@ -161,6 +163,7 @@ function performFullReset(fixtures, targets) {
|
|
|
161
163
|
targets.journal.clear();
|
|
162
164
|
require_chaos.resetChaosWarnings(targets.defaults.logger);
|
|
163
165
|
targets.defaults.chaos = void 0;
|
|
166
|
+
targets.defaults.misbehavior?.byTestId.clear();
|
|
164
167
|
targets.videoStates.clear();
|
|
165
168
|
targets.openRouterVideoJobs.clear();
|
|
166
169
|
targets.veoVideoJobs.clear();
|
|
@@ -206,7 +209,7 @@ function fixtureResponseKind(response) {
|
|
|
206
209
|
if (require_helpers.isImageResponse(response)) return "image";
|
|
207
210
|
if (require_helpers.isEmbeddingResponse(response)) return "embedding";
|
|
208
211
|
if (require_helpers.isJSONResponse(response)) return "json";
|
|
209
|
-
if (require_helpers.
|
|
212
|
+
if (require_helpers.isCombinedFixtureResponse(response)) return "contentWithToolCalls";
|
|
210
213
|
if (require_helpers.isToolCallResponse(response)) return "toolCalls";
|
|
211
214
|
if (require_helpers.isTextResponse(response)) return "text";
|
|
212
215
|
return "unknown";
|
|
@@ -370,7 +373,8 @@ function planControlApiFakes(raw, mounts) {
|
|
|
370
373
|
const target = require_mcp_fakes_mount.ensureFakeMount(mounts, group.path, {
|
|
371
374
|
journal: wiring.journal,
|
|
372
375
|
registry: wiring.defaults.registry,
|
|
373
|
-
logger: wiring.defaults.logger
|
|
376
|
+
logger: wiring.defaults.logger,
|
|
377
|
+
replaySpeed: wiring.defaults.replaySpeed
|
|
374
378
|
}, wiring.defaults.logger);
|
|
375
379
|
if ("conflict" in target) continue;
|
|
376
380
|
const result = target.handler.addMcpFakes?.(group.sources, { kind: "control-api" });
|
|
@@ -421,6 +425,7 @@ function listControlApiFakes(mounts, testId, context, mount) {
|
|
|
421
425
|
}] : []);
|
|
422
426
|
return mount !== null && listed.length === 0 ? null : listed;
|
|
423
427
|
}
|
|
428
|
+
const controlFixtureAdditions = /* @__PURE__ */ new WeakMap();
|
|
424
429
|
/**
|
|
425
430
|
* Handle requests under `/__aimock/`. Returns `true` if the request was
|
|
426
431
|
* handled, `false` if the path doesn't match the control prefix.
|
|
@@ -492,7 +497,7 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
492
497
|
"Content-Type": "application/json",
|
|
493
498
|
"X-Total-Count": String(total)
|
|
494
499
|
});
|
|
495
|
-
res.end(
|
|
500
|
+
res.end(journalJson(entries, journal));
|
|
496
501
|
return true;
|
|
497
502
|
}
|
|
498
503
|
if (subPath === "/fixtures" && req.method === "GET") {
|
|
@@ -512,19 +517,70 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
512
517
|
res.end(JSON.stringify({ error: "Invalid 'include': expected 'fixtures'" }));
|
|
513
518
|
return true;
|
|
514
519
|
}
|
|
520
|
+
const extendedTools = require_helpers.isExtendedResponsesToolsList(fixtures);
|
|
515
521
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
516
522
|
res.end(JSON.stringify({
|
|
517
523
|
count: fixtures.length,
|
|
518
524
|
fixtures: fixtures.map((fixture, index) => ({
|
|
519
525
|
index,
|
|
520
526
|
match: redactForInspection(fixture.match),
|
|
521
|
-
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)),
|
|
522
528
|
...fixture.latency !== void 0 ? { latency: fixture.latency } : {},
|
|
523
529
|
...fixture.chaos !== void 0 ? { chaos: fixture.chaos } : {}
|
|
524
530
|
}))
|
|
525
531
|
}));
|
|
526
532
|
return true;
|
|
527
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
|
+
}
|
|
528
584
|
if (subPath === "/chaos" && req.method === "GET") {
|
|
529
585
|
const scopeId = chaosScopeId(req);
|
|
530
586
|
if (scopeId === null) return writeBlankTestId(res);
|
|
@@ -615,6 +671,7 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
615
671
|
mounts: listed
|
|
616
672
|
});
|
|
617
673
|
}
|
|
674
|
+
if (subPath === "/mcp/fakes/report" && req.method === "GET") return require_mcp_fakes_report.handleFakesReportRoute(res, searchParams, mounts);
|
|
618
675
|
if (subPath === "/fixtures" && req.method === "POST") {
|
|
619
676
|
let raw;
|
|
620
677
|
try {
|
|
@@ -655,8 +712,25 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
655
712
|
res.end(JSON.stringify({ error: `Fixture at index ${invalidMatchIndex}: match must be an object` }));
|
|
656
713
|
return true;
|
|
657
714
|
}
|
|
658
|
-
const
|
|
659
|
-
const
|
|
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");
|
|
660
734
|
const fakes = withFakes ? planControlApiFakes(parsed.mcpFakes, mounts) : null;
|
|
661
735
|
const fakeErrors = fakes?.errors ?? [];
|
|
662
736
|
const validationFailed = (details) => {
|
|
@@ -667,7 +741,11 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
667
741
|
}));
|
|
668
742
|
return true;
|
|
669
743
|
};
|
|
670
|
-
if (errors.length > 0 || fakeErrors.length > 0) return validationFailed([
|
|
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
|
+
]);
|
|
671
749
|
let mcpFakesAdded = 0;
|
|
672
750
|
if (fakes) try {
|
|
673
751
|
mcpFakesAdded = fakes.apply({
|
|
@@ -689,6 +767,7 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
689
767
|
}
|
|
690
768
|
if (subPath === "/fixtures" && req.method === "DELETE") {
|
|
691
769
|
require_fixture_loader.clearFixtureQueue(fixtures);
|
|
770
|
+
journal.clearMisbehaviorCounters();
|
|
692
771
|
for (const { handler } of mounts) handler.clearMcpFakes?.();
|
|
693
772
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
694
773
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -805,8 +884,9 @@ function clientAbortedMidBody(req, res, err) {
|
|
|
805
884
|
if (err instanceof require_helpers.RequestBodyTooLargeError) return false;
|
|
806
885
|
return res.destroyed && !req.complete;
|
|
807
886
|
}
|
|
808
|
-
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) {
|
|
809
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";
|
|
810
890
|
let raw;
|
|
811
891
|
try {
|
|
812
892
|
raw = await require_helpers.readBody(req);
|
|
@@ -972,6 +1052,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
972
1052
|
const openRouterFallback = openRouter && Array.isArray(body.models) && body.models.length > 0;
|
|
973
1053
|
const selectFixture = (probe, counts) => {
|
|
974
1054
|
for (;;) {
|
|
1055
|
+
beforeFixtureSelection?.();
|
|
975
1056
|
const attempt = require_router.matchFixtureDiagnostic(fixtures, probe, counts, defaults.requestTransform, matchOptions);
|
|
976
1057
|
if (attempt.fixture && require_fixture_loader.isOneShotError(attempt.fixture) && !require_fixture_loader.claimOneShotError(fixtures, attempt.fixture)) continue;
|
|
977
1058
|
return attempt;
|
|
@@ -1055,13 +1136,32 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1055
1136
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1056
1137
|
const missWouldProxy = require_helpers.wouldProxyMiss(effectiveStrict, defaults.record, providerKey);
|
|
1057
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
|
+
};
|
|
1058
1156
|
if (chaosAction === "drop" || chaosAction === "disconnect" || chaosAction === "rateLimit") {
|
|
1059
1157
|
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, fixture ? "fixture" : noFixtureSource, defaults.registry, defaults.logger);
|
|
1158
|
+
recordBypass("chaos-fired");
|
|
1060
1159
|
require_fixture_loader.releaseOneShotError(fixtures, fixture);
|
|
1061
1160
|
return;
|
|
1062
1161
|
}
|
|
1063
1162
|
if (chaosAction === "malformed" && (fixture || !missWouldProxy)) {
|
|
1064
1163
|
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, fixture ? "fixture" : "internal", defaults.registry, defaults.logger);
|
|
1164
|
+
recordBypass("chaos-fired");
|
|
1065
1165
|
require_fixture_loader.releaseOneShotError(fixtures, fixture);
|
|
1066
1166
|
return;
|
|
1067
1167
|
}
|
|
@@ -1093,6 +1193,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1093
1193
|
const hookOptions = chaosAction === "malformed" ? {
|
|
1094
1194
|
beforeWriteResponse: () => {
|
|
1095
1195
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, chaosContext, "proxy", defaults.registry, defaults.logger);
|
|
1196
|
+
recordBypass("chaos-fired");
|
|
1096
1197
|
return true;
|
|
1097
1198
|
},
|
|
1098
1199
|
onHookBypassed: (reason) => {
|
|
@@ -1135,10 +1236,12 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1135
1236
|
} : {}
|
|
1136
1237
|
}
|
|
1137
1238
|
});
|
|
1239
|
+
recordBypass("proxied");
|
|
1138
1240
|
return;
|
|
1139
1241
|
}
|
|
1140
1242
|
if (chaosAction === "malformed") {
|
|
1141
1243
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, chaosContext, "internal", defaults.registry, defaults.logger);
|
|
1244
|
+
recordBypass("chaos-fired");
|
|
1142
1245
|
return;
|
|
1143
1246
|
}
|
|
1144
1247
|
}
|
|
@@ -1161,13 +1264,71 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1161
1264
|
} }));
|
|
1162
1265
|
return;
|
|
1163
1266
|
}
|
|
1164
|
-
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;
|
|
1165
1316
|
const latency = fixture.latency ?? defaults.latency;
|
|
1166
1317
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
1167
1318
|
const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
|
|
1168
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("");
|
|
1169
|
-
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
|
+
};
|
|
1170
1330
|
const shapeORCompletion = (completion, overrides) => {
|
|
1331
|
+
if (appliedUsage) completion.usage = appliedUsage;
|
|
1171
1332
|
if (!openRouter) return completion;
|
|
1172
1333
|
return require_openrouter_chat.shapeOpenRouterCompletion(completion, require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel), overrides?.id !== void 0);
|
|
1173
1334
|
};
|
|
@@ -1180,7 +1341,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1180
1341
|
const openRouterProcessing = !!(openRouter && fixture.openRouterProcessing);
|
|
1181
1342
|
if (require_helpers.isErrorResponse(response)) {
|
|
1182
1343
|
const status = response.status ?? 500;
|
|
1183
|
-
journal.add({
|
|
1344
|
+
recordOutcome(journal.add({
|
|
1184
1345
|
method: req.method ?? "POST",
|
|
1185
1346
|
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1186
1347
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -1189,12 +1350,12 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1189
1350
|
status,
|
|
1190
1351
|
fixture
|
|
1191
1352
|
}
|
|
1192
|
-
});
|
|
1353
|
+
}));
|
|
1193
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 });
|
|
1194
1355
|
return;
|
|
1195
1356
|
}
|
|
1196
1357
|
if (require_helpers.isAudioResponse(response)) {
|
|
1197
|
-
journal.add({
|
|
1358
|
+
recordOutcome(journal.add({
|
|
1198
1359
|
method: req.method ?? "POST",
|
|
1199
1360
|
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1200
1361
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -1203,18 +1364,58 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1203
1364
|
status: 422,
|
|
1204
1365
|
fixture
|
|
1205
1366
|
}
|
|
1206
|
-
});
|
|
1367
|
+
}));
|
|
1207
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: {
|
|
1208
1369
|
message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
|
|
1209
1370
|
type: "invalid_request_error"
|
|
1210
1371
|
} }));
|
|
1211
1372
|
return;
|
|
1212
1373
|
}
|
|
1213
|
-
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)) {
|
|
1214
1415
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1215
1416
|
const overrides = require_helpers.extractOverrides(response);
|
|
1216
1417
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1217
|
-
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);
|
|
1218
1419
|
const streaming = body.stream === true;
|
|
1219
1420
|
const blockOutcome = response.blocks && response.blocks.length > 0 ? require_helpers.resolveFixtureBlockOutcome(response.blocks) : void 0;
|
|
1220
1421
|
const journalEntry = journal.add({
|
|
@@ -1227,16 +1428,18 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1227
1428
|
fixture
|
|
1228
1429
|
}
|
|
1229
1430
|
});
|
|
1431
|
+
recordOutcome(journalEntry);
|
|
1432
|
+
const toolCalls = require_helpers.requireEmittedFunctionToolCalls(response, wire);
|
|
1230
1433
|
if (!streaming) {
|
|
1231
|
-
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 ? {
|
|
1232
1435
|
...overrides,
|
|
1233
1436
|
finishReason: overrides?.finishReason ?? (blockOutcome.hasToolCalls ? "tool_calls" : "stop")
|
|
1234
1437
|
} : overrides, body.messages);
|
|
1235
1438
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1236
1439
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1237
1440
|
} else {
|
|
1238
|
-
const chunks = require_helpers.buildContentWithToolCallsChunks(blockOutcome?.content ?? response.content ?? "", blockOutcome?.toolCalls ??
|
|
1239
|
-
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("");
|
|
1240
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;
|
|
1241
1444
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1242
1445
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
@@ -1246,7 +1449,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1246
1449
|
signal: interruption?.signal,
|
|
1247
1450
|
onChunkSent: interruption?.tick,
|
|
1248
1451
|
usageChunk,
|
|
1249
|
-
recordedTimings:
|
|
1452
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1250
1453
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1251
1454
|
openRouterProcessing
|
|
1252
1455
|
})) {
|
|
@@ -1262,7 +1465,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1262
1465
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1263
1466
|
const overrides = require_helpers.extractOverrides(response);
|
|
1264
1467
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1265
|
-
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);
|
|
1266
1469
|
const journalEntry = journal.add({
|
|
1267
1470
|
method: req.method ?? "POST",
|
|
1268
1471
|
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
@@ -1273,6 +1476,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1273
1476
|
fixture
|
|
1274
1477
|
}
|
|
1275
1478
|
});
|
|
1479
|
+
recordOutcome(journalEntry);
|
|
1276
1480
|
if (body.stream !== true) {
|
|
1277
1481
|
const completion = require_helpers.buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
|
|
1278
1482
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -1288,7 +1492,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1288
1492
|
signal: interruption?.signal,
|
|
1289
1493
|
onChunkSent: interruption?.tick,
|
|
1290
1494
|
usageChunk,
|
|
1291
|
-
recordedTimings:
|
|
1495
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1292
1496
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1293
1497
|
openRouterProcessing
|
|
1294
1498
|
})) {
|
|
@@ -1304,7 +1508,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1304
1508
|
if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
|
|
1305
1509
|
const overrides = require_helpers.extractOverrides(response);
|
|
1306
1510
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1307
|
-
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);
|
|
1308
1512
|
const journalEntry = journal.add({
|
|
1309
1513
|
method: req.method ?? "POST",
|
|
1310
1514
|
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
@@ -1315,13 +1519,15 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1315
1519
|
fixture
|
|
1316
1520
|
}
|
|
1317
1521
|
});
|
|
1522
|
+
recordOutcome(journalEntry);
|
|
1523
|
+
const toolCalls = require_helpers.requireServedFunctionToolCalls(response, wire);
|
|
1318
1524
|
if (body.stream !== true) {
|
|
1319
|
-
const completion = require_helpers.buildToolCallCompletion(
|
|
1525
|
+
const completion = require_helpers.buildToolCallCompletion(toolCalls, responseModel, effReasoning, overrides, body.messages);
|
|
1320
1526
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1321
1527
|
res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
|
|
1322
1528
|
} else {
|
|
1323
|
-
const chunks = require_helpers.buildToolCallChunks(
|
|
1324
|
-
const completionText =
|
|
1529
|
+
const chunks = require_helpers.buildToolCallChunks(toolCalls, responseModel, chunkSize, effReasoning, overrides);
|
|
1530
|
+
const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
|
|
1325
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;
|
|
1326
1532
|
shapeORChunks(chunks, usageChunk, overrides);
|
|
1327
1533
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
@@ -1331,7 +1537,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1331
1537
|
signal: interruption?.signal,
|
|
1332
1538
|
onChunkSent: interruption?.tick,
|
|
1333
1539
|
usageChunk,
|
|
1334
|
-
recordedTimings:
|
|
1540
|
+
recordedTimings: resolveRecordedTimings(chunks),
|
|
1335
1541
|
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1336
1542
|
openRouterProcessing
|
|
1337
1543
|
})) {
|
|
@@ -1343,7 +1549,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1343
1549
|
}
|
|
1344
1550
|
return;
|
|
1345
1551
|
}
|
|
1346
|
-
journal.add({
|
|
1552
|
+
recordOutcome(journal.add({
|
|
1347
1553
|
method: req.method ?? "POST",
|
|
1348
1554
|
path: req.url ?? require_route_registry.COMPLETIONS_PATH,
|
|
1349
1555
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -1352,7 +1558,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
1352
1558
|
status: 500,
|
|
1353
1559
|
fixture
|
|
1354
1560
|
}
|
|
1355
|
-
});
|
|
1561
|
+
}));
|
|
1356
1562
|
require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, "Fixture response did not match any known type") : JSON.stringify({ error: {
|
|
1357
1563
|
message: "Fixture response did not match any known type",
|
|
1358
1564
|
type: "server_error"
|
|
@@ -1377,22 +1583,90 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1377
1583
|
}
|
|
1378
1584
|
}
|
|
1379
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
|
+
/**
|
|
1380
1610
|
* The server of `createServerWithResolvedAuth`, after the MCP fakes hand-off.
|
|
1381
1611
|
* `commitHandOff` runs once the server listens; if it throws, the server is
|
|
1382
1612
|
* closed and start rejects.
|
|
1383
1613
|
*/
|
|
1384
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();
|
|
1385
1630
|
const host = options?.host ?? "127.0.0.1";
|
|
1386
1631
|
const port = options?.port ?? 0;
|
|
1387
1632
|
const registry = options?.metrics ? require_metrics.createMetricsRegistry() : void 0;
|
|
1388
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);
|
|
1389
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
|
+
}
|
|
1390
1660
|
const defaults = {
|
|
1391
1661
|
latency: serverOptions.latency ?? 0,
|
|
1392
1662
|
chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),
|
|
1393
1663
|
replaySpeed: serverOptions.replaySpeed ?? 1,
|
|
1394
1664
|
logger,
|
|
1395
1665
|
chaosByTestId,
|
|
1666
|
+
misbehavior: misbehaviorEnabled ? misbehavior : void 0,
|
|
1667
|
+
get misbehaviorCounters() {
|
|
1668
|
+
return journal;
|
|
1669
|
+
},
|
|
1396
1670
|
get chaos() {
|
|
1397
1671
|
return {
|
|
1398
1672
|
base: chaosByTestId.get(require_constants.DEFAULT_TEST_ID) ?? serverOptions.chaos,
|
|
@@ -1415,6 +1689,13 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1415
1689
|
get strict() {
|
|
1416
1690
|
return serverOptions.strict;
|
|
1417
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
|
+
},
|
|
1418
1699
|
get requestTransform() {
|
|
1419
1700
|
return serverOptions.requestTransform;
|
|
1420
1701
|
},
|
|
@@ -1505,6 +1786,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1505
1786
|
if (req) ownJournalEntry.set(req, entry);
|
|
1506
1787
|
}
|
|
1507
1788
|
});
|
|
1789
|
+
if (serverOptions.responsesTools === "extended") journal.configureFixtureViewSource(require_fixture_loader.responsesToolsViewSource);
|
|
1508
1790
|
const videoStates = new require_video.VideoStateMap();
|
|
1509
1791
|
const openRouterVideoJobs = new require_openrouter_video.OpenRouterVideoJobMap();
|
|
1510
1792
|
const veoVideoJobs = new require_veo_video.VeoVideoJobMap();
|
|
@@ -1519,6 +1801,85 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1519
1801
|
return status >= 400 && status < 500 ? "invalid_request_error" : "server_error";
|
|
1520
1802
|
}
|
|
1521
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
|
+
/**
|
|
1522
1883
|
* The newest journal entry this request already produced, or null.
|
|
1523
1884
|
*
|
|
1524
1885
|
* Attribution is by the identity of the `IncomingMessage` the entry was
|
|
@@ -1569,6 +1930,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1569
1930
|
function routeError(req, res, err, pathname, opts) {
|
|
1570
1931
|
const route = `${req.method ?? "?"} ${pathname}`;
|
|
1571
1932
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
1933
|
+
const invalidToolArguments = require_helpers.isFixtureToolCallError(err);
|
|
1572
1934
|
const clientFault = err instanceof require_helpers.RequestBodyTooLargeError;
|
|
1573
1935
|
const clientAbort = clientAbortedMidBody(req, res, err);
|
|
1574
1936
|
const status = clientFault ? 400 : 500;
|
|
@@ -1609,8 +1971,10 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1609
1971
|
return;
|
|
1610
1972
|
}
|
|
1611
1973
|
if (!res.headersSent) {
|
|
1612
|
-
if (existing)
|
|
1613
|
-
|
|
1974
|
+
if (existing) {
|
|
1975
|
+
existing.response.status = status;
|
|
1976
|
+
if (invalidToolArguments) existing.response.error = msg;
|
|
1977
|
+
} else try {
|
|
1614
1978
|
journal.add({
|
|
1615
1979
|
method: req.method ?? "?",
|
|
1616
1980
|
path: req.url ?? "?",
|
|
@@ -1620,14 +1984,15 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1620
1984
|
response: {
|
|
1621
1985
|
status,
|
|
1622
1986
|
fixture: null,
|
|
1623
|
-
source: "internal"
|
|
1987
|
+
source: "internal",
|
|
1988
|
+
...invalidToolArguments ? { error: msg } : {}
|
|
1624
1989
|
}
|
|
1625
1990
|
});
|
|
1626
1991
|
} catch (jErr) {
|
|
1627
1992
|
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
1628
1993
|
}
|
|
1629
1994
|
setCorsHeaders(res);
|
|
1630
|
-
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: {
|
|
1631
1996
|
message: msg,
|
|
1632
1997
|
type: errorTypeForStatus(status)
|
|
1633
1998
|
} }));
|
|
@@ -1676,13 +2041,17 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1676
2041
|
if (handler.setJournal) handler.setJournal(journal);
|
|
1677
2042
|
if (registry && handler.setRegistry) handler.setRegistry(registry);
|
|
1678
2043
|
if (handler.setLogger) handler.setLogger(logger);
|
|
2044
|
+
if (handler instanceof require_mcp_mock.MCPMock) handler.setReplaySpeed(defaults.replaySpeed);
|
|
1679
2045
|
}
|
|
1680
2046
|
if (registry) registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
1681
2047
|
const server = node_http.createServer((req, res) => {
|
|
1682
2048
|
requestScope.run(req, () => {
|
|
1683
|
-
|
|
2049
|
+
require_helpers.runWithToolArgumentsScope({
|
|
2050
|
+
strict: defaults.strictToolArguments === true,
|
|
2051
|
+
logger
|
|
2052
|
+
}, () => handleHttpRequest(req, res).catch((err) => {
|
|
1684
2053
|
routeError(req, res, err, req.url ?? "?");
|
|
1685
|
-
});
|
|
2054
|
+
}));
|
|
1686
2055
|
});
|
|
1687
2056
|
});
|
|
1688
2057
|
async function handleHttpRequest(req, res) {
|
|
@@ -1765,16 +2134,40 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1765
2134
|
require_api_key_auth.markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
1766
2135
|
}
|
|
1767
2136
|
if (pathname.startsWith(require_route_registry.CONTROL_PREFIX)) {
|
|
2137
|
+
ensureRawFixturePositions();
|
|
1768
2138
|
await handleControlAPI(req, res, pathname, parsedUrl.searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults, mountList);
|
|
1769
2139
|
return;
|
|
1770
2140
|
}
|
|
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;
|
|
2161
|
+
}
|
|
1771
2162
|
for (const { path: mountPath, handler } of mountList) if (pathname === mountPath || pathname.startsWith(mountPath + "/")) {
|
|
1772
2163
|
const subPath = pathname.slice(mountPath.length) || "/";
|
|
1773
2164
|
if (await handler.handleRequest(req, res, subPath)) return;
|
|
1774
2165
|
}
|
|
1775
2166
|
if (pathname === require_route_registry.OLLAMA_CHAT_PATH && req.method === "POST") {
|
|
1776
2167
|
try {
|
|
1777
|
-
|
|
2168
|
+
const raw = await require_helpers.readBody(req);
|
|
2169
|
+
ensureRawFixturePositions();
|
|
2170
|
+
await require_ollama.handleOllama(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1778
2171
|
} catch (err) {
|
|
1779
2172
|
routeError(req, res, err, pathname);
|
|
1780
2173
|
}
|
|
@@ -1782,7 +2175,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1782
2175
|
}
|
|
1783
2176
|
if (pathname === require_route_registry.OLLAMA_GENERATE_PATH && req.method === "POST") {
|
|
1784
2177
|
try {
|
|
1785
|
-
|
|
2178
|
+
const raw = await require_helpers.readBody(req);
|
|
2179
|
+
ensureRawFixturePositions();
|
|
2180
|
+
await require_ollama.handleOllamaGenerate(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1786
2181
|
} catch (err) {
|
|
1787
2182
|
routeError(req, res, err, pathname);
|
|
1788
2183
|
}
|
|
@@ -1790,7 +2185,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1790
2185
|
}
|
|
1791
2186
|
if ((pathname === require_route_registry.OLLAMA_EMBEDDINGS_PATH || pathname === require_route_registry.OLLAMA_EMBED_PATH) && req.method === "POST") {
|
|
1792
2187
|
try {
|
|
1793
|
-
|
|
2188
|
+
const raw = await require_helpers.readBody(req);
|
|
2189
|
+
ensureRawFixturePositions();
|
|
2190
|
+
await require_ollama.handleOllamaEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
1794
2191
|
} catch (err) {
|
|
1795
2192
|
routeError(req, res, err, pathname);
|
|
1796
2193
|
}
|
|
@@ -1823,6 +2220,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1823
2220
|
}
|
|
1824
2221
|
if (pathname === require_route_registry.OPENROUTER_VIDEO_MODELS_PATH && req.method === "GET") {
|
|
1825
2222
|
try {
|
|
2223
|
+
ensureRawFixturePositions();
|
|
1826
2224
|
await require_openrouter_video.handleOpenRouterVideoModels(req, res, fixtures, journal, defaults, setCorsHeaders);
|
|
1827
2225
|
} catch (err) {
|
|
1828
2226
|
routeError(req, res, err, pathname);
|
|
@@ -1832,6 +2230,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1832
2230
|
const openRouterVideoStatusMatch = pathname.match(require_route_registry.OPENROUTER_VIDEO_STATUS_RE);
|
|
1833
2231
|
if (openRouterVideoStatusMatch && req.method === "GET") {
|
|
1834
2232
|
try {
|
|
2233
|
+
ensureRawFixturePositions();
|
|
1835
2234
|
await require_openrouter_video.handleOpenRouterVideoStatus(req, res, openRouterVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
|
|
1836
2235
|
} catch (err) {
|
|
1837
2236
|
routeError(req, res, err, pathname);
|
|
@@ -1841,7 +2240,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1841
2240
|
if (pathname === require_route_registry.OPENROUTER_VIDEOS_PATH && req.method === "POST") {
|
|
1842
2241
|
setCorsHeaders(res);
|
|
1843
2242
|
try {
|
|
1844
|
-
|
|
2243
|
+
const raw = await require_helpers.readBody(req);
|
|
2244
|
+
ensureRawFixturePositions();
|
|
2245
|
+
await require_openrouter_video.handleOpenRouterVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, openRouterVideoJobs);
|
|
1845
2246
|
} catch (err) {
|
|
1846
2247
|
routeError(req, res, err, pathname);
|
|
1847
2248
|
}
|
|
@@ -1850,6 +2251,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1850
2251
|
const bytePlusVideoStatusMatch = pathname.match(require_route_registry.BYTEPLUS_VIDEO_STATUS_RE);
|
|
1851
2252
|
if (bytePlusVideoStatusMatch && req.method === "GET") {
|
|
1852
2253
|
try {
|
|
2254
|
+
ensureRawFixturePositions();
|
|
1853
2255
|
await require_byteplus_video.handleBytePlusVideoStatus(req, res, bytePlusVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, bytePlusVideoJobs);
|
|
1854
2256
|
} catch (err) {
|
|
1855
2257
|
routeError(req, res, err, pathname);
|
|
@@ -1859,13 +2261,16 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
1859
2261
|
if (require_route_registry.BYTEPLUS_VIDEO_SUBMIT_RE.test(pathname) && req.method === "POST") {
|
|
1860
2262
|
setCorsHeaders(res);
|
|
1861
2263
|
try {
|
|
1862
|
-
|
|
2264
|
+
const raw = await require_helpers.readBody(req);
|
|
2265
|
+
ensureRawFixturePositions();
|
|
2266
|
+
await require_byteplus_video.handleBytePlusVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, bytePlusVideoJobs);
|
|
1863
2267
|
} catch (err) {
|
|
1864
2268
|
routeError(req, res, err, pathname);
|
|
1865
2269
|
}
|
|
1866
2270
|
return;
|
|
1867
2271
|
}
|
|
1868
2272
|
if (pathname === require_route_registry.OPENROUTER_MODELS_PATH && req.method === "GET") {
|
|
2273
|
+
ensureRawFixturePositions();
|
|
1869
2274
|
require_openrouter_chat.handleOpenRouterModels(req, res, fixtures, journal, defaults, setCorsHeaders);
|
|
1870
2275
|
return;
|
|
1871
2276
|
}
|
|
@@ -2204,7 +2609,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2204
2609
|
}
|
|
2205
2610
|
const entries = journal.getAll(opts);
|
|
2206
2611
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2207
|
-
res.end(
|
|
2612
|
+
res.end(journalJson(entries, journal));
|
|
2208
2613
|
return;
|
|
2209
2614
|
}
|
|
2210
2615
|
if (req.method === "DELETE") {
|
|
@@ -2218,7 +2623,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2218
2623
|
}
|
|
2219
2624
|
if (pathname === require_route_registry.RESPONSES_PATH && req.method === "POST") {
|
|
2220
2625
|
try {
|
|
2221
|
-
|
|
2626
|
+
const raw = await require_helpers.readBody(req);
|
|
2627
|
+
ensureRawFixturePositions();
|
|
2628
|
+
await require_responses.handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2222
2629
|
} catch (err) {
|
|
2223
2630
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2224
2631
|
}
|
|
@@ -2226,7 +2633,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2226
2633
|
}
|
|
2227
2634
|
if (pathname === require_route_registry.MESSAGES_PATH && req.method === "POST") {
|
|
2228
2635
|
try {
|
|
2229
|
-
|
|
2636
|
+
const raw = await require_helpers.readBody(req);
|
|
2637
|
+
ensureRawFixturePositions();
|
|
2638
|
+
await require_messages.handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2230
2639
|
} catch (err) {
|
|
2231
2640
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2232
2641
|
}
|
|
@@ -2234,7 +2643,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2234
2643
|
}
|
|
2235
2644
|
if (pathname === require_route_registry.COHERE_CHAT_PATH && req.method === "POST") {
|
|
2236
2645
|
try {
|
|
2237
|
-
|
|
2646
|
+
const raw = await require_helpers.readBody(req);
|
|
2647
|
+
ensureRawFixturePositions();
|
|
2648
|
+
await require_cohere.handleCohere(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2238
2649
|
} catch (err) {
|
|
2239
2650
|
routeError(req, res, err, pathname, { streamEvent: (m) => `event: error\ndata: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2240
2651
|
}
|
|
@@ -2242,7 +2653,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2242
2653
|
}
|
|
2243
2654
|
if (pathname === require_route_registry.COHERE_EMBED_PATH && req.method === "POST") {
|
|
2244
2655
|
try {
|
|
2245
|
-
|
|
2656
|
+
const raw = await require_helpers.readBody(req);
|
|
2657
|
+
ensureRawFixturePositions();
|
|
2658
|
+
await require_cohere.handleCohereEmbed(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2246
2659
|
} catch (err) {
|
|
2247
2660
|
routeError(req, res, err, pathname);
|
|
2248
2661
|
}
|
|
@@ -2262,6 +2675,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2262
2675
|
} catch (err) {
|
|
2263
2676
|
if (!(err instanceof SyntaxError)) defaults.logger.error(`Unexpected error in Azure model injection: ${err instanceof Error ? err.message : String(err)}`);
|
|
2264
2677
|
}
|
|
2678
|
+
ensureRawFixturePositions();
|
|
2265
2679
|
await require_embeddings.handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider, originalPathname === require_route_registry.EMBEDDINGS_PATH);
|
|
2266
2680
|
} catch (err) {
|
|
2267
2681
|
routeError(req, res, err, pathname);
|
|
@@ -2270,7 +2684,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2270
2684
|
}
|
|
2271
2685
|
if (pathname === require_route_registry.IMAGES_PATH && req.method === "POST") {
|
|
2272
2686
|
try {
|
|
2273
|
-
|
|
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);
|
|
2274
2690
|
} catch (err) {
|
|
2275
2691
|
routeError(req, res, err, pathname);
|
|
2276
2692
|
}
|
|
@@ -2278,7 +2694,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2278
2694
|
}
|
|
2279
2695
|
if (pathname === require_route_registry.IMAGES_EDIT_PATH && req.method === "POST") {
|
|
2280
2696
|
try {
|
|
2281
|
-
|
|
2697
|
+
const raw = await require_helpers.readBody(req);
|
|
2698
|
+
ensureRawFixturePositions();
|
|
2699
|
+
await require_images.handleImageEdit(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2282
2700
|
} catch (err) {
|
|
2283
2701
|
routeError(req, res, err, pathname);
|
|
2284
2702
|
}
|
|
@@ -2286,7 +2704,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2286
2704
|
}
|
|
2287
2705
|
if (pathname === require_route_registry.IMAGES_VARIATIONS_PATH && req.method === "POST") {
|
|
2288
2706
|
try {
|
|
2289
|
-
|
|
2707
|
+
const raw = await require_helpers.readBody(req);
|
|
2708
|
+
ensureRawFixturePositions();
|
|
2709
|
+
await require_images.handleImageVariations(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2290
2710
|
} catch (err) {
|
|
2291
2711
|
routeError(req, res, err, pathname);
|
|
2292
2712
|
}
|
|
@@ -2294,7 +2714,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2294
2714
|
}
|
|
2295
2715
|
if (pathname === require_route_registry.SPEECH_PATH && req.method === "POST") {
|
|
2296
2716
|
try {
|
|
2297
|
-
|
|
2717
|
+
const raw = await require_helpers.readBody(req);
|
|
2718
|
+
ensureRawFixturePositions();
|
|
2719
|
+
await require_speech.handleSpeech(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2298
2720
|
} catch (err) {
|
|
2299
2721
|
routeError(req, res, err, pathname);
|
|
2300
2722
|
}
|
|
@@ -2302,7 +2724,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2302
2724
|
}
|
|
2303
2725
|
if (pathname === require_route_registry.TRANSCRIPTIONS_PATH && req.method === "POST") {
|
|
2304
2726
|
try {
|
|
2305
|
-
|
|
2727
|
+
const raw = await require_helpers.readBody(req);
|
|
2728
|
+
ensureRawFixturePositions();
|
|
2729
|
+
await require_transcription.handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2306
2730
|
} catch (err) {
|
|
2307
2731
|
routeError(req, res, err, pathname);
|
|
2308
2732
|
}
|
|
@@ -2310,7 +2734,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2310
2734
|
}
|
|
2311
2735
|
if (pathname === require_route_registry.TRANSLATIONS_PATH && req.method === "POST") {
|
|
2312
2736
|
try {
|
|
2313
|
-
|
|
2737
|
+
const raw = await require_helpers.readBody(req);
|
|
2738
|
+
ensureRawFixturePositions();
|
|
2739
|
+
await require_transcription.handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders, "translation");
|
|
2314
2740
|
} catch (err) {
|
|
2315
2741
|
routeError(req, res, err, pathname);
|
|
2316
2742
|
}
|
|
@@ -2319,7 +2745,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2319
2745
|
if (pathname === require_route_registry.GROK_VIDEO_SUBMIT_PATH && req.method === "POST") {
|
|
2320
2746
|
setCorsHeaders(res);
|
|
2321
2747
|
try {
|
|
2322
|
-
|
|
2748
|
+
const raw = await require_helpers.readBody(req);
|
|
2749
|
+
ensureRawFixturePositions();
|
|
2750
|
+
await require_grok_video.handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, grokVideoJobs);
|
|
2323
2751
|
} catch (err) {
|
|
2324
2752
|
routeError(req, res, err, pathname);
|
|
2325
2753
|
}
|
|
@@ -2327,7 +2755,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2327
2755
|
}
|
|
2328
2756
|
if (pathname === require_route_registry.VIDEOS_PATH && req.method === "POST") {
|
|
2329
2757
|
try {
|
|
2330
|
-
|
|
2758
|
+
const raw = await require_helpers.readBody(req);
|
|
2759
|
+
ensureRawFixturePositions();
|
|
2760
|
+
await require_video.handleVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, videoStates);
|
|
2331
2761
|
} catch (err) {
|
|
2332
2762
|
routeError(req, res, err, pathname);
|
|
2333
2763
|
}
|
|
@@ -2336,6 +2766,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2336
2766
|
const grokVideoStatusMatch = pathname.match(require_route_registry.GROK_VIDEO_STATUS_RE);
|
|
2337
2767
|
if (grokVideoStatusMatch && grokVideoStatusMatch[1] !== "generations" && req.method === "GET") {
|
|
2338
2768
|
try {
|
|
2769
|
+
ensureRawFixturePositions();
|
|
2339
2770
|
await require_grok_video.handleGrokVideoStatus(req, res, grokVideoStatusMatch[1], fixtures, journal, defaults, setCorsHeaders, grokVideoJobs, videoStates);
|
|
2340
2771
|
} catch (err) {
|
|
2341
2772
|
routeError(req, res, err, pathname);
|
|
@@ -2346,7 +2777,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2346
2777
|
if (veoPredictMatch && req.method === "POST") {
|
|
2347
2778
|
setCorsHeaders(res);
|
|
2348
2779
|
try {
|
|
2349
|
-
|
|
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);
|
|
2350
2783
|
} catch (err) {
|
|
2351
2784
|
routeError(req, res, err, pathname);
|
|
2352
2785
|
}
|
|
@@ -2355,6 +2788,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2355
2788
|
const veoOperationMatch = pathname.match(require_route_registry.VEO_OPERATION_RE);
|
|
2356
2789
|
if (veoOperationMatch && req.method === "GET") {
|
|
2357
2790
|
try {
|
|
2791
|
+
ensureRawFixturePositions();
|
|
2358
2792
|
await require_veo_video.handleVeoVideoStatus(req, res, veoOperationMatch[1], fixtures, journal, defaults, setCorsHeaders, veoVideoJobs);
|
|
2359
2793
|
} catch (err) {
|
|
2360
2794
|
routeError(req, res, err, pathname);
|
|
@@ -2365,7 +2799,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2365
2799
|
if (geminiPredictMatch && req.method === "POST") {
|
|
2366
2800
|
const predictModel = geminiPredictMatch[1];
|
|
2367
2801
|
try {
|
|
2368
|
-
|
|
2802
|
+
const raw = await require_helpers.readBody(req);
|
|
2803
|
+
ensureRawFixturePositions();
|
|
2804
|
+
await require_images.handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders, "gemini", predictModel);
|
|
2369
2805
|
} catch (err) {
|
|
2370
2806
|
routeError(req, res, err, pathname);
|
|
2371
2807
|
}
|
|
@@ -2373,7 +2809,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2373
2809
|
}
|
|
2374
2810
|
if (pathname === require_route_registry.GEMINI_INTERACTIONS_PATH && req.method === "POST") {
|
|
2375
2811
|
try {
|
|
2376
|
-
|
|
2812
|
+
const raw = await require_helpers.readBody(req);
|
|
2813
|
+
ensureRawFixturePositions();
|
|
2814
|
+
await require_gemini_interactions.handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders);
|
|
2377
2815
|
} catch (err) {
|
|
2378
2816
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2379
2817
|
}
|
|
@@ -2383,7 +2821,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2383
2821
|
if (geminiEmbedMatch && req.method === "POST") {
|
|
2384
2822
|
const embedModel = geminiEmbedMatch[1];
|
|
2385
2823
|
try {
|
|
2386
|
-
|
|
2824
|
+
const raw = await require_helpers.readBody(req);
|
|
2825
|
+
ensureRawFixturePositions();
|
|
2826
|
+
await require_gemini_embeddings.handleGeminiEmbedContent(req, res, raw, embedModel, fixtures, journal, defaults, setCorsHeaders);
|
|
2387
2827
|
} catch (err) {
|
|
2388
2828
|
routeError(req, res, err, pathname);
|
|
2389
2829
|
}
|
|
@@ -2394,7 +2834,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2394
2834
|
const geminiModel = geminiMatch[1];
|
|
2395
2835
|
const streaming = geminiMatch[2] === "streamGenerateContent";
|
|
2396
2836
|
try {
|
|
2397
|
-
|
|
2837
|
+
const raw = await require_helpers.readBody(req);
|
|
2838
|
+
ensureRawFixturePositions();
|
|
2839
|
+
await require_gemini.handleGemini(req, res, raw, geminiModel, streaming, fixtures, journal, defaults, setCorsHeaders);
|
|
2398
2840
|
} catch (err) {
|
|
2399
2841
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2400
2842
|
}
|
|
@@ -2405,7 +2847,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2405
2847
|
const vertexModel = vertexMatch[1];
|
|
2406
2848
|
const streaming = vertexMatch[2] === "streamGenerateContent";
|
|
2407
2849
|
try {
|
|
2408
|
-
|
|
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");
|
|
2409
2853
|
} catch (err) {
|
|
2410
2854
|
routeError(req, res, err, pathname, { streamEvent: (m) => `data: ${JSON.stringify({ error: { message: m } })}\n\n` });
|
|
2411
2855
|
}
|
|
@@ -2415,7 +2859,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2415
2859
|
if (bedrockMatch && req.method === "POST") {
|
|
2416
2860
|
const bedrockModelId = bedrockMatch[1];
|
|
2417
2861
|
try {
|
|
2418
|
-
|
|
2862
|
+
const raw = await require_helpers.readBody(req);
|
|
2863
|
+
ensureRawFixturePositions();
|
|
2864
|
+
await require_bedrock.handleBedrock(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2419
2865
|
} catch (err) {
|
|
2420
2866
|
routeError(req, res, err, pathname);
|
|
2421
2867
|
}
|
|
@@ -2425,7 +2871,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2425
2871
|
if (bedrockStreamMatch && req.method === "POST") {
|
|
2426
2872
|
const bedrockModelId = bedrockStreamMatch[1];
|
|
2427
2873
|
try {
|
|
2428
|
-
|
|
2874
|
+
const raw = await require_helpers.readBody(req);
|
|
2875
|
+
ensureRawFixturePositions();
|
|
2876
|
+
await require_bedrock.handleBedrockStream(req, res, raw, bedrockModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2429
2877
|
} catch (err) {
|
|
2430
2878
|
routeError(req, res, err, pathname);
|
|
2431
2879
|
}
|
|
@@ -2435,7 +2883,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2435
2883
|
if (converseMatch && req.method === "POST") {
|
|
2436
2884
|
const converseModelId = converseMatch[1];
|
|
2437
2885
|
try {
|
|
2438
|
-
|
|
2886
|
+
const raw = await require_helpers.readBody(req);
|
|
2887
|
+
ensureRawFixturePositions();
|
|
2888
|
+
await require_bedrock_converse.handleConverse(req, res, raw, converseModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2439
2889
|
} catch (err) {
|
|
2440
2890
|
routeError(req, res, err, pathname);
|
|
2441
2891
|
}
|
|
@@ -2445,7 +2895,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2445
2895
|
if (converseStreamMatch && req.method === "POST") {
|
|
2446
2896
|
const converseStreamModelId = converseStreamMatch[1];
|
|
2447
2897
|
try {
|
|
2448
|
-
|
|
2898
|
+
const raw = await require_helpers.readBody(req);
|
|
2899
|
+
ensureRawFixturePositions();
|
|
2900
|
+
await require_bedrock_converse.handleConverseStream(req, res, raw, converseStreamModelId, fixtures, journal, defaults, setCorsHeaders);
|
|
2449
2901
|
} catch (err) {
|
|
2450
2902
|
routeError(req, res, err, pathname);
|
|
2451
2903
|
}
|
|
@@ -2478,7 +2930,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2478
2930
|
if (pathname === require_route_registry.ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
|
|
2479
2931
|
setCorsHeaders(res);
|
|
2480
2932
|
try {
|
|
2481
|
-
|
|
2933
|
+
const raw = await require_helpers.readBody(req);
|
|
2934
|
+
ensureRawFixturePositions();
|
|
2935
|
+
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, "sound-generation");
|
|
2482
2936
|
} catch (err) {
|
|
2483
2937
|
routeError(req, res, err, pathname);
|
|
2484
2938
|
}
|
|
@@ -2487,7 +2941,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2487
2941
|
if (pathname === require_route_registry.ELEVENLABS_VOICE_DESIGN_PATH && req.method === "POST") {
|
|
2488
2942
|
setCorsHeaders(res);
|
|
2489
2943
|
try {
|
|
2490
|
-
|
|
2944
|
+
const raw = await require_helpers.readBody(req);
|
|
2945
|
+
ensureRawFixturePositions();
|
|
2946
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceDesign(req, res, raw, fixtures, defaults, journal);
|
|
2491
2947
|
} catch (err) {
|
|
2492
2948
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2493
2949
|
}
|
|
@@ -2496,7 +2952,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2496
2952
|
if (pathname === require_route_registry.ELEVENLABS_VOICE_CREATE_PATH && req.method === "POST") {
|
|
2497
2953
|
setCorsHeaders(res);
|
|
2498
2954
|
try {
|
|
2499
|
-
|
|
2955
|
+
const raw = await require_helpers.readBody(req);
|
|
2956
|
+
ensureRawFixturePositions();
|
|
2957
|
+
await require_elevenlabs_voice.handleElevenLabsVoiceCreate(req, res, raw, fixtures, defaults, journal);
|
|
2500
2958
|
} catch (err) {
|
|
2501
2959
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2502
2960
|
}
|
|
@@ -2527,8 +2985,13 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2527
2985
|
return;
|
|
2528
2986
|
}
|
|
2529
2987
|
try {
|
|
2530
|
-
if (req.method === "GET")
|
|
2531
|
-
|
|
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
|
+
}
|
|
2532
2995
|
} catch (err) {
|
|
2533
2996
|
routeError(req, res, err, pathname, { service: "elevenlabs-voice" });
|
|
2534
2997
|
}
|
|
@@ -2539,7 +3002,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2539
3002
|
setCorsHeaders(res);
|
|
2540
3003
|
const voiceId = elevenLabsTTSMatch[1];
|
|
2541
3004
|
try {
|
|
2542
|
-
|
|
3005
|
+
const raw = await require_helpers.readBody(req);
|
|
3006
|
+
ensureRawFixturePositions();
|
|
3007
|
+
await require_elevenlabs_audio.handleElevenLabsTTS(req, res, raw, fixtures, defaults, journal, voiceId);
|
|
2543
3008
|
} catch (err) {
|
|
2544
3009
|
routeError(req, res, err, pathname);
|
|
2545
3010
|
}
|
|
@@ -2550,7 +3015,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2550
3015
|
setCorsHeaders(res);
|
|
2551
3016
|
const musicSubType = musicMatch[1] ?? "music";
|
|
2552
3017
|
try {
|
|
2553
|
-
|
|
3018
|
+
const raw = await require_helpers.readBody(req);
|
|
3019
|
+
ensureRawFixturePositions();
|
|
3020
|
+
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, musicSubType);
|
|
2554
3021
|
} catch (err) {
|
|
2555
3022
|
routeError(req, res, err, pathname);
|
|
2556
3023
|
}
|
|
@@ -2581,6 +3048,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2581
3048
|
return;
|
|
2582
3049
|
}
|
|
2583
3050
|
}
|
|
3051
|
+
ensureRawFixturePositions();
|
|
2584
3052
|
if (await require_fal.handleFal(req, res, raw, pathname, fixtures, defaults, journal) === "handled") return;
|
|
2585
3053
|
} catch (err) {
|
|
2586
3054
|
routeError(req, res, err, pathname);
|
|
@@ -2590,7 +3058,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2590
3058
|
if (pathname.match(require_route_registry.FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
|
|
2591
3059
|
setCorsHeaders(res);
|
|
2592
3060
|
try {
|
|
2593
|
-
|
|
3061
|
+
const raw = falBody ?? await require_helpers.readBody(req);
|
|
3062
|
+
ensureRawFixturePositions();
|
|
3063
|
+
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2594
3064
|
} catch (err) {
|
|
2595
3065
|
routeError(req, res, err, pathname);
|
|
2596
3066
|
}
|
|
@@ -2617,6 +3087,7 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2617
3087
|
}, "internal", defaults.registry, defaults.logger);
|
|
2618
3088
|
return;
|
|
2619
3089
|
}
|
|
3090
|
+
ensureRawFixturePositions();
|
|
2620
3091
|
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2621
3092
|
} catch (err) {
|
|
2622
3093
|
routeError(req, res, err, pathname);
|
|
@@ -2626,7 +3097,9 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2626
3097
|
if (pathname.match(require_route_registry.FAL_RUN_RE) && req.method === "POST") {
|
|
2627
3098
|
setCorsHeaders(res);
|
|
2628
3099
|
try {
|
|
2629
|
-
|
|
3100
|
+
const raw = falBody ?? await require_helpers.readBody(req);
|
|
3101
|
+
ensureRawFixturePositions();
|
|
3102
|
+
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
2630
3103
|
} catch (err) {
|
|
2631
3104
|
routeError(req, res, err, pathname);
|
|
2632
3105
|
}
|
|
@@ -2644,13 +3117,14 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2644
3117
|
}
|
|
2645
3118
|
const completionsProvider = azureDeploymentId ? "azure" : isOpenRouter ? "openrouter" : isBytePlusArk ? "byteplus" : "openai";
|
|
2646
3119
|
try {
|
|
2647
|
-
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/"));
|
|
2648
3121
|
} catch (err) {
|
|
2649
3122
|
routeError(req, res, err, pathname, {
|
|
2650
3123
|
envelope: (m, status) => isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(status, m) : JSON.stringify({ error: {
|
|
2651
3124
|
message: m,
|
|
2652
3125
|
type: errorTypeForStatus(status)
|
|
2653
3126
|
} }),
|
|
3127
|
+
...isOpenRouter ? { toolCallEnvelope: (m, code) => require_openrouter_chat.serializeOpenRouterError(500, m, { reason: code }) } : {},
|
|
2654
3128
|
streamEvent: (m, status) => `data: ${isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(status, m) : JSON.stringify({ error: {
|
|
2655
3129
|
message: m,
|
|
2656
3130
|
type: errorTypeForStatus(status)
|
|
@@ -2738,6 +3212,25 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2738
3212
|
socket.destroy();
|
|
2739
3213
|
return;
|
|
2740
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
|
+
}
|
|
2741
3234
|
let liveId;
|
|
2742
3235
|
if (pathname === require_route_registry.LIVE_PATH) {
|
|
2743
3236
|
if (req.method !== "GET" || parsedUrl.searchParams.has("model")) {
|
|
@@ -2799,7 +3292,8 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2799
3292
|
...defaults,
|
|
2800
3293
|
model: "gpt-4",
|
|
2801
3294
|
testId: wsTestId,
|
|
2802
|
-
upgradeHeaders: req.headers
|
|
3295
|
+
upgradeHeaders: req.headers,
|
|
3296
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2803
3297
|
});
|
|
2804
3298
|
else if (pathname === require_route_registry.REALTIME_PATH) {
|
|
2805
3299
|
const transcriptionIntent = parsedUrl.searchParams.get("intent") === "transcription";
|
|
@@ -2809,13 +3303,15 @@ async function startServer(fixtures, options, resolvedAuth, mountList, logger, s
|
|
|
2809
3303
|
model,
|
|
2810
3304
|
transcriptionIntent,
|
|
2811
3305
|
testId: wsTestId,
|
|
2812
|
-
upgradeHeaders: req.headers
|
|
3306
|
+
upgradeHeaders: req.headers,
|
|
3307
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2813
3308
|
});
|
|
2814
3309
|
} else if (pathname === require_route_registry.GEMINI_LIVE_PATH) require_ws_gemini_live.handleWebSocketGeminiLive(ws, fixtures, journal, {
|
|
2815
3310
|
...defaults,
|
|
2816
3311
|
model: "gemini-2.0-flash",
|
|
2817
3312
|
testId: wsTestId,
|
|
2818
|
-
upgradeHeaders: req.headers
|
|
3313
|
+
upgradeHeaders: req.headers,
|
|
3314
|
+
beforeProcessMessage: ensureRawFixturePositions
|
|
2819
3315
|
});
|
|
2820
3316
|
}
|
|
2821
3317
|
const originalClose = server.close.bind(server);
|