@copilotkit/aimock 1.17.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/bedrock-converse.cjs +4 -4
- 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 +4 -4
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +4 -4
- 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 +4 -4
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +35 -9
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +17 -2
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +17 -2
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +35 -10
- package/dist/chaos.js.map +1 -1
- package/dist/cohere.cjs +2 -2
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.js +2 -2
- package/dist/cohere.js.map +1 -1
- package/dist/elevenlabs-audio.cjs +5 -2
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.js +5 -2
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +2 -2
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.js +2 -2
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +11 -4
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.js +11 -5
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +424 -0
- package/dist/fal.cjs.map +1 -0
- package/dist/fal.d.cts +39 -0
- package/dist/fal.d.cts.map +1 -0
- package/dist/fal.d.ts +39 -0
- package/dist/fal.d.ts.map +1 -0
- package/dist/fal.js +420 -0
- package/dist/fal.js.map +1 -0
- package/dist/fixture-loader.cjs +2 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +3 -3
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +4 -2
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.js +4 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +2 -2
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.js +2 -2
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +4 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +4 -1
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +2 -2
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +2 -2
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/llmock.cjs +15 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +2 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +2 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +15 -0
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +2 -2
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +2 -2
- package/dist/messages.js.map +1 -1
- package/dist/ollama.cjs +4 -4
- 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 +4 -4
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +42 -17
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +50 -5
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +50 -5
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +42 -17
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +2 -2
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.js +2 -2
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +1 -1
- 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 +2 -2
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +128 -52
- 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 +129 -53
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +2 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +2 -2
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +2 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.js +2 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +30 -6
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +30 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/video.cjs +9 -3
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +9 -3
- package/dist/video.js.map +1 -1
- package/package.json +1 -1
package/dist/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAudioResponse, isImageResponse, isTranscriptionResponse, isVideoResponse } from "./helpers.js";
|
|
1
|
+
import { isAudioResponse, isErrorResponse, isImageResponse, isJSONResponse, isTranscriptionResponse, isVideoResponse } from "./helpers.js";
|
|
2
2
|
|
|
3
3
|
//#region src/router.ts
|
|
4
4
|
function getLastMessageByRole(messages, role) {
|
|
@@ -31,7 +31,7 @@ function matchFixture(fixtures, req, matchCounts, requestTransform) {
|
|
|
31
31
|
if (match.endpoint !== reqEndpoint) continue;
|
|
32
32
|
} else if (reqEndpoint && reqEndpoint !== "chat" && reqEndpoint !== "embedding") {
|
|
33
33
|
const r = fixture.response;
|
|
34
|
-
if (!(reqEndpoint === "image" && isImageResponse(r) || reqEndpoint === "speech" && isAudioResponse(r) || reqEndpoint === "audio-gen" && isAudioResponse(r) || reqEndpoint === "fal-audio" && isAudioResponse(r) || reqEndpoint === "transcription" && isTranscriptionResponse(r) || reqEndpoint === "video" && isVideoResponse(r))) continue;
|
|
34
|
+
if (!(reqEndpoint === "image" && isImageResponse(r) || reqEndpoint === "speech" && isAudioResponse(r) || reqEndpoint === "audio-gen" && isAudioResponse(r) || reqEndpoint === "fal-audio" && isAudioResponse(r) || reqEndpoint === "fal" && (isJSONResponse(r) || isErrorResponse(r)) || reqEndpoint === "transcription" && isTranscriptionResponse(r) || reqEndpoint === "video" && isVideoResponse(r))) continue;
|
|
35
35
|
}
|
|
36
36
|
if (match.userMessage !== void 0) {
|
|
37
37
|
const msg = getLastMessageByRole(effective.messages, "user");
|
package/dist/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","names":[],"sources":["../src/router.ts"],"sourcesContent":["import type { ChatCompletionRequest, ChatMessage, ContentPart, Fixture } from \"./types.js\";\nimport {\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n} from \"./helpers.js\";\n\nexport function getLastMessageByRole(messages: ChatMessage[], role: string): ChatMessage | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === role) return messages[i];\n }\n return null;\n}\n\n/**\n * Extract the text content from a message's content field.\n * Handles both plain string content and array-of-parts content\n * (e.g. `[{type: \"text\", text: \"...\"}]` as sent by some SDKs).\n */\nexport function getTextContent(content: string | ContentPart[] | null): string | null {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const texts = content\n .filter((p) => p.type === \"text\" && typeof p.text === \"string\" && p.text !== \"\")\n .map((p) => p.text as string);\n return texts.length > 0 ? texts.join(\"\") : null;\n }\n return null;\n}\n\nexport function matchFixture(\n fixtures: Fixture[],\n req: ChatCompletionRequest,\n matchCounts?: Map<Fixture, number>,\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest,\n): Fixture | null {\n // Apply transform once before matching — used for stripping dynamic data\n const effective = requestTransform ? requestTransform(req) : req;\n const useExactMatch = !!requestTransform;\n\n for (const fixture of fixtures) {\n const { match } = fixture;\n\n // predicate — if present, must return true (receives original request)\n if (match.predicate !== undefined) {\n if (!match.predicate(req)) continue;\n }\n\n // endpoint — bidirectional filtering:\n // 1. If fixture has endpoint set, only match requests of that type\n // 2. If request has _endpointType but fixture doesn't, skip fixtures\n // whose response type is incompatible (prevents generic chat fixtures\n // from matching image/speech/video requests and causing 500s)\n const reqEndpoint = effective._endpointType as string | undefined;\n if (match.endpoint !== undefined) {\n if (match.endpoint !== reqEndpoint) continue;\n } else if (reqEndpoint && reqEndpoint !== \"chat\" && reqEndpoint !== \"embedding\") {\n // Fixture has no endpoint restriction but request is multimedia —\n // only match if the response type is compatible\n const r = fixture.response;\n const compatible =\n (reqEndpoint === \"image\" && isImageResponse(r)) ||\n (reqEndpoint === \"speech\" && isAudioResponse(r)) ||\n (reqEndpoint === \"audio-gen\" && isAudioResponse(r)) ||\n (reqEndpoint === \"fal-audio\" && isAudioResponse(r)) ||\n (reqEndpoint === \"transcription\" && isTranscriptionResponse(r)) ||\n (reqEndpoint === \"video\" && isVideoResponse(r));\n if (!compatible) continue;\n }\n\n // userMessage — case-sensitive match against the last user message content.\n // String matching is intentionally case-sensitive so fixture authors can\n // rely on exact string values. This differs from the case-insensitive\n // matchesPattern() in helpers.ts, which is used for search/rerank/moderation\n // where exact casing rarely matters.\n if (match.userMessage !== undefined) {\n const msg = getLastMessageByRole(effective.messages, \"user\");\n const text = msg ? getTextContent(msg.content) : null;\n if (!text) continue;\n if (typeof match.userMessage === \"string\") {\n if (useExactMatch) {\n if (text !== match.userMessage) continue;\n } else {\n if (!text.includes(match.userMessage)) continue;\n }\n } else {\n match.userMessage.lastIndex = 0;\n if (!match.userMessage.test(text)) continue;\n }\n }\n\n // toolCallId — a toolCallId fixture answers the model's response to a tool\n // result, which by API contract only happens when the conversation's LAST\n // message is a tool result. If a newer user (or other) turn follows the\n // tool message, the stale tool_call_id must not shadow userMessage matchers.\n if (match.toolCallId !== undefined) {\n const last = effective.messages[effective.messages.length - 1];\n if (!last || last.role !== \"tool\" || last.tool_call_id !== match.toolCallId) continue;\n }\n\n // toolName — match against any tool definition by function.name\n if (match.toolName !== undefined) {\n const tools = effective.tools ?? [];\n const found = tools.some((t) => t.function.name === match.toolName);\n if (!found) continue;\n }\n\n // inputText — case-sensitive match against the embedding input text.\n // Same rationale as userMessage above: fixture authors specify exact strings.\n if (match.inputText !== undefined) {\n const embeddingInput = effective.embeddingInput;\n if (!embeddingInput) continue;\n if (typeof match.inputText === \"string\") {\n if (useExactMatch) {\n if (embeddingInput !== match.inputText) continue;\n } else {\n if (!embeddingInput.includes(match.inputText)) continue;\n }\n } else {\n match.inputText.lastIndex = 0;\n if (!match.inputText.test(embeddingInput)) continue;\n }\n }\n\n // responseFormat — exact string match against request response_format.type\n if (match.responseFormat !== undefined) {\n const reqType = effective.response_format?.type;\n if (reqType !== match.responseFormat) continue;\n }\n\n // model — exact string or regexp\n if (match.model !== undefined) {\n if (typeof match.model === \"string\") {\n if (effective.model !== match.model) continue;\n } else {\n match.model.lastIndex = 0;\n if (!match.model.test(effective.model)) continue;\n }\n }\n\n // sequenceIndex — check against the fixture's match count\n if (match.sequenceIndex !== undefined && matchCounts !== undefined) {\n const count = matchCounts.get(fixture) ?? 0;\n if (count !== match.sequenceIndex) continue;\n }\n\n if (match.turnIndex !== undefined) {\n const assistantCount = effective.messages.filter((m) => m.role === \"assistant\").length;\n if (assistantCount !== match.turnIndex) continue;\n }\n\n if (match.hasToolResult !== undefined) {\n const hasTool = effective.messages.some((m) => m.role === \"tool\");\n if (hasTool !== match.hasToolResult) continue;\n }\n\n return fixture;\n }\n\n return null;\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"router.js","names":[],"sources":["../src/router.ts"],"sourcesContent":["import type { ChatCompletionRequest, ChatMessage, ContentPart, Fixture } from \"./types.js\";\nimport {\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n isJSONResponse,\n isErrorResponse,\n} from \"./helpers.js\";\n\nexport function getLastMessageByRole(messages: ChatMessage[], role: string): ChatMessage | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === role) return messages[i];\n }\n return null;\n}\n\n/**\n * Extract the text content from a message's content field.\n * Handles both plain string content and array-of-parts content\n * (e.g. `[{type: \"text\", text: \"...\"}]` as sent by some SDKs).\n */\nexport function getTextContent(content: string | ContentPart[] | null): string | null {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const texts = content\n .filter((p) => p.type === \"text\" && typeof p.text === \"string\" && p.text !== \"\")\n .map((p) => p.text as string);\n return texts.length > 0 ? texts.join(\"\") : null;\n }\n return null;\n}\n\nexport function matchFixture(\n fixtures: Fixture[],\n req: ChatCompletionRequest,\n matchCounts?: Map<Fixture, number>,\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest,\n): Fixture | null {\n // Apply transform once before matching — used for stripping dynamic data\n const effective = requestTransform ? requestTransform(req) : req;\n const useExactMatch = !!requestTransform;\n\n for (const fixture of fixtures) {\n const { match } = fixture;\n\n // predicate — if present, must return true (receives original request)\n if (match.predicate !== undefined) {\n if (!match.predicate(req)) continue;\n }\n\n // endpoint — bidirectional filtering:\n // 1. If fixture has endpoint set, only match requests of that type\n // 2. If request has _endpointType but fixture doesn't, skip fixtures\n // whose response type is incompatible (prevents generic chat fixtures\n // from matching image/speech/video requests and causing 500s)\n const reqEndpoint = effective._endpointType as string | undefined;\n if (match.endpoint !== undefined) {\n if (match.endpoint !== reqEndpoint) continue;\n } else if (reqEndpoint && reqEndpoint !== \"chat\" && reqEndpoint !== \"embedding\") {\n // Fixture has no endpoint restriction but request is multimedia —\n // only match if the response type is compatible\n const r = fixture.response;\n const compatible =\n (reqEndpoint === \"image\" && isImageResponse(r)) ||\n (reqEndpoint === \"speech\" && isAudioResponse(r)) ||\n (reqEndpoint === \"audio-gen\" && isAudioResponse(r)) ||\n (reqEndpoint === \"fal-audio\" && isAudioResponse(r)) ||\n (reqEndpoint === \"fal\" && (isJSONResponse(r) || isErrorResponse(r))) ||\n (reqEndpoint === \"transcription\" && isTranscriptionResponse(r)) ||\n (reqEndpoint === \"video\" && isVideoResponse(r));\n if (!compatible) continue;\n }\n\n // userMessage — case-sensitive match against the last user message content.\n // String matching is intentionally case-sensitive so fixture authors can\n // rely on exact string values. This differs from the case-insensitive\n // matchesPattern() in helpers.ts, which is used for search/rerank/moderation\n // where exact casing rarely matters.\n if (match.userMessage !== undefined) {\n const msg = getLastMessageByRole(effective.messages, \"user\");\n const text = msg ? getTextContent(msg.content) : null;\n if (!text) continue;\n if (typeof match.userMessage === \"string\") {\n if (useExactMatch) {\n if (text !== match.userMessage) continue;\n } else {\n if (!text.includes(match.userMessage)) continue;\n }\n } else {\n match.userMessage.lastIndex = 0;\n if (!match.userMessage.test(text)) continue;\n }\n }\n\n // toolCallId — a toolCallId fixture answers the model's response to a tool\n // result, which by API contract only happens when the conversation's LAST\n // message is a tool result. If a newer user (or other) turn follows the\n // tool message, the stale tool_call_id must not shadow userMessage matchers.\n if (match.toolCallId !== undefined) {\n const last = effective.messages[effective.messages.length - 1];\n if (!last || last.role !== \"tool\" || last.tool_call_id !== match.toolCallId) continue;\n }\n\n // toolName — match against any tool definition by function.name\n if (match.toolName !== undefined) {\n const tools = effective.tools ?? [];\n const found = tools.some((t) => t.function.name === match.toolName);\n if (!found) continue;\n }\n\n // inputText — case-sensitive match against the embedding input text.\n // Same rationale as userMessage above: fixture authors specify exact strings.\n if (match.inputText !== undefined) {\n const embeddingInput = effective.embeddingInput;\n if (!embeddingInput) continue;\n if (typeof match.inputText === \"string\") {\n if (useExactMatch) {\n if (embeddingInput !== match.inputText) continue;\n } else {\n if (!embeddingInput.includes(match.inputText)) continue;\n }\n } else {\n match.inputText.lastIndex = 0;\n if (!match.inputText.test(embeddingInput)) continue;\n }\n }\n\n // responseFormat — exact string match against request response_format.type\n if (match.responseFormat !== undefined) {\n const reqType = effective.response_format?.type;\n if (reqType !== match.responseFormat) continue;\n }\n\n // model — exact string or regexp\n if (match.model !== undefined) {\n if (typeof match.model === \"string\") {\n if (effective.model !== match.model) continue;\n } else {\n match.model.lastIndex = 0;\n if (!match.model.test(effective.model)) continue;\n }\n }\n\n // sequenceIndex — check against the fixture's match count\n if (match.sequenceIndex !== undefined && matchCounts !== undefined) {\n const count = matchCounts.get(fixture) ?? 0;\n if (count !== match.sequenceIndex) continue;\n }\n\n if (match.turnIndex !== undefined) {\n const assistantCount = effective.messages.filter((m) => m.role === \"assistant\").length;\n if (assistantCount !== match.turnIndex) continue;\n }\n\n if (match.hasToolResult !== undefined) {\n const hasTool = effective.messages.some((m) => m.role === \"tool\");\n if (hasTool !== match.hasToolResult) continue;\n }\n\n return fixture;\n }\n\n return null;\n}\n"],"mappings":"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"}
|
package/dist/server.cjs
CHANGED
|
@@ -21,6 +21,7 @@ const require_transcription = require('./transcription.cjs');
|
|
|
21
21
|
const require_video = require('./video.cjs');
|
|
22
22
|
const require_elevenlabs_audio = require('./elevenlabs-audio.cjs');
|
|
23
23
|
const require_fal_audio = require('./fal-audio.cjs');
|
|
24
|
+
const require_fal = require('./fal.cjs');
|
|
24
25
|
const require_ollama = require('./ollama.cjs');
|
|
25
26
|
const require_cohere = require('./cohere.cjs');
|
|
26
27
|
const require_search = require('./search.cjs');
|
|
@@ -56,6 +57,7 @@ const ELEVENLABS_MUSIC_RE = /^\/v1\/music(?:\/(.+))?$/;
|
|
|
56
57
|
const FAL_QUEUE_SUBMIT_RE = /^\/fal\/queue\/submit\/(.+)$/;
|
|
57
58
|
const FAL_QUEUE_REQUESTS_RE = /^\/fal\/queue\/requests\/(.+)$/;
|
|
58
59
|
const FAL_RUN_RE = /^\/fal\/run\/(.+)$/;
|
|
60
|
+
const FAL_PREFIX_RE = /^\/fal(?:\/.*)?$/;
|
|
59
61
|
const DEFAULT_CHUNK_SIZE = 20;
|
|
60
62
|
const COMPAT_SUFFIXES = [
|
|
61
63
|
"/chat/completions",
|
|
@@ -211,6 +213,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
|
|
|
211
213
|
journal.clear();
|
|
212
214
|
videoStates.clear();
|
|
213
215
|
require_fal_audio.falJobs.clear();
|
|
216
|
+
require_fal.falQueueStates.clear();
|
|
214
217
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
215
218
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
216
219
|
res.end(JSON.stringify({ reset: true }));
|
|
@@ -329,6 +332,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
329
332
|
} }));
|
|
330
333
|
return;
|
|
331
334
|
}
|
|
335
|
+
const method = req.method ?? "POST";
|
|
336
|
+
const path = req.url ?? COMPLETIONS_PATH;
|
|
337
|
+
const flatHeaders = require_helpers.flattenHeaders(req.headers);
|
|
332
338
|
body._endpointType = "chat";
|
|
333
339
|
const testId = require_helpers.getTestId(req);
|
|
334
340
|
const fixture = require_router.matchFixture(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
@@ -340,18 +346,40 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
340
346
|
const snippet = typeof lastUserMsg?.content === "string" ? lastUserMsg.content.slice(0, 80) : "";
|
|
341
347
|
defaults.logger.debug(`No fixture matched for request (model=${body.model ?? "?"}, msg="${snippet}")`);
|
|
342
348
|
}
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
const flatHeaders = require_helpers.flattenHeaders(req.headers);
|
|
346
|
-
if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
349
|
+
const chaosAction = require_chaos.evaluateChaos(fixture, defaults.chaos, req.headers, defaults.logger);
|
|
350
|
+
const chaosContext = {
|
|
347
351
|
method,
|
|
348
352
|
path,
|
|
349
353
|
headers: flatHeaders,
|
|
350
354
|
body
|
|
351
|
-
}
|
|
355
|
+
};
|
|
356
|
+
if (chaosAction === "drop" || chaosAction === "disconnect") {
|
|
357
|
+
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, fixture ? "fixture" : "proxy", defaults.registry);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (fixture && chaosAction === "malformed") {
|
|
361
|
+
require_chaos.applyChaosAction(chaosAction, res, fixture, journal, chaosContext, "fixture", defaults.registry);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
352
364
|
if (!fixture) {
|
|
353
365
|
if (defaults.record && providerKey) {
|
|
354
|
-
|
|
366
|
+
const hookOptions = chaosAction === "malformed" ? {
|
|
367
|
+
beforeWriteResponse: () => {
|
|
368
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, chaosContext, "proxy", defaults.registry);
|
|
369
|
+
return true;
|
|
370
|
+
},
|
|
371
|
+
onHookBypassed: (reason) => {
|
|
372
|
+
defaults.logger.warn(`[chaos] malformed bypassed on proxy: upstream returned SSE (${reason})`);
|
|
373
|
+
defaults.registry?.incrementCounter("aimock_chaos_bypassed_total", {
|
|
374
|
+
action: "malformed",
|
|
375
|
+
source: "proxy",
|
|
376
|
+
reason
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
} : void 0;
|
|
380
|
+
const outcome = await require_recorder.proxyAndRecord(req, res, body, providerKey, req.url ?? COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions);
|
|
381
|
+
if (outcome === "handled_by_hook") return;
|
|
382
|
+
if (outcome === "relayed") {
|
|
355
383
|
journal.add({
|
|
356
384
|
method: req.method ?? "POST",
|
|
357
385
|
path: req.url ?? COMPLETIONS_PATH,
|
|
@@ -868,7 +896,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
868
896
|
const videoStatusMatch = pathname.match(VIDEOS_STATUS_RE);
|
|
869
897
|
if (videoStatusMatch && req.method === "GET") {
|
|
870
898
|
const videoId = videoStatusMatch[1];
|
|
871
|
-
require_video.handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStates);
|
|
899
|
+
require_video.handleVideoStatus(req, res, videoId, journal, defaults, setCorsHeaders, videoStates);
|
|
872
900
|
return;
|
|
873
901
|
}
|
|
874
902
|
const geminiPredictMatch = pathname.match(GEMINI_PREDICT_RE);
|
|
@@ -1097,15 +1125,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1097
1125
|
setCorsHeaders(res);
|
|
1098
1126
|
try {
|
|
1099
1127
|
const raw = await readBody(req);
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1128
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1129
|
+
if (chaosAction) {
|
|
1130
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1131
|
+
method: req.method ?? "POST",
|
|
1132
|
+
path: pathname,
|
|
1133
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1134
|
+
body: {
|
|
1135
|
+
model: "",
|
|
1136
|
+
messages: []
|
|
1137
|
+
}
|
|
1138
|
+
}, "fixture", defaults.registry);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1109
1141
|
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, "sound-generation");
|
|
1110
1142
|
} catch (err) {
|
|
1111
1143
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
@@ -1123,15 +1155,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1123
1155
|
const musicSubType = musicMatch[1] ?? "music";
|
|
1124
1156
|
try {
|
|
1125
1157
|
const raw = await readBody(req);
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1158
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1159
|
+
if (chaosAction) {
|
|
1160
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1161
|
+
method: req.method ?? "POST",
|
|
1162
|
+
path: pathname,
|
|
1163
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1164
|
+
body: {
|
|
1165
|
+
model: "",
|
|
1166
|
+
messages: []
|
|
1167
|
+
}
|
|
1168
|
+
}, "fixture", defaults.registry);
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1135
1171
|
await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, musicSubType);
|
|
1136
1172
|
} catch (err) {
|
|
1137
1173
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
@@ -1143,19 +1179,51 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1143
1179
|
}
|
|
1144
1180
|
return;
|
|
1145
1181
|
}
|
|
1182
|
+
if (FAL_PREFIX_RE.test(pathname) && req.headers["x-fal-target-host"]) {
|
|
1183
|
+
setCorsHeaders(res);
|
|
1184
|
+
try {
|
|
1185
|
+
const raw = req.method === "POST" || req.method === "PUT" ? await readBody(req) : "";
|
|
1186
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1187
|
+
if (chaosAction) {
|
|
1188
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1189
|
+
method: req.method ?? "GET",
|
|
1190
|
+
path: pathname,
|
|
1191
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1192
|
+
body: {
|
|
1193
|
+
model: "",
|
|
1194
|
+
messages: []
|
|
1195
|
+
}
|
|
1196
|
+
}, "fixture", defaults.registry);
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (await require_fal.handleFal(req, res, raw, pathname, fixtures, defaults, journal) === "handled") return;
|
|
1200
|
+
} catch (err) {
|
|
1201
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
1202
|
+
if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
1203
|
+
message: msg,
|
|
1204
|
+
type: "server_error"
|
|
1205
|
+
} }));
|
|
1206
|
+
else if (!res.writableEnded) res.destroy();
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1146
1210
|
if (pathname.match(FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
|
|
1147
1211
|
setCorsHeaders(res);
|
|
1148
1212
|
try {
|
|
1149
1213
|
const raw = await readBody(req);
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1214
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1215
|
+
if (chaosAction) {
|
|
1216
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1217
|
+
method: req.method ?? "POST",
|
|
1218
|
+
path: pathname,
|
|
1219
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1220
|
+
body: {
|
|
1221
|
+
model: "",
|
|
1222
|
+
messages: []
|
|
1223
|
+
}
|
|
1224
|
+
}, "fixture", defaults.registry);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1159
1227
|
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
1160
1228
|
} catch (err) {
|
|
1161
1229
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
@@ -1171,15 +1239,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1171
1239
|
setCorsHeaders(res);
|
|
1172
1240
|
try {
|
|
1173
1241
|
const raw = req.method === "POST" ? await readBody(req) : "{}";
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1242
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1243
|
+
if (chaosAction) {
|
|
1244
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1245
|
+
method: req.method ?? "GET",
|
|
1246
|
+
path: pathname,
|
|
1247
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1248
|
+
body: {
|
|
1249
|
+
model: "",
|
|
1250
|
+
messages: []
|
|
1251
|
+
}
|
|
1252
|
+
}, "fixture", defaults.registry);
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1183
1255
|
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
1184
1256
|
} catch (err) {
|
|
1185
1257
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
@@ -1195,15 +1267,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1195
1267
|
setCorsHeaders(res);
|
|
1196
1268
|
try {
|
|
1197
1269
|
const raw = await readBody(req);
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1270
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
1271
|
+
if (chaosAction) {
|
|
1272
|
+
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1273
|
+
method: req.method ?? "POST",
|
|
1274
|
+
path: pathname,
|
|
1275
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1276
|
+
body: {
|
|
1277
|
+
model: "",
|
|
1278
|
+
messages: []
|
|
1279
|
+
}
|
|
1280
|
+
}, "fixture", defaults.registry);
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1207
1283
|
await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
|
|
1208
1284
|
} catch (err) {
|
|
1209
1285
|
const msg = err instanceof Error ? err.message : "Internal error";
|