@copilotkit/aimock 1.9.0 → 1.11.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.
Files changed (64) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +17 -17
  4. package/dist/agui-handler.cjs +340 -0
  5. package/dist/agui-handler.cjs.map +1 -0
  6. package/dist/agui-handler.d.cts +96 -0
  7. package/dist/agui-handler.d.cts.map +1 -0
  8. package/dist/agui-handler.d.ts +96 -0
  9. package/dist/agui-handler.d.ts.map +1 -0
  10. package/dist/agui-handler.js +326 -0
  11. package/dist/agui-handler.js.map +1 -0
  12. package/dist/agui-mock.cjs +190 -0
  13. package/dist/agui-mock.cjs.map +1 -0
  14. package/dist/agui-mock.d.cts +50 -0
  15. package/dist/agui-mock.d.cts.map +1 -0
  16. package/dist/agui-mock.d.ts +50 -0
  17. package/dist/agui-mock.d.ts.map +1 -0
  18. package/dist/agui-mock.js +188 -0
  19. package/dist/agui-mock.js.map +1 -0
  20. package/dist/agui-recorder.cjs +153 -0
  21. package/dist/agui-recorder.cjs.map +1 -0
  22. package/dist/agui-recorder.d.cts +19 -0
  23. package/dist/agui-recorder.d.cts.map +1 -0
  24. package/dist/agui-recorder.d.ts +19 -0
  25. package/dist/agui-recorder.d.ts.map +1 -0
  26. package/dist/agui-recorder.js +147 -0
  27. package/dist/agui-recorder.js.map +1 -0
  28. package/dist/agui-types.d.cts +231 -0
  29. package/dist/agui-types.d.cts.map +1 -0
  30. package/dist/agui-types.d.ts +231 -0
  31. package/dist/agui-types.d.ts.map +1 -0
  32. package/dist/cli.cjs +42 -5
  33. package/dist/cli.cjs.map +1 -1
  34. package/dist/cli.js +42 -5
  35. package/dist/cli.js.map +1 -1
  36. package/dist/config-loader.cjs +19 -0
  37. package/dist/config-loader.cjs.map +1 -1
  38. package/dist/config-loader.d.cts +16 -0
  39. package/dist/config-loader.d.cts.map +1 -1
  40. package/dist/config-loader.d.ts +16 -0
  41. package/dist/config-loader.d.ts.map +1 -1
  42. package/dist/config-loader.js +19 -0
  43. package/dist/config-loader.js.map +1 -1
  44. package/dist/index.cjs +19 -0
  45. package/dist/index.d.cts +5 -1
  46. package/dist/index.d.ts +5 -1
  47. package/dist/index.js +4 -1
  48. package/dist/recorder.cjs +23 -21
  49. package/dist/recorder.cjs.map +1 -1
  50. package/dist/recorder.js +23 -21
  51. package/dist/recorder.js.map +1 -1
  52. package/dist/suite.cjs +8 -0
  53. package/dist/suite.cjs.map +1 -1
  54. package/dist/suite.d.cts +4 -0
  55. package/dist/suite.d.cts.map +1 -1
  56. package/dist/suite.d.ts +4 -0
  57. package/dist/suite.d.ts.map +1 -1
  58. package/dist/suite.js +8 -0
  59. package/dist/suite.js.map +1 -1
  60. package/dist/types.d.cts +2 -0
  61. package/dist/types.d.cts.map +1 -1
  62. package/dist/types.d.ts +2 -0
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +1 -1
package/dist/recorder.cjs CHANGED
@@ -117,27 +117,29 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
117
117
  const matchValues = Object.values(fixtureMatch);
118
118
  const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === void 0);
119
119
  if (isEmptyMatch) defaults.logger.warn("Recorded fixture has empty match criteria — skipping in-memory registration");
120
- const filename = `${providerKey}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${node_crypto.randomUUID().slice(0, 8)}.json`;
121
- const filepath = node_path.join(fixturePath, filename);
122
- let writtenToDisk = false;
123
- try {
124
- node_fs.mkdirSync(fixturePath, { recursive: true });
125
- const warnings = [];
126
- if (isEmptyMatch) warnings.push("Empty match criteria — this fixture will not match any request");
127
- if (collapsed?.truncated) warnings.push("Stream response was truncated — fixture may be incomplete");
128
- const fileContent = { fixtures: [fixture] };
129
- if (warnings.length > 0) fileContent._warning = warnings.join("; ");
130
- node_fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), "utf-8");
131
- writtenToDisk = true;
132
- } catch (err) {
133
- const msg = err instanceof Error ? err.message : "Unknown filesystem error";
134
- defaults.logger.error(`Failed to save fixture to disk: ${msg}`);
135
- res.setHeader("X-LLMock-Record-Error", msg);
136
- }
137
- if (writtenToDisk) {
138
- if (!isEmptyMatch) fixtures.push(fixture);
139
- defaults.logger.warn(`Response recorded → ${filepath}`);
140
- } else defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);
120
+ if (!defaults.record?.proxyOnly) {
121
+ const filename = `${providerKey}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${node_crypto.randomUUID().slice(0, 8)}.json`;
122
+ const filepath = node_path.join(fixturePath, filename);
123
+ let writtenToDisk = false;
124
+ try {
125
+ node_fs.mkdirSync(fixturePath, { recursive: true });
126
+ const warnings = [];
127
+ if (isEmptyMatch) warnings.push("Empty match criteriathis fixture will not match any request");
128
+ if (collapsed?.truncated) warnings.push("Stream response was truncated — fixture may be incomplete");
129
+ const fileContent = { fixtures: [fixture] };
130
+ if (warnings.length > 0) fileContent._warning = warnings.join("; ");
131
+ node_fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), "utf-8");
132
+ writtenToDisk = true;
133
+ } catch (err) {
134
+ const msg = err instanceof Error ? err.message : "Unknown filesystem error";
135
+ defaults.logger.error(`Failed to save fixture to disk: ${msg}`);
136
+ res.setHeader("X-LLMock-Record-Error", msg);
137
+ }
138
+ if (writtenToDisk) {
139
+ if (!isEmptyMatch) fixtures.push(fixture);
140
+ defaults.logger.warn(`Response recorded ${filepath}`);
141
+ } else defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);
142
+ } else defaults.logger.info(`Proxied ${providerKey} request (proxy-only mode)`);
141
143
  const relayHeaders = {};
142
144
  if (ctString) relayHeaders["Content-Type"] = ctString;
143
145
  res.writeHead(upstreamStatus, relayHeaders);
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.cjs","names":["resolveUpstreamUrl","collapseStreamingResponse","crypto","path","https","http","getLastMessageByRole","getTextContent"],"sources":["../src/recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n RecordConfig,\n RecordProviderKey,\n ToolCall,\n} from \"./types.js\";\nimport { getLastMessageByRole, getTextContent } from \"./router.js\";\nimport type { Logger } from \"./logger.js\";\nimport { collapseStreamingResponse } from \"./stream-collapse.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\n\n/** Headers to strip when proxying — hop-by-hop (RFC 2616 §13.5.1) + client-set. */\nconst STRIP_HEADERS = new Set([\n // Hop-by-hop (RFC 2616 §13.5.1)\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authorization\",\n \"proxy-authenticate\",\n // Set by HTTP client from the target URL / body\n \"host\",\n \"content-length\",\n // Not relevant for LLM APIs; avoid leaking or mismatched encoding\n \"cookie\",\n \"accept-encoding\",\n]);\n\n/**\n * Proxy an unmatched request to the real upstream provider, record the\n * response as a fixture on disk and in memory, then relay the response\n * back to the original client.\n *\n * Returns `true` if the request was proxied (provider configured),\n * `false` if no upstream URL is configured for the given provider key.\n */\nexport async function proxyAndRecord(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n request: ChatCompletionRequest,\n providerKey: RecordProviderKey,\n pathname: string,\n fixtures: Fixture[],\n defaults: {\n record?: RecordConfig;\n logger: Logger;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n },\n rawBody?: string,\n): Promise<boolean> {\n const record = defaults.record;\n if (!record) return false;\n\n const providers = record.providers;\n const upstreamUrl = providers[providerKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return false;\n }\n\n const fixturePath = record.fixturePath ?? \"./fixtures/recorded\";\n let target: URL;\n try {\n target = resolveUpstreamUrl(upstreamUrl, pathname);\n } catch {\n defaults.logger.error(`Invalid upstream URL for provider \"${providerKey}\": ${upstreamUrl}`);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Invalid upstream URL: ${upstreamUrl}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);\n\n // Forward all request headers except hop-by-hop and client-set ones.\n const forwardHeaders: Record<string, string> = {};\n for (const [name, val] of Object.entries(req.headers)) {\n if (val !== undefined && !STRIP_HEADERS.has(name)) {\n forwardHeaders[name] = Array.isArray(val) ? val.join(\", \") : val;\n }\n }\n\n const requestBody = rawBody ?? JSON.stringify(request);\n\n // Make upstream request\n let upstreamStatus: number;\n let upstreamHeaders: http.IncomingHttpHeaders;\n let upstreamBody: string;\n let rawBuffer: Buffer;\n\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n // Detect streaming response and collapse if necessary\n const contentType = upstreamHeaders[\"content-type\"];\n const ctString = Array.isArray(contentType) ? contentType.join(\", \") : (contentType ?? \"\");\n const isBinaryStream = ctString.toLowerCase().includes(\"application/vnd.amazon.eventstream\");\n const collapsed = collapseStreamingResponse(\n ctString,\n providerKey,\n isBinaryStream ? rawBuffer : upstreamBody,\n defaults.logger,\n );\n\n let fixtureResponse: FixtureResponse;\n\n if (collapsed) {\n // Streaming response — use collapsed result\n defaults.logger.warn(`Streaming response detected (${ctString}) — collapsing to fixture`);\n if (collapsed.truncated) {\n defaults.logger.warn(\"Bedrock EventStream: CRC mismatch — response may be truncated\");\n }\n if (collapsed.droppedChunks && collapsed.droppedChunks > 0) {\n defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse`);\n }\n if (collapsed.content === \"\" && (!collapsed.toolCalls || collapsed.toolCalls.length === 0)) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n }\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n defaults.logger.warn(\n \"Collapsed response has both content and toolCalls — preferring toolCalls\",\n );\n }\n fixtureResponse = { toolCalls: collapsed.toolCalls };\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\" };\n }\n } else {\n // Non-streaming — try to parse as JSON\n let parsedResponse: unknown = null;\n try {\n parsedResponse = JSON.parse(upstreamBody);\n } catch {\n // Not JSON — could be an unknown format\n defaults.logger.warn(\"Upstream response is not valid JSON — saving as error fixture\");\n }\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch {\n /* not JSON */\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n\n // Build the match criteria from the (optionally transformed) request\n const matchRequest = defaults.requestTransform ? defaults.requestTransform(request) : request;\n const fixtureMatch = buildFixtureMatch(matchRequest);\n\n // Build and save the fixture\n const fixture: Fixture = { match: fixtureMatch, response: fixtureResponse };\n\n // Check if the match is empty (all undefined values) — warn but still save to disk\n const matchValues = Object.values(fixtureMatch);\n const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === undefined);\n if (isEmptyMatch) {\n defaults.logger.warn(\n \"Recorded fixture has empty match criteria — skipping in-memory registration\",\n );\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `${providerKey}-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n let writtenToDisk = false;\n try {\n // Ensure fixture directory exists\n fs.mkdirSync(fixturePath, { recursive: true });\n\n // Collect warnings for the fixture file\n const warnings: string[] = [];\n if (isEmptyMatch) {\n warnings.push(\"Empty match criteria — this fixture will not match any request\");\n }\n if (collapsed?.truncated) {\n warnings.push(\"Stream response was truncated — fixture may be incomplete\");\n }\n\n // Auth headers are forwarded to upstream but excluded from saved fixtures for security\n const fileContent: Record<string, unknown> = { fixtures: [fixture] };\n if (warnings.length > 0) {\n fileContent._warning = warnings.join(\"; \");\n }\n fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), \"utf-8\");\n writtenToDisk = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n defaults.logger.error(`Failed to save fixture to disk: ${msg}`);\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n }\n\n if (writtenToDisk) {\n // Register in memory so subsequent identical requests match (skip if empty match)\n if (!isEmptyMatch) {\n fixtures.push(fixture);\n }\n defaults.logger.warn(`Response recorded → ${filepath}`);\n } else {\n defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);\n }\n\n // Relay upstream response to client\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n res.end(isBinaryStream ? rawBuffer : upstreamBody);\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string; rawBuffer: Buffer }> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n const BODY_TIMEOUT_MS = 30_000;\n const req = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (res) => {\n res.setTimeout(BODY_TIMEOUT_MS, () => {\n req.destroy(new Error(`Upstream response timed out after ${BODY_TIMEOUT_MS / 1000}s`));\n });\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n });\n });\n },\n );\n req.on(\"timeout\", () => {\n req.destroy(\n new Error(\n `Upstream request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s: ${target.href}`,\n ),\n );\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Detect the response format from the parsed upstream JSON and convert\n * it into an llmock FixtureResponse.\n */\nfunction buildFixtureResponse(\n parsed: unknown,\n status: number,\n encodingFormat?: string,\n): FixtureResponse {\n if (parsed === null || parsed === undefined) {\n // Raw / unparseable response — save as error\n return {\n error: { message: \"Upstream returned non-JSON response\", type: \"proxy_error\" },\n status,\n };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Error response\n if (obj.error) {\n const err = obj.error as Record<string, unknown>;\n return {\n error: {\n message: String(err.message ?? \"Unknown error\"),\n type: String(err.type ?? \"api_error\"),\n code: err.code ? String(err.code) : undefined,\n },\n status,\n };\n }\n\n // OpenAI embeddings: { data: [{ embedding: [...] }] }\n if (Array.isArray(obj.data) && obj.data.length > 0) {\n const first = obj.data[0] as Record<string, unknown>;\n if (Array.isArray(first.embedding)) {\n return { embedding: first.embedding as number[] };\n }\n if (typeof first.embedding === \"string\" && encodingFormat === \"base64\") {\n try {\n const buf = Buffer.from(first.embedding, \"base64\");\n const floats = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n } catch {\n // Corrupted base64 or non-float32 data — fall through to error\n }\n }\n }\n\n // Direct embedding: { embedding: [...] }\n if (Array.isArray(obj.embedding)) {\n return { embedding: obj.embedding as number[] };\n }\n\n // OpenAI chat completion: { choices: [{ message: { content, tool_calls } }] }\n if (Array.isArray(obj.choices) && obj.choices.length > 0) {\n const choice = obj.choices[0] as Record<string, unknown>;\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n // Tool calls\n if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (message.tool_calls as Array<Record<string, unknown>>).map(\n (tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name),\n arguments: String(fn.arguments),\n };\n },\n );\n return { toolCalls };\n }\n // Text content\n if (typeof message.content === \"string\") {\n return { content: message.content };\n }\n }\n }\n\n // Anthropic: { content: [{ type: \"text\", text: \"...\" }] } or tool_use\n if (Array.isArray(obj.content) && obj.content.length > 0) {\n const blocks = obj.content as Array<Record<string, unknown>>;\n // Check for tool_use blocks first\n const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => ({\n name: String(b.name),\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input),\n }));\n return { toolCalls };\n }\n // Text blocks\n const textBlock = blocks.find((b) => b.type === \"text\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n\n // Gemini: { candidates: [{ content: { parts: [{ text: \"...\" }] } }] }\n if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {\n const candidate = obj.candidates[0] as Record<string, unknown>;\n const content = candidate.content as Record<string, unknown> | undefined;\n if (content && Array.isArray(content.parts)) {\n const parts = content.parts as Array<Record<string, unknown>>;\n // Tool calls (functionCall)\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCalls: ToolCall[] = fnCallParts.map((p) => {\n const fc = p.functionCall as Record<string, unknown>;\n return {\n name: String(fc.name),\n arguments: typeof fc.args === \"string\" ? fc.args : JSON.stringify(fc.args),\n };\n });\n return { toolCalls };\n }\n // Text\n const textPart = parts.find((p) => typeof p.text === \"string\");\n if (textPart && typeof textPart.text === \"string\") {\n return { content: textPart.text };\n }\n }\n }\n\n // Bedrock Converse: { output: { message: { role, content: [{ text }, { toolUse }] } } }\n if (obj.output && typeof obj.output === \"object\") {\n const output = obj.output as Record<string, unknown>;\n const msg = output.message as Record<string, unknown> | undefined;\n if (msg && Array.isArray(msg.content)) {\n const blocks = msg.content as Array<Record<string, unknown>>;\n const toolUseBlocks = blocks.filter((b) => b.toolUse);\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => {\n const tu = b.toolUse as Record<string, unknown>;\n return {\n name: String(tu.name ?? \"\"),\n arguments: typeof tu.input === \"string\" ? tu.input : JSON.stringify(tu.input),\n };\n });\n return { toolCalls };\n }\n const textBlock = blocks.find((b) => typeof b.text === \"string\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n }\n\n // Ollama: { message: { content: \"...\", tool_calls: [...] } }\n if (obj.message && typeof obj.message === \"object\") {\n const msg = obj.message as Record<string, unknown>;\n // Tool calls (check before content — Ollama sends content: \"\" alongside tool_calls)\n if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (msg.tool_calls as Array<Record<string, unknown>>)\n .filter((tc) => tc.function != null)\n .map((tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n };\n });\n return { toolCalls };\n }\n if (typeof msg.content === \"string\" && msg.content.length > 0) {\n return { content: msg.content };\n }\n // Ollama message with content array (like Cohere)\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const first = msg.content[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") {\n return { content: first.text };\n }\n }\n }\n\n // Fallback: unknown format — save as error\n return {\n error: {\n message: \"Could not detect response format from upstream\",\n type: \"proxy_error\",\n },\n status,\n };\n}\n\n/**\n * Derive fixture match criteria from the original request.\n */\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n} {\n // Embedding request\n if (request.embeddingInput) {\n return { inputText: request.embeddingInput };\n }\n\n // Chat request — match on the last user message\n const lastUser = getLastMessageByRole(request.messages ?? [], \"user\");\n if (lastUser) {\n const text = getTextContent(lastUser.content);\n if (text) {\n return { userMessage: text };\n }\n }\n\n return {};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recorder.cjs","names":["resolveUpstreamUrl","collapseStreamingResponse","crypto","path","https","http","getLastMessageByRole","getTextContent"],"sources":["../src/recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n RecordConfig,\n RecordProviderKey,\n ToolCall,\n} from \"./types.js\";\nimport { getLastMessageByRole, getTextContent } from \"./router.js\";\nimport type { Logger } from \"./logger.js\";\nimport { collapseStreamingResponse } from \"./stream-collapse.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\n\n/** Headers to strip when proxying — hop-by-hop (RFC 2616 §13.5.1) + client-set. */\nconst STRIP_HEADERS = new Set([\n // Hop-by-hop (RFC 2616 §13.5.1)\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authorization\",\n \"proxy-authenticate\",\n // Set by HTTP client from the target URL / body\n \"host\",\n \"content-length\",\n // Not relevant for LLM APIs; avoid leaking or mismatched encoding\n \"cookie\",\n \"accept-encoding\",\n]);\n\n/**\n * Proxy an unmatched request to the real upstream provider, record the\n * response as a fixture on disk and in memory, then relay the response\n * back to the original client.\n *\n * Returns `true` if the request was proxied (provider configured),\n * `false` if no upstream URL is configured for the given provider key.\n */\nexport async function proxyAndRecord(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n request: ChatCompletionRequest,\n providerKey: RecordProviderKey,\n pathname: string,\n fixtures: Fixture[],\n defaults: {\n record?: RecordConfig;\n logger: Logger;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n },\n rawBody?: string,\n): Promise<boolean> {\n const record = defaults.record;\n if (!record) return false;\n\n const providers = record.providers;\n const upstreamUrl = providers[providerKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return false;\n }\n\n const fixturePath = record.fixturePath ?? \"./fixtures/recorded\";\n let target: URL;\n try {\n target = resolveUpstreamUrl(upstreamUrl, pathname);\n } catch {\n defaults.logger.error(`Invalid upstream URL for provider \"${providerKey}\": ${upstreamUrl}`);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Invalid upstream URL: ${upstreamUrl}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);\n\n // Forward all request headers except hop-by-hop and client-set ones.\n const forwardHeaders: Record<string, string> = {};\n for (const [name, val] of Object.entries(req.headers)) {\n if (val !== undefined && !STRIP_HEADERS.has(name)) {\n forwardHeaders[name] = Array.isArray(val) ? val.join(\", \") : val;\n }\n }\n\n const requestBody = rawBody ?? JSON.stringify(request);\n\n // Make upstream request\n let upstreamStatus: number;\n let upstreamHeaders: http.IncomingHttpHeaders;\n let upstreamBody: string;\n let rawBuffer: Buffer;\n\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n // Detect streaming response and collapse if necessary\n const contentType = upstreamHeaders[\"content-type\"];\n const ctString = Array.isArray(contentType) ? contentType.join(\", \") : (contentType ?? \"\");\n const isBinaryStream = ctString.toLowerCase().includes(\"application/vnd.amazon.eventstream\");\n const collapsed = collapseStreamingResponse(\n ctString,\n providerKey,\n isBinaryStream ? rawBuffer : upstreamBody,\n defaults.logger,\n );\n\n let fixtureResponse: FixtureResponse;\n\n if (collapsed) {\n // Streaming response — use collapsed result\n defaults.logger.warn(`Streaming response detected (${ctString}) — collapsing to fixture`);\n if (collapsed.truncated) {\n defaults.logger.warn(\"Bedrock EventStream: CRC mismatch — response may be truncated\");\n }\n if (collapsed.droppedChunks && collapsed.droppedChunks > 0) {\n defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse`);\n }\n if (collapsed.content === \"\" && (!collapsed.toolCalls || collapsed.toolCalls.length === 0)) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n }\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n defaults.logger.warn(\n \"Collapsed response has both content and toolCalls — preferring toolCalls\",\n );\n }\n fixtureResponse = { toolCalls: collapsed.toolCalls };\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\" };\n }\n } else {\n // Non-streaming — try to parse as JSON\n let parsedResponse: unknown = null;\n try {\n parsedResponse = JSON.parse(upstreamBody);\n } catch {\n // Not JSON — could be an unknown format\n defaults.logger.warn(\"Upstream response is not valid JSON — saving as error fixture\");\n }\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch {\n /* not JSON */\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n\n // Build the match criteria from the (optionally transformed) request\n const matchRequest = defaults.requestTransform ? defaults.requestTransform(request) : request;\n const fixtureMatch = buildFixtureMatch(matchRequest);\n\n // Build and save the fixture\n const fixture: Fixture = { match: fixtureMatch, response: fixtureResponse };\n\n // Check if the match is empty (all undefined values) — warn but still save to disk\n const matchValues = Object.values(fixtureMatch);\n const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === undefined);\n if (isEmptyMatch) {\n defaults.logger.warn(\n \"Recorded fixture has empty match criteria — skipping in-memory registration\",\n );\n }\n\n // In proxy-only mode, skip recording to disk and in-memory caching\n if (!defaults.record?.proxyOnly) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `${providerKey}-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n let writtenToDisk = false;\n try {\n // Ensure fixture directory exists\n fs.mkdirSync(fixturePath, { recursive: true });\n\n // Collect warnings for the fixture file\n const warnings: string[] = [];\n if (isEmptyMatch) {\n warnings.push(\"Empty match criteria — this fixture will not match any request\");\n }\n if (collapsed?.truncated) {\n warnings.push(\"Stream response was truncated — fixture may be incomplete\");\n }\n\n // Auth headers are forwarded to upstream but excluded from saved fixtures for security\n const fileContent: Record<string, unknown> = { fixtures: [fixture] };\n if (warnings.length > 0) {\n fileContent._warning = warnings.join(\"; \");\n }\n fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), \"utf-8\");\n writtenToDisk = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n defaults.logger.error(`Failed to save fixture to disk: ${msg}`);\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n }\n\n if (writtenToDisk) {\n // Register in memory so subsequent identical requests match (skip if empty match)\n if (!isEmptyMatch) {\n fixtures.push(fixture);\n }\n defaults.logger.warn(`Response recorded → ${filepath}`);\n } else {\n defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);\n }\n } else {\n defaults.logger.info(`Proxied ${providerKey} request (proxy-only mode)`);\n }\n\n // Relay upstream response to client\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n res.end(isBinaryStream ? rawBuffer : upstreamBody);\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string; rawBuffer: Buffer }> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n const BODY_TIMEOUT_MS = 30_000;\n const req = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (res) => {\n res.setTimeout(BODY_TIMEOUT_MS, () => {\n req.destroy(new Error(`Upstream response timed out after ${BODY_TIMEOUT_MS / 1000}s`));\n });\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n });\n });\n },\n );\n req.on(\"timeout\", () => {\n req.destroy(\n new Error(\n `Upstream request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s: ${target.href}`,\n ),\n );\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Detect the response format from the parsed upstream JSON and convert\n * it into an llmock FixtureResponse.\n */\nfunction buildFixtureResponse(\n parsed: unknown,\n status: number,\n encodingFormat?: string,\n): FixtureResponse {\n if (parsed === null || parsed === undefined) {\n // Raw / unparseable response — save as error\n return {\n error: { message: \"Upstream returned non-JSON response\", type: \"proxy_error\" },\n status,\n };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Error response\n if (obj.error) {\n const err = obj.error as Record<string, unknown>;\n return {\n error: {\n message: String(err.message ?? \"Unknown error\"),\n type: String(err.type ?? \"api_error\"),\n code: err.code ? String(err.code) : undefined,\n },\n status,\n };\n }\n\n // OpenAI embeddings: { data: [{ embedding: [...] }] }\n if (Array.isArray(obj.data) && obj.data.length > 0) {\n const first = obj.data[0] as Record<string, unknown>;\n if (Array.isArray(first.embedding)) {\n return { embedding: first.embedding as number[] };\n }\n if (typeof first.embedding === \"string\" && encodingFormat === \"base64\") {\n try {\n const buf = Buffer.from(first.embedding, \"base64\");\n const floats = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n } catch {\n // Corrupted base64 or non-float32 data — fall through to error\n }\n }\n }\n\n // Direct embedding: { embedding: [...] }\n if (Array.isArray(obj.embedding)) {\n return { embedding: obj.embedding as number[] };\n }\n\n // OpenAI chat completion: { choices: [{ message: { content, tool_calls } }] }\n if (Array.isArray(obj.choices) && obj.choices.length > 0) {\n const choice = obj.choices[0] as Record<string, unknown>;\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n // Tool calls\n if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (message.tool_calls as Array<Record<string, unknown>>).map(\n (tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name),\n arguments: String(fn.arguments),\n };\n },\n );\n return { toolCalls };\n }\n // Text content\n if (typeof message.content === \"string\") {\n return { content: message.content };\n }\n }\n }\n\n // Anthropic: { content: [{ type: \"text\", text: \"...\" }] } or tool_use\n if (Array.isArray(obj.content) && obj.content.length > 0) {\n const blocks = obj.content as Array<Record<string, unknown>>;\n // Check for tool_use blocks first\n const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => ({\n name: String(b.name),\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input),\n }));\n return { toolCalls };\n }\n // Text blocks\n const textBlock = blocks.find((b) => b.type === \"text\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n\n // Gemini: { candidates: [{ content: { parts: [{ text: \"...\" }] } }] }\n if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {\n const candidate = obj.candidates[0] as Record<string, unknown>;\n const content = candidate.content as Record<string, unknown> | undefined;\n if (content && Array.isArray(content.parts)) {\n const parts = content.parts as Array<Record<string, unknown>>;\n // Tool calls (functionCall)\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCalls: ToolCall[] = fnCallParts.map((p) => {\n const fc = p.functionCall as Record<string, unknown>;\n return {\n name: String(fc.name),\n arguments: typeof fc.args === \"string\" ? fc.args : JSON.stringify(fc.args),\n };\n });\n return { toolCalls };\n }\n // Text\n const textPart = parts.find((p) => typeof p.text === \"string\");\n if (textPart && typeof textPart.text === \"string\") {\n return { content: textPart.text };\n }\n }\n }\n\n // Bedrock Converse: { output: { message: { role, content: [{ text }, { toolUse }] } } }\n if (obj.output && typeof obj.output === \"object\") {\n const output = obj.output as Record<string, unknown>;\n const msg = output.message as Record<string, unknown> | undefined;\n if (msg && Array.isArray(msg.content)) {\n const blocks = msg.content as Array<Record<string, unknown>>;\n const toolUseBlocks = blocks.filter((b) => b.toolUse);\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => {\n const tu = b.toolUse as Record<string, unknown>;\n return {\n name: String(tu.name ?? \"\"),\n arguments: typeof tu.input === \"string\" ? tu.input : JSON.stringify(tu.input),\n };\n });\n return { toolCalls };\n }\n const textBlock = blocks.find((b) => typeof b.text === \"string\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n }\n\n // Ollama: { message: { content: \"...\", tool_calls: [...] } }\n if (obj.message && typeof obj.message === \"object\") {\n const msg = obj.message as Record<string, unknown>;\n // Tool calls (check before content — Ollama sends content: \"\" alongside tool_calls)\n if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (msg.tool_calls as Array<Record<string, unknown>>)\n .filter((tc) => tc.function != null)\n .map((tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n };\n });\n return { toolCalls };\n }\n if (typeof msg.content === \"string\" && msg.content.length > 0) {\n return { content: msg.content };\n }\n // Ollama message with content array (like Cohere)\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const first = msg.content[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") {\n return { content: first.text };\n }\n }\n }\n\n // Fallback: unknown format — save as error\n return {\n error: {\n message: \"Could not detect response format from upstream\",\n type: \"proxy_error\",\n },\n status,\n };\n}\n\n/**\n * Derive fixture match criteria from the original request.\n */\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n} {\n // Embedding request\n if (request.embeddingInput) {\n return { inputText: request.embeddingInput };\n }\n\n // Chat request — match on the last user message\n const lastUser = getLastMessageByRole(request.messages ?? [], \"user\");\n if (lastUser) {\n const text = getTextContent(lastUser.content);\n if (text) {\n return { userMessage: text };\n }\n }\n\n return {};\n}\n"],"mappings":"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"}
package/dist/recorder.js CHANGED
@@ -111,27 +111,29 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
111
111
  const matchValues = Object.values(fixtureMatch);
112
112
  const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === void 0);
113
113
  if (isEmptyMatch) defaults.logger.warn("Recorded fixture has empty match criteria — skipping in-memory registration");
114
- const filename = `${providerKey}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${crypto.randomUUID().slice(0, 8)}.json`;
115
- const filepath = path.join(fixturePath, filename);
116
- let writtenToDisk = false;
117
- try {
118
- fs.mkdirSync(fixturePath, { recursive: true });
119
- const warnings = [];
120
- if (isEmptyMatch) warnings.push("Empty match criteria — this fixture will not match any request");
121
- if (collapsed?.truncated) warnings.push("Stream response was truncated — fixture may be incomplete");
122
- const fileContent = { fixtures: [fixture] };
123
- if (warnings.length > 0) fileContent._warning = warnings.join("; ");
124
- fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), "utf-8");
125
- writtenToDisk = true;
126
- } catch (err) {
127
- const msg = err instanceof Error ? err.message : "Unknown filesystem error";
128
- defaults.logger.error(`Failed to save fixture to disk: ${msg}`);
129
- res.setHeader("X-LLMock-Record-Error", msg);
130
- }
131
- if (writtenToDisk) {
132
- if (!isEmptyMatch) fixtures.push(fixture);
133
- defaults.logger.warn(`Response recorded → ${filepath}`);
134
- } else defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);
114
+ if (!defaults.record?.proxyOnly) {
115
+ const filename = `${providerKey}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${crypto.randomUUID().slice(0, 8)}.json`;
116
+ const filepath = path.join(fixturePath, filename);
117
+ let writtenToDisk = false;
118
+ try {
119
+ fs.mkdirSync(fixturePath, { recursive: true });
120
+ const warnings = [];
121
+ if (isEmptyMatch) warnings.push("Empty match criteriathis fixture will not match any request");
122
+ if (collapsed?.truncated) warnings.push("Stream response was truncated — fixture may be incomplete");
123
+ const fileContent = { fixtures: [fixture] };
124
+ if (warnings.length > 0) fileContent._warning = warnings.join("; ");
125
+ fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), "utf-8");
126
+ writtenToDisk = true;
127
+ } catch (err) {
128
+ const msg = err instanceof Error ? err.message : "Unknown filesystem error";
129
+ defaults.logger.error(`Failed to save fixture to disk: ${msg}`);
130
+ res.setHeader("X-LLMock-Record-Error", msg);
131
+ }
132
+ if (writtenToDisk) {
133
+ if (!isEmptyMatch) fixtures.push(fixture);
134
+ defaults.logger.warn(`Response recorded ${filepath}`);
135
+ } else defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);
136
+ } else defaults.logger.info(`Proxied ${providerKey} request (proxy-only mode)`);
135
137
  const relayHeaders = {};
136
138
  if (ctString) relayHeaders["Content-Type"] = ctString;
137
139
  res.writeHead(upstreamStatus, relayHeaders);
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.js","names":[],"sources":["../src/recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n RecordConfig,\n RecordProviderKey,\n ToolCall,\n} from \"./types.js\";\nimport { getLastMessageByRole, getTextContent } from \"./router.js\";\nimport type { Logger } from \"./logger.js\";\nimport { collapseStreamingResponse } from \"./stream-collapse.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\n\n/** Headers to strip when proxying — hop-by-hop (RFC 2616 §13.5.1) + client-set. */\nconst STRIP_HEADERS = new Set([\n // Hop-by-hop (RFC 2616 §13.5.1)\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authorization\",\n \"proxy-authenticate\",\n // Set by HTTP client from the target URL / body\n \"host\",\n \"content-length\",\n // Not relevant for LLM APIs; avoid leaking or mismatched encoding\n \"cookie\",\n \"accept-encoding\",\n]);\n\n/**\n * Proxy an unmatched request to the real upstream provider, record the\n * response as a fixture on disk and in memory, then relay the response\n * back to the original client.\n *\n * Returns `true` if the request was proxied (provider configured),\n * `false` if no upstream URL is configured for the given provider key.\n */\nexport async function proxyAndRecord(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n request: ChatCompletionRequest,\n providerKey: RecordProviderKey,\n pathname: string,\n fixtures: Fixture[],\n defaults: {\n record?: RecordConfig;\n logger: Logger;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n },\n rawBody?: string,\n): Promise<boolean> {\n const record = defaults.record;\n if (!record) return false;\n\n const providers = record.providers;\n const upstreamUrl = providers[providerKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return false;\n }\n\n const fixturePath = record.fixturePath ?? \"./fixtures/recorded\";\n let target: URL;\n try {\n target = resolveUpstreamUrl(upstreamUrl, pathname);\n } catch {\n defaults.logger.error(`Invalid upstream URL for provider \"${providerKey}\": ${upstreamUrl}`);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Invalid upstream URL: ${upstreamUrl}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);\n\n // Forward all request headers except hop-by-hop and client-set ones.\n const forwardHeaders: Record<string, string> = {};\n for (const [name, val] of Object.entries(req.headers)) {\n if (val !== undefined && !STRIP_HEADERS.has(name)) {\n forwardHeaders[name] = Array.isArray(val) ? val.join(\", \") : val;\n }\n }\n\n const requestBody = rawBody ?? JSON.stringify(request);\n\n // Make upstream request\n let upstreamStatus: number;\n let upstreamHeaders: http.IncomingHttpHeaders;\n let upstreamBody: string;\n let rawBuffer: Buffer;\n\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n // Detect streaming response and collapse if necessary\n const contentType = upstreamHeaders[\"content-type\"];\n const ctString = Array.isArray(contentType) ? contentType.join(\", \") : (contentType ?? \"\");\n const isBinaryStream = ctString.toLowerCase().includes(\"application/vnd.amazon.eventstream\");\n const collapsed = collapseStreamingResponse(\n ctString,\n providerKey,\n isBinaryStream ? rawBuffer : upstreamBody,\n defaults.logger,\n );\n\n let fixtureResponse: FixtureResponse;\n\n if (collapsed) {\n // Streaming response — use collapsed result\n defaults.logger.warn(`Streaming response detected (${ctString}) — collapsing to fixture`);\n if (collapsed.truncated) {\n defaults.logger.warn(\"Bedrock EventStream: CRC mismatch — response may be truncated\");\n }\n if (collapsed.droppedChunks && collapsed.droppedChunks > 0) {\n defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse`);\n }\n if (collapsed.content === \"\" && (!collapsed.toolCalls || collapsed.toolCalls.length === 0)) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n }\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n defaults.logger.warn(\n \"Collapsed response has both content and toolCalls — preferring toolCalls\",\n );\n }\n fixtureResponse = { toolCalls: collapsed.toolCalls };\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\" };\n }\n } else {\n // Non-streaming — try to parse as JSON\n let parsedResponse: unknown = null;\n try {\n parsedResponse = JSON.parse(upstreamBody);\n } catch {\n // Not JSON — could be an unknown format\n defaults.logger.warn(\"Upstream response is not valid JSON — saving as error fixture\");\n }\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch {\n /* not JSON */\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n\n // Build the match criteria from the (optionally transformed) request\n const matchRequest = defaults.requestTransform ? defaults.requestTransform(request) : request;\n const fixtureMatch = buildFixtureMatch(matchRequest);\n\n // Build and save the fixture\n const fixture: Fixture = { match: fixtureMatch, response: fixtureResponse };\n\n // Check if the match is empty (all undefined values) — warn but still save to disk\n const matchValues = Object.values(fixtureMatch);\n const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === undefined);\n if (isEmptyMatch) {\n defaults.logger.warn(\n \"Recorded fixture has empty match criteria — skipping in-memory registration\",\n );\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `${providerKey}-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n let writtenToDisk = false;\n try {\n // Ensure fixture directory exists\n fs.mkdirSync(fixturePath, { recursive: true });\n\n // Collect warnings for the fixture file\n const warnings: string[] = [];\n if (isEmptyMatch) {\n warnings.push(\"Empty match criteria — this fixture will not match any request\");\n }\n if (collapsed?.truncated) {\n warnings.push(\"Stream response was truncated — fixture may be incomplete\");\n }\n\n // Auth headers are forwarded to upstream but excluded from saved fixtures for security\n const fileContent: Record<string, unknown> = { fixtures: [fixture] };\n if (warnings.length > 0) {\n fileContent._warning = warnings.join(\"; \");\n }\n fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), \"utf-8\");\n writtenToDisk = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n defaults.logger.error(`Failed to save fixture to disk: ${msg}`);\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n }\n\n if (writtenToDisk) {\n // Register in memory so subsequent identical requests match (skip if empty match)\n if (!isEmptyMatch) {\n fixtures.push(fixture);\n }\n defaults.logger.warn(`Response recorded → ${filepath}`);\n } else {\n defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);\n }\n\n // Relay upstream response to client\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n res.end(isBinaryStream ? rawBuffer : upstreamBody);\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string; rawBuffer: Buffer }> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n const BODY_TIMEOUT_MS = 30_000;\n const req = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (res) => {\n res.setTimeout(BODY_TIMEOUT_MS, () => {\n req.destroy(new Error(`Upstream response timed out after ${BODY_TIMEOUT_MS / 1000}s`));\n });\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n });\n });\n },\n );\n req.on(\"timeout\", () => {\n req.destroy(\n new Error(\n `Upstream request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s: ${target.href}`,\n ),\n );\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Detect the response format from the parsed upstream JSON and convert\n * it into an llmock FixtureResponse.\n */\nfunction buildFixtureResponse(\n parsed: unknown,\n status: number,\n encodingFormat?: string,\n): FixtureResponse {\n if (parsed === null || parsed === undefined) {\n // Raw / unparseable response — save as error\n return {\n error: { message: \"Upstream returned non-JSON response\", type: \"proxy_error\" },\n status,\n };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Error response\n if (obj.error) {\n const err = obj.error as Record<string, unknown>;\n return {\n error: {\n message: String(err.message ?? \"Unknown error\"),\n type: String(err.type ?? \"api_error\"),\n code: err.code ? String(err.code) : undefined,\n },\n status,\n };\n }\n\n // OpenAI embeddings: { data: [{ embedding: [...] }] }\n if (Array.isArray(obj.data) && obj.data.length > 0) {\n const first = obj.data[0] as Record<string, unknown>;\n if (Array.isArray(first.embedding)) {\n return { embedding: first.embedding as number[] };\n }\n if (typeof first.embedding === \"string\" && encodingFormat === \"base64\") {\n try {\n const buf = Buffer.from(first.embedding, \"base64\");\n const floats = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n } catch {\n // Corrupted base64 or non-float32 data — fall through to error\n }\n }\n }\n\n // Direct embedding: { embedding: [...] }\n if (Array.isArray(obj.embedding)) {\n return { embedding: obj.embedding as number[] };\n }\n\n // OpenAI chat completion: { choices: [{ message: { content, tool_calls } }] }\n if (Array.isArray(obj.choices) && obj.choices.length > 0) {\n const choice = obj.choices[0] as Record<string, unknown>;\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n // Tool calls\n if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (message.tool_calls as Array<Record<string, unknown>>).map(\n (tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name),\n arguments: String(fn.arguments),\n };\n },\n );\n return { toolCalls };\n }\n // Text content\n if (typeof message.content === \"string\") {\n return { content: message.content };\n }\n }\n }\n\n // Anthropic: { content: [{ type: \"text\", text: \"...\" }] } or tool_use\n if (Array.isArray(obj.content) && obj.content.length > 0) {\n const blocks = obj.content as Array<Record<string, unknown>>;\n // Check for tool_use blocks first\n const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => ({\n name: String(b.name),\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input),\n }));\n return { toolCalls };\n }\n // Text blocks\n const textBlock = blocks.find((b) => b.type === \"text\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n\n // Gemini: { candidates: [{ content: { parts: [{ text: \"...\" }] } }] }\n if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {\n const candidate = obj.candidates[0] as Record<string, unknown>;\n const content = candidate.content as Record<string, unknown> | undefined;\n if (content && Array.isArray(content.parts)) {\n const parts = content.parts as Array<Record<string, unknown>>;\n // Tool calls (functionCall)\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCalls: ToolCall[] = fnCallParts.map((p) => {\n const fc = p.functionCall as Record<string, unknown>;\n return {\n name: String(fc.name),\n arguments: typeof fc.args === \"string\" ? fc.args : JSON.stringify(fc.args),\n };\n });\n return { toolCalls };\n }\n // Text\n const textPart = parts.find((p) => typeof p.text === \"string\");\n if (textPart && typeof textPart.text === \"string\") {\n return { content: textPart.text };\n }\n }\n }\n\n // Bedrock Converse: { output: { message: { role, content: [{ text }, { toolUse }] } } }\n if (obj.output && typeof obj.output === \"object\") {\n const output = obj.output as Record<string, unknown>;\n const msg = output.message as Record<string, unknown> | undefined;\n if (msg && Array.isArray(msg.content)) {\n const blocks = msg.content as Array<Record<string, unknown>>;\n const toolUseBlocks = blocks.filter((b) => b.toolUse);\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => {\n const tu = b.toolUse as Record<string, unknown>;\n return {\n name: String(tu.name ?? \"\"),\n arguments: typeof tu.input === \"string\" ? tu.input : JSON.stringify(tu.input),\n };\n });\n return { toolCalls };\n }\n const textBlock = blocks.find((b) => typeof b.text === \"string\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n }\n\n // Ollama: { message: { content: \"...\", tool_calls: [...] } }\n if (obj.message && typeof obj.message === \"object\") {\n const msg = obj.message as Record<string, unknown>;\n // Tool calls (check before content — Ollama sends content: \"\" alongside tool_calls)\n if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (msg.tool_calls as Array<Record<string, unknown>>)\n .filter((tc) => tc.function != null)\n .map((tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n };\n });\n return { toolCalls };\n }\n if (typeof msg.content === \"string\" && msg.content.length > 0) {\n return { content: msg.content };\n }\n // Ollama message with content array (like Cohere)\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const first = msg.content[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") {\n return { content: first.text };\n }\n }\n }\n\n // Fallback: unknown format — save as error\n return {\n error: {\n message: \"Could not detect response format from upstream\",\n type: \"proxy_error\",\n },\n status,\n };\n}\n\n/**\n * Derive fixture match criteria from the original request.\n */\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n} {\n // Embedding request\n if (request.embeddingInput) {\n return { inputText: request.embeddingInput };\n }\n\n // Chat request — match on the last user message\n const lastUser = getLastMessageByRole(request.messages ?? [], \"user\");\n if (lastUser) {\n const text = getTextContent(lastUser.content);\n if (text) {\n return { userMessage: text };\n }\n }\n\n return {};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recorder.js","names":[],"sources":["../src/recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n RecordConfig,\n RecordProviderKey,\n ToolCall,\n} from \"./types.js\";\nimport { getLastMessageByRole, getTextContent } from \"./router.js\";\nimport type { Logger } from \"./logger.js\";\nimport { collapseStreamingResponse } from \"./stream-collapse.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\n\n/** Headers to strip when proxying — hop-by-hop (RFC 2616 §13.5.1) + client-set. */\nconst STRIP_HEADERS = new Set([\n // Hop-by-hop (RFC 2616 §13.5.1)\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authorization\",\n \"proxy-authenticate\",\n // Set by HTTP client from the target URL / body\n \"host\",\n \"content-length\",\n // Not relevant for LLM APIs; avoid leaking or mismatched encoding\n \"cookie\",\n \"accept-encoding\",\n]);\n\n/**\n * Proxy an unmatched request to the real upstream provider, record the\n * response as a fixture on disk and in memory, then relay the response\n * back to the original client.\n *\n * Returns `true` if the request was proxied (provider configured),\n * `false` if no upstream URL is configured for the given provider key.\n */\nexport async function proxyAndRecord(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n request: ChatCompletionRequest,\n providerKey: RecordProviderKey,\n pathname: string,\n fixtures: Fixture[],\n defaults: {\n record?: RecordConfig;\n logger: Logger;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n },\n rawBody?: string,\n): Promise<boolean> {\n const record = defaults.record;\n if (!record) return false;\n\n const providers = record.providers;\n const upstreamUrl = providers[providerKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return false;\n }\n\n const fixturePath = record.fixturePath ?? \"./fixtures/recorded\";\n let target: URL;\n try {\n target = resolveUpstreamUrl(upstreamUrl, pathname);\n } catch {\n defaults.logger.error(`Invalid upstream URL for provider \"${providerKey}\": ${upstreamUrl}`);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Invalid upstream URL: ${upstreamUrl}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);\n\n // Forward all request headers except hop-by-hop and client-set ones.\n const forwardHeaders: Record<string, string> = {};\n for (const [name, val] of Object.entries(req.headers)) {\n if (val !== undefined && !STRIP_HEADERS.has(name)) {\n forwardHeaders[name] = Array.isArray(val) ? val.join(\", \") : val;\n }\n }\n\n const requestBody = rawBody ?? JSON.stringify(request);\n\n // Make upstream request\n let upstreamStatus: number;\n let upstreamHeaders: http.IncomingHttpHeaders;\n let upstreamBody: string;\n let rawBuffer: Buffer;\n\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return true;\n }\n\n // Detect streaming response and collapse if necessary\n const contentType = upstreamHeaders[\"content-type\"];\n const ctString = Array.isArray(contentType) ? contentType.join(\", \") : (contentType ?? \"\");\n const isBinaryStream = ctString.toLowerCase().includes(\"application/vnd.amazon.eventstream\");\n const collapsed = collapseStreamingResponse(\n ctString,\n providerKey,\n isBinaryStream ? rawBuffer : upstreamBody,\n defaults.logger,\n );\n\n let fixtureResponse: FixtureResponse;\n\n if (collapsed) {\n // Streaming response — use collapsed result\n defaults.logger.warn(`Streaming response detected (${ctString}) — collapsing to fixture`);\n if (collapsed.truncated) {\n defaults.logger.warn(\"Bedrock EventStream: CRC mismatch — response may be truncated\");\n }\n if (collapsed.droppedChunks && collapsed.droppedChunks > 0) {\n defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse`);\n }\n if (collapsed.content === \"\" && (!collapsed.toolCalls || collapsed.toolCalls.length === 0)) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n }\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n defaults.logger.warn(\n \"Collapsed response has both content and toolCalls — preferring toolCalls\",\n );\n }\n fixtureResponse = { toolCalls: collapsed.toolCalls };\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\" };\n }\n } else {\n // Non-streaming — try to parse as JSON\n let parsedResponse: unknown = null;\n try {\n parsedResponse = JSON.parse(upstreamBody);\n } catch {\n // Not JSON — could be an unknown format\n defaults.logger.warn(\"Upstream response is not valid JSON — saving as error fixture\");\n }\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch {\n /* not JSON */\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n\n // Build the match criteria from the (optionally transformed) request\n const matchRequest = defaults.requestTransform ? defaults.requestTransform(request) : request;\n const fixtureMatch = buildFixtureMatch(matchRequest);\n\n // Build and save the fixture\n const fixture: Fixture = { match: fixtureMatch, response: fixtureResponse };\n\n // Check if the match is empty (all undefined values) — warn but still save to disk\n const matchValues = Object.values(fixtureMatch);\n const isEmptyMatch = matchValues.length === 0 || matchValues.every((v) => v === undefined);\n if (isEmptyMatch) {\n defaults.logger.warn(\n \"Recorded fixture has empty match criteria — skipping in-memory registration\",\n );\n }\n\n // In proxy-only mode, skip recording to disk and in-memory caching\n if (!defaults.record?.proxyOnly) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `${providerKey}-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n let writtenToDisk = false;\n try {\n // Ensure fixture directory exists\n fs.mkdirSync(fixturePath, { recursive: true });\n\n // Collect warnings for the fixture file\n const warnings: string[] = [];\n if (isEmptyMatch) {\n warnings.push(\"Empty match criteria — this fixture will not match any request\");\n }\n if (collapsed?.truncated) {\n warnings.push(\"Stream response was truncated — fixture may be incomplete\");\n }\n\n // Auth headers are forwarded to upstream but excluded from saved fixtures for security\n const fileContent: Record<string, unknown> = { fixtures: [fixture] };\n if (warnings.length > 0) {\n fileContent._warning = warnings.join(\"; \");\n }\n fs.writeFileSync(filepath, JSON.stringify(fileContent, null, 2), \"utf-8\");\n writtenToDisk = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n defaults.logger.error(`Failed to save fixture to disk: ${msg}`);\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n }\n\n if (writtenToDisk) {\n // Register in memory so subsequent identical requests match (skip if empty match)\n if (!isEmptyMatch) {\n fixtures.push(fixture);\n }\n defaults.logger.warn(`Response recorded → ${filepath}`);\n } else {\n defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);\n }\n } else {\n defaults.logger.info(`Proxied ${providerKey} request (proxy-only mode)`);\n }\n\n // Relay upstream response to client\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n res.end(isBinaryStream ? rawBuffer : upstreamBody);\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string; rawBuffer: Buffer }> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n const BODY_TIMEOUT_MS = 30_000;\n const req = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (res) => {\n res.setTimeout(BODY_TIMEOUT_MS, () => {\n req.destroy(new Error(`Upstream response timed out after ${BODY_TIMEOUT_MS / 1000}s`));\n });\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n });\n });\n },\n );\n req.on(\"timeout\", () => {\n req.destroy(\n new Error(\n `Upstream request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s: ${target.href}`,\n ),\n );\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Detect the response format from the parsed upstream JSON and convert\n * it into an llmock FixtureResponse.\n */\nfunction buildFixtureResponse(\n parsed: unknown,\n status: number,\n encodingFormat?: string,\n): FixtureResponse {\n if (parsed === null || parsed === undefined) {\n // Raw / unparseable response — save as error\n return {\n error: { message: \"Upstream returned non-JSON response\", type: \"proxy_error\" },\n status,\n };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Error response\n if (obj.error) {\n const err = obj.error as Record<string, unknown>;\n return {\n error: {\n message: String(err.message ?? \"Unknown error\"),\n type: String(err.type ?? \"api_error\"),\n code: err.code ? String(err.code) : undefined,\n },\n status,\n };\n }\n\n // OpenAI embeddings: { data: [{ embedding: [...] }] }\n if (Array.isArray(obj.data) && obj.data.length > 0) {\n const first = obj.data[0] as Record<string, unknown>;\n if (Array.isArray(first.embedding)) {\n return { embedding: first.embedding as number[] };\n }\n if (typeof first.embedding === \"string\" && encodingFormat === \"base64\") {\n try {\n const buf = Buffer.from(first.embedding, \"base64\");\n const floats = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n } catch {\n // Corrupted base64 or non-float32 data — fall through to error\n }\n }\n }\n\n // Direct embedding: { embedding: [...] }\n if (Array.isArray(obj.embedding)) {\n return { embedding: obj.embedding as number[] };\n }\n\n // OpenAI chat completion: { choices: [{ message: { content, tool_calls } }] }\n if (Array.isArray(obj.choices) && obj.choices.length > 0) {\n const choice = obj.choices[0] as Record<string, unknown>;\n const message = choice.message as Record<string, unknown> | undefined;\n if (message) {\n // Tool calls\n if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (message.tool_calls as Array<Record<string, unknown>>).map(\n (tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name),\n arguments: String(fn.arguments),\n };\n },\n );\n return { toolCalls };\n }\n // Text content\n if (typeof message.content === \"string\") {\n return { content: message.content };\n }\n }\n }\n\n // Anthropic: { content: [{ type: \"text\", text: \"...\" }] } or tool_use\n if (Array.isArray(obj.content) && obj.content.length > 0) {\n const blocks = obj.content as Array<Record<string, unknown>>;\n // Check for tool_use blocks first\n const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => ({\n name: String(b.name),\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input),\n }));\n return { toolCalls };\n }\n // Text blocks\n const textBlock = blocks.find((b) => b.type === \"text\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n\n // Gemini: { candidates: [{ content: { parts: [{ text: \"...\" }] } }] }\n if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {\n const candidate = obj.candidates[0] as Record<string, unknown>;\n const content = candidate.content as Record<string, unknown> | undefined;\n if (content && Array.isArray(content.parts)) {\n const parts = content.parts as Array<Record<string, unknown>>;\n // Tool calls (functionCall)\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCalls: ToolCall[] = fnCallParts.map((p) => {\n const fc = p.functionCall as Record<string, unknown>;\n return {\n name: String(fc.name),\n arguments: typeof fc.args === \"string\" ? fc.args : JSON.stringify(fc.args),\n };\n });\n return { toolCalls };\n }\n // Text\n const textPart = parts.find((p) => typeof p.text === \"string\");\n if (textPart && typeof textPart.text === \"string\") {\n return { content: textPart.text };\n }\n }\n }\n\n // Bedrock Converse: { output: { message: { role, content: [{ text }, { toolUse }] } } }\n if (obj.output && typeof obj.output === \"object\") {\n const output = obj.output as Record<string, unknown>;\n const msg = output.message as Record<string, unknown> | undefined;\n if (msg && Array.isArray(msg.content)) {\n const blocks = msg.content as Array<Record<string, unknown>>;\n const toolUseBlocks = blocks.filter((b) => b.toolUse);\n if (toolUseBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolUseBlocks.map((b) => {\n const tu = b.toolUse as Record<string, unknown>;\n return {\n name: String(tu.name ?? \"\"),\n arguments: typeof tu.input === \"string\" ? tu.input : JSON.stringify(tu.input),\n };\n });\n return { toolCalls };\n }\n const textBlock = blocks.find((b) => typeof b.text === \"string\");\n if (textBlock && typeof textBlock.text === \"string\") {\n return { content: textBlock.text };\n }\n }\n }\n\n // Ollama: { message: { content: \"...\", tool_calls: [...] } }\n if (obj.message && typeof obj.message === \"object\") {\n const msg = obj.message as Record<string, unknown>;\n // Tool calls (check before content — Ollama sends content: \"\" alongside tool_calls)\n if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {\n const toolCalls: ToolCall[] = (msg.tool_calls as Array<Record<string, unknown>>)\n .filter((tc) => tc.function != null)\n .map((tc) => {\n const fn = tc.function as Record<string, unknown>;\n return {\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n };\n });\n return { toolCalls };\n }\n if (typeof msg.content === \"string\" && msg.content.length > 0) {\n return { content: msg.content };\n }\n // Ollama message with content array (like Cohere)\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const first = msg.content[0] as Record<string, unknown>;\n if (typeof first.text === \"string\") {\n return { content: first.text };\n }\n }\n }\n\n // Fallback: unknown format — save as error\n return {\n error: {\n message: \"Could not detect response format from upstream\",\n type: \"proxy_error\",\n },\n status,\n };\n}\n\n/**\n * Derive fixture match criteria from the original request.\n */\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n} {\n // Embedding request\n if (request.embeddingInput) {\n return { inputText: request.embeddingInput };\n }\n\n // Chat request — match on the last user message\n const lastUser = getLastMessageByRole(request.messages ?? [], \"user\");\n if (lastUser) {\n const text = getTextContent(lastUser.content);\n if (text) {\n return { userMessage: text };\n }\n }\n\n return {};\n}\n"],"mappings":"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"}
package/dist/suite.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  const require_a2a_mock = require('./a2a-mock.cjs');
2
2
  const require_llmock = require('./llmock.cjs');
3
3
  const require_mcp_mock = require('./mcp-mock.cjs');
4
+ const require_agui_mock = require('./agui-mock.cjs');
4
5
  const require_vector_mock = require('./vector-mock.cjs');
5
6
 
6
7
  //#region src/suite.ts
@@ -9,6 +10,7 @@ async function createMockSuite(options = {}) {
9
10
  let mcp;
10
11
  let a2a;
11
12
  let vector;
13
+ let agui;
12
14
  if (options.mcp) {
13
15
  mcp = new require_mcp_mock.MCPMock(options.mcp);
14
16
  llm.mount("/mcp", mcp);
@@ -21,11 +23,16 @@ async function createMockSuite(options = {}) {
21
23
  vector = new require_vector_mock.VectorMock(options.vector);
22
24
  llm.mount("/vector", vector);
23
25
  }
26
+ if (options.agui) {
27
+ agui = new require_agui_mock.AGUIMock(options.agui);
28
+ llm.mount("/agui", agui);
29
+ }
24
30
  return {
25
31
  llm,
26
32
  mcp,
27
33
  a2a,
28
34
  vector,
35
+ agui,
29
36
  async start() {
30
37
  await llm.start();
31
38
  },
@@ -37,6 +44,7 @@ async function createMockSuite(options = {}) {
37
44
  if (mcp) mcp.reset();
38
45
  if (a2a) a2a.reset();
39
46
  if (vector) vector.reset();
47
+ if (agui) agui.reset();
40
48
  }
41
49
  };
42
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"suite.cjs","names":["LLMock","MCPMock","A2AMock","VectorMock"],"sources":["../src/suite.ts"],"sourcesContent":["import { LLMock } from \"./llmock.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport type { MockServerOptions } from \"./types.js\";\nimport type { MCPMockOptions } from \"./mcp-types.js\";\nimport type { A2AMockOptions } from \"./a2a-types.js\";\nimport type { VectorMockOptions } from \"./vector-types.js\";\n\nexport interface MockSuiteOptions {\n llm?: MockServerOptions;\n mcp?: MCPMockOptions;\n a2a?: A2AMockOptions;\n vector?: VectorMockOptions;\n}\n\nexport interface MockSuite {\n llm: LLMock;\n mcp?: MCPMock;\n a2a?: A2AMock;\n vector?: VectorMock;\n start(): Promise<void>;\n stop(): Promise<void>;\n reset(): void;\n}\n\nexport async function createMockSuite(options: MockSuiteOptions = {}): Promise<MockSuite> {\n const llm = new LLMock(options.llm);\n let mcp: MCPMock | undefined;\n let a2a: A2AMock | undefined;\n let vector: VectorMock | undefined;\n\n if (options.mcp) {\n mcp = new MCPMock(options.mcp);\n llm.mount(\"/mcp\", mcp);\n }\n\n if (options.a2a) {\n a2a = new A2AMock(options.a2a);\n llm.mount(\"/a2a\", a2a);\n }\n\n if (options.vector) {\n vector = new VectorMock(options.vector);\n llm.mount(\"/vector\", vector);\n }\n\n return {\n llm,\n mcp,\n a2a,\n vector,\n async start() {\n await llm.start();\n },\n async stop() {\n await llm.stop();\n },\n reset() {\n llm.reset();\n if (mcp) mcp.reset();\n if (a2a) a2a.reset();\n if (vector) vector.reset();\n },\n };\n}\n"],"mappings":";;;;;;AA0BA,eAAsB,gBAAgB,UAA4B,EAAE,EAAsB;CACxF,MAAM,MAAM,IAAIA,sBAAO,QAAQ,IAAI;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,KAAI,QAAQ,KAAK;AACf,QAAM,IAAIC,yBAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,KAAK;AACf,QAAM,IAAIC,yBAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,QAAQ;AAClB,WAAS,IAAIC,+BAAW,QAAQ,OAAO;AACvC,MAAI,MAAM,WAAW,OAAO;;AAG9B,QAAO;EACL;EACA;EACA;EACA;EACA,MAAM,QAAQ;AACZ,SAAM,IAAI,OAAO;;EAEnB,MAAM,OAAO;AACX,SAAM,IAAI,MAAM;;EAElB,QAAQ;AACN,OAAI,OAAO;AACX,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,OAAQ,QAAO,OAAO;;EAE7B"}
1
+ {"version":3,"file":"suite.cjs","names":["LLMock","MCPMock","A2AMock","VectorMock","AGUIMock"],"sources":["../src/suite.ts"],"sourcesContent":["import { LLMock } from \"./llmock.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport type { MockServerOptions } from \"./types.js\";\nimport type { MCPMockOptions } from \"./mcp-types.js\";\nimport type { A2AMockOptions } from \"./a2a-types.js\";\nimport type { VectorMockOptions } from \"./vector-types.js\";\nimport type { AGUIMockOptions } from \"./agui-types.js\";\n\nexport interface MockSuiteOptions {\n llm?: MockServerOptions;\n mcp?: MCPMockOptions;\n a2a?: A2AMockOptions;\n vector?: VectorMockOptions;\n agui?: AGUIMockOptions;\n}\n\nexport interface MockSuite {\n llm: LLMock;\n mcp?: MCPMock;\n a2a?: A2AMock;\n vector?: VectorMock;\n agui?: AGUIMock;\n start(): Promise<void>;\n stop(): Promise<void>;\n reset(): void;\n}\n\nexport async function createMockSuite(options: MockSuiteOptions = {}): Promise<MockSuite> {\n const llm = new LLMock(options.llm);\n let mcp: MCPMock | undefined;\n let a2a: A2AMock | undefined;\n let vector: VectorMock | undefined;\n let agui: AGUIMock | undefined;\n\n if (options.mcp) {\n mcp = new MCPMock(options.mcp);\n llm.mount(\"/mcp\", mcp);\n }\n\n if (options.a2a) {\n a2a = new A2AMock(options.a2a);\n llm.mount(\"/a2a\", a2a);\n }\n\n if (options.vector) {\n vector = new VectorMock(options.vector);\n llm.mount(\"/vector\", vector);\n }\n\n if (options.agui) {\n agui = new AGUIMock(options.agui);\n llm.mount(\"/agui\", agui);\n }\n\n return {\n llm,\n mcp,\n a2a,\n vector,\n agui,\n async start() {\n await llm.start();\n },\n async stop() {\n await llm.stop();\n },\n reset() {\n llm.reset();\n if (mcp) mcp.reset();\n if (a2a) a2a.reset();\n if (vector) vector.reset();\n if (agui) agui.reset();\n },\n };\n}\n"],"mappings":";;;;;;;AA8BA,eAAsB,gBAAgB,UAA4B,EAAE,EAAsB;CACxF,MAAM,MAAM,IAAIA,sBAAO,QAAQ,IAAI;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,KAAI,QAAQ,KAAK;AACf,QAAM,IAAIC,yBAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,KAAK;AACf,QAAM,IAAIC,yBAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,QAAQ;AAClB,WAAS,IAAIC,+BAAW,QAAQ,OAAO;AACvC,MAAI,MAAM,WAAW,OAAO;;AAG9B,KAAI,QAAQ,MAAM;AAChB,SAAO,IAAIC,2BAAS,QAAQ,KAAK;AACjC,MAAI,MAAM,SAAS,KAAK;;AAG1B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,MAAM,QAAQ;AACZ,SAAM,IAAI,OAAO;;EAEnB,MAAM,OAAO;AACX,SAAM,IAAI,MAAM;;EAElB,QAAQ;AACN,OAAI,OAAO;AACX,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,OAAQ,QAAO,OAAO;AAC1B,OAAI,KAAM,MAAK,OAAO;;EAEzB"}
package/dist/suite.d.cts CHANGED
@@ -3,9 +3,11 @@ import { A2AMockOptions } from "./a2a-types.cjs";
3
3
  import { A2AMock } from "./a2a-mock.cjs";
4
4
  import { LLMock } from "./llmock.cjs";
5
5
  import { MCPMockOptions } from "./mcp-types.cjs";
6
+ import { AGUIMockOptions } from "./agui-types.cjs";
6
7
  import { VectorMockOptions } from "./vector-types.cjs";
7
8
  import { MCPMock } from "./mcp-mock.cjs";
8
9
  import { VectorMock } from "./vector-mock.cjs";
10
+ import { AGUIMock } from "./agui-mock.cjs";
9
11
 
10
12
  //#region src/suite.d.ts
11
13
  interface MockSuiteOptions {
@@ -13,12 +15,14 @@ interface MockSuiteOptions {
13
15
  mcp?: MCPMockOptions;
14
16
  a2a?: A2AMockOptions;
15
17
  vector?: VectorMockOptions;
18
+ agui?: AGUIMockOptions;
16
19
  }
17
20
  interface MockSuite {
18
21
  llm: LLMock;
19
22
  mcp?: MCPMock;
20
23
  a2a?: A2AMock;
21
24
  vector?: VectorMock;
25
+ agui?: AGUIMock;
22
26
  start(): Promise<void>;
23
27
  stop(): Promise<void>;
24
28
  reset(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"suite.d.cts","names":[],"sources":["../src/suite.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;UASiB,gBAAA;QACT;EADS,GAAA,CAAA,EAET,cAFyB;EAAA,GAAA,CAAA,EAGzB,cAHyB;QACzB,CAAA,EAGG,iBAHH;;AAEA,UAIS,SAAA,CAJT;KACG,EAIJ,MAJI;EAAiB,GAAA,CAAA,EAKpB,OALoB;EAGX,GAAA,CAAA,EAGT,OAHkB;EAAA,MAAA,CAAA,EAIf,UAJe;OACnB,EAAA,EAII,OAJJ,CAAA,IAAA,CAAA;MACC,EAAA,EAIE,OAJF,CAAA,IAAA,CAAA;OACA,EAAA,EAAA,IAAA;;AAEG,iBAKW,eAAA,CALX,OAAA,CAAA,EAKoC,gBALpC,CAAA,EAK4D,OAL5D,CAKoE,SALpE,CAAA"}
1
+ {"version":3,"file":"suite.d.cts","names":[],"sources":["../src/suite.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;UAWiB,gBAAA;QACT;EADS,GAAA,CAAA,EAET,cAFyB;EAAA,GAAA,CAAA,EAGzB,cAHyB;QACzB,CAAA,EAGG,iBAHH;MACA,CAAA,EAGC,eAHD;;AAEG,UAIM,SAAA,CAJN;KACF,EAIF,MAJE;EAAe,GAAA,CAAA,EAKhB,OALgB;EAGP,GAAA,CAAA,EAGT,OAHkB;EAAA,MAAA,CAAA,EAIf,UAJe;MACnB,CAAA,EAIE,QAJF;OACC,EAAA,EAIG,OAJH,CAAA,IAAA,CAAA;MACA,EAAA,EAIE,OAJF,CAAA,IAAA,CAAA;OACG,EAAA,EAAA,IAAA;;AAEA,iBAKW,eAAA,CALX,OAAA,CAAA,EAKoC,gBALpC,CAAA,EAK4D,OAL5D,CAKoE,SALpE,CAAA"}
package/dist/suite.d.ts CHANGED
@@ -3,9 +3,11 @@ import { A2AMockOptions } from "./a2a-types.js";
3
3
  import { A2AMock } from "./a2a-mock.js";
4
4
  import { LLMock } from "./llmock.js";
5
5
  import { MCPMockOptions } from "./mcp-types.js";
6
+ import { AGUIMockOptions } from "./agui-types.js";
6
7
  import { VectorMockOptions } from "./vector-types.js";
7
8
  import { MCPMock } from "./mcp-mock.js";
8
9
  import { VectorMock } from "./vector-mock.js";
10
+ import { AGUIMock } from "./agui-mock.js";
9
11
 
10
12
  //#region src/suite.d.ts
11
13
  interface MockSuiteOptions {
@@ -13,12 +15,14 @@ interface MockSuiteOptions {
13
15
  mcp?: MCPMockOptions;
14
16
  a2a?: A2AMockOptions;
15
17
  vector?: VectorMockOptions;
18
+ agui?: AGUIMockOptions;
16
19
  }
17
20
  interface MockSuite {
18
21
  llm: LLMock;
19
22
  mcp?: MCPMock;
20
23
  a2a?: A2AMock;
21
24
  vector?: VectorMock;
25
+ agui?: AGUIMock;
22
26
  start(): Promise<void>;
23
27
  stop(): Promise<void>;
24
28
  reset(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"suite.d.ts","names":[],"sources":["../src/suite.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;UASiB,gBAAA;QACT;EADS,GAAA,CAAA,EAET,cAFyB;EAAA,GAAA,CAAA,EAGzB,cAHyB;QACzB,CAAA,EAGG,iBAHH;;AAEA,UAIS,SAAA,CAJT;KACG,EAIJ,MAJI;EAAiB,GAAA,CAAA,EAKpB,OALoB;EAGX,GAAA,CAAA,EAGT,OAHkB;EAAA,MAAA,CAAA,EAIf,UAJe;OACnB,EAAA,EAII,OAJJ,CAAA,IAAA,CAAA;MACC,EAAA,EAIE,OAJF,CAAA,IAAA,CAAA;OACA,EAAA,EAAA,IAAA;;AAEG,iBAKW,eAAA,CALX,OAAA,CAAA,EAKoC,gBALpC,CAAA,EAK4D,OAL5D,CAKoE,SALpE,CAAA"}
1
+ {"version":3,"file":"suite.d.ts","names":[],"sources":["../src/suite.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;UAWiB,gBAAA;QACT;EADS,GAAA,CAAA,EAET,cAFyB;EAAA,GAAA,CAAA,EAGzB,cAHyB;QACzB,CAAA,EAGG,iBAHH;MACA,CAAA,EAGC,eAHD;;AAEG,UAIM,SAAA,CAJN;KACF,EAIF,MAJE;EAAe,GAAA,CAAA,EAKhB,OALgB;EAGP,GAAA,CAAA,EAGT,OAHkB;EAAA,MAAA,CAAA,EAIf,UAJe;MACnB,CAAA,EAIE,QAJF;OACC,EAAA,EAIG,OAJH,CAAA,IAAA,CAAA;MACA,EAAA,EAIE,OAJF,CAAA,IAAA,CAAA;OACG,EAAA,EAAA,IAAA;;AAEA,iBAKW,eAAA,CALX,OAAA,CAAA,EAKoC,gBALpC,CAAA,EAK4D,OAL5D,CAKoE,SALpE,CAAA"}
package/dist/suite.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { A2AMock } from "./a2a-mock.js";
2
2
  import { LLMock } from "./llmock.js";
3
3
  import { MCPMock } from "./mcp-mock.js";
4
+ import { AGUIMock } from "./agui-mock.js";
4
5
  import { VectorMock } from "./vector-mock.js";
5
6
 
6
7
  //#region src/suite.ts
@@ -9,6 +10,7 @@ async function createMockSuite(options = {}) {
9
10
  let mcp;
10
11
  let a2a;
11
12
  let vector;
13
+ let agui;
12
14
  if (options.mcp) {
13
15
  mcp = new MCPMock(options.mcp);
14
16
  llm.mount("/mcp", mcp);
@@ -21,11 +23,16 @@ async function createMockSuite(options = {}) {
21
23
  vector = new VectorMock(options.vector);
22
24
  llm.mount("/vector", vector);
23
25
  }
26
+ if (options.agui) {
27
+ agui = new AGUIMock(options.agui);
28
+ llm.mount("/agui", agui);
29
+ }
24
30
  return {
25
31
  llm,
26
32
  mcp,
27
33
  a2a,
28
34
  vector,
35
+ agui,
29
36
  async start() {
30
37
  await llm.start();
31
38
  },
@@ -37,6 +44,7 @@ async function createMockSuite(options = {}) {
37
44
  if (mcp) mcp.reset();
38
45
  if (a2a) a2a.reset();
39
46
  if (vector) vector.reset();
47
+ if (agui) agui.reset();
40
48
  }
41
49
  };
42
50
  }
package/dist/suite.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"suite.js","names":[],"sources":["../src/suite.ts"],"sourcesContent":["import { LLMock } from \"./llmock.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport type { MockServerOptions } from \"./types.js\";\nimport type { MCPMockOptions } from \"./mcp-types.js\";\nimport type { A2AMockOptions } from \"./a2a-types.js\";\nimport type { VectorMockOptions } from \"./vector-types.js\";\n\nexport interface MockSuiteOptions {\n llm?: MockServerOptions;\n mcp?: MCPMockOptions;\n a2a?: A2AMockOptions;\n vector?: VectorMockOptions;\n}\n\nexport interface MockSuite {\n llm: LLMock;\n mcp?: MCPMock;\n a2a?: A2AMock;\n vector?: VectorMock;\n start(): Promise<void>;\n stop(): Promise<void>;\n reset(): void;\n}\n\nexport async function createMockSuite(options: MockSuiteOptions = {}): Promise<MockSuite> {\n const llm = new LLMock(options.llm);\n let mcp: MCPMock | undefined;\n let a2a: A2AMock | undefined;\n let vector: VectorMock | undefined;\n\n if (options.mcp) {\n mcp = new MCPMock(options.mcp);\n llm.mount(\"/mcp\", mcp);\n }\n\n if (options.a2a) {\n a2a = new A2AMock(options.a2a);\n llm.mount(\"/a2a\", a2a);\n }\n\n if (options.vector) {\n vector = new VectorMock(options.vector);\n llm.mount(\"/vector\", vector);\n }\n\n return {\n llm,\n mcp,\n a2a,\n vector,\n async start() {\n await llm.start();\n },\n async stop() {\n await llm.stop();\n },\n reset() {\n llm.reset();\n if (mcp) mcp.reset();\n if (a2a) a2a.reset();\n if (vector) vector.reset();\n },\n };\n}\n"],"mappings":";;;;;;AA0BA,eAAsB,gBAAgB,UAA4B,EAAE,EAAsB;CACxF,MAAM,MAAM,IAAI,OAAO,QAAQ,IAAI;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,KAAI,QAAQ,KAAK;AACf,QAAM,IAAI,QAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,KAAK;AACf,QAAM,IAAI,QAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,QAAQ;AAClB,WAAS,IAAI,WAAW,QAAQ,OAAO;AACvC,MAAI,MAAM,WAAW,OAAO;;AAG9B,QAAO;EACL;EACA;EACA;EACA;EACA,MAAM,QAAQ;AACZ,SAAM,IAAI,OAAO;;EAEnB,MAAM,OAAO;AACX,SAAM,IAAI,MAAM;;EAElB,QAAQ;AACN,OAAI,OAAO;AACX,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,OAAQ,QAAO,OAAO;;EAE7B"}
1
+ {"version":3,"file":"suite.js","names":[],"sources":["../src/suite.ts"],"sourcesContent":["import { LLMock } from \"./llmock.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport type { MockServerOptions } from \"./types.js\";\nimport type { MCPMockOptions } from \"./mcp-types.js\";\nimport type { A2AMockOptions } from \"./a2a-types.js\";\nimport type { VectorMockOptions } from \"./vector-types.js\";\nimport type { AGUIMockOptions } from \"./agui-types.js\";\n\nexport interface MockSuiteOptions {\n llm?: MockServerOptions;\n mcp?: MCPMockOptions;\n a2a?: A2AMockOptions;\n vector?: VectorMockOptions;\n agui?: AGUIMockOptions;\n}\n\nexport interface MockSuite {\n llm: LLMock;\n mcp?: MCPMock;\n a2a?: A2AMock;\n vector?: VectorMock;\n agui?: AGUIMock;\n start(): Promise<void>;\n stop(): Promise<void>;\n reset(): void;\n}\n\nexport async function createMockSuite(options: MockSuiteOptions = {}): Promise<MockSuite> {\n const llm = new LLMock(options.llm);\n let mcp: MCPMock | undefined;\n let a2a: A2AMock | undefined;\n let vector: VectorMock | undefined;\n let agui: AGUIMock | undefined;\n\n if (options.mcp) {\n mcp = new MCPMock(options.mcp);\n llm.mount(\"/mcp\", mcp);\n }\n\n if (options.a2a) {\n a2a = new A2AMock(options.a2a);\n llm.mount(\"/a2a\", a2a);\n }\n\n if (options.vector) {\n vector = new VectorMock(options.vector);\n llm.mount(\"/vector\", vector);\n }\n\n if (options.agui) {\n agui = new AGUIMock(options.agui);\n llm.mount(\"/agui\", agui);\n }\n\n return {\n llm,\n mcp,\n a2a,\n vector,\n agui,\n async start() {\n await llm.start();\n },\n async stop() {\n await llm.stop();\n },\n reset() {\n llm.reset();\n if (mcp) mcp.reset();\n if (a2a) a2a.reset();\n if (vector) vector.reset();\n if (agui) agui.reset();\n },\n };\n}\n"],"mappings":";;;;;;;AA8BA,eAAsB,gBAAgB,UAA4B,EAAE,EAAsB;CACxF,MAAM,MAAM,IAAI,OAAO,QAAQ,IAAI;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,KAAI,QAAQ,KAAK;AACf,QAAM,IAAI,QAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,KAAK;AACf,QAAM,IAAI,QAAQ,QAAQ,IAAI;AAC9B,MAAI,MAAM,QAAQ,IAAI;;AAGxB,KAAI,QAAQ,QAAQ;AAClB,WAAS,IAAI,WAAW,QAAQ,OAAO;AACvC,MAAI,MAAM,WAAW,OAAO;;AAG9B,KAAI,QAAQ,MAAM;AAChB,SAAO,IAAI,SAAS,QAAQ,KAAK;AACjC,MAAI,MAAM,SAAS,KAAK;;AAG1B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,MAAM,QAAQ;AACZ,SAAM,IAAI,OAAO;;EAEnB,MAAM,OAAO;AACX,SAAM,IAAI,MAAM;;EAElB,QAAQ;AACN,OAAI,OAAO;AACX,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,IAAK,KAAI,OAAO;AACpB,OAAI,OAAQ,QAAO,OAAO;AAC1B,OAAI,KAAM,MAAK,OAAO;;EAEzB"}
package/dist/types.d.cts CHANGED
@@ -222,6 +222,8 @@ type RecordProviderKey = "openai" | "anthropic" | "gemini" | "vertexai" | "bedro
222
222
  interface RecordConfig {
223
223
  providers: Partial<Record<RecordProviderKey, string>>;
224
224
  fixturePath?: string;
225
+ /** Proxy unmatched requests without saving fixtures or caching in memory. */
226
+ proxyOnly?: boolean;
225
227
  }
226
228
  interface MockServerOptions {
227
229
  port?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;;UAQiB,SAAA;qBAER,IAAA,CAAK,sBACL,IAAA,CAAK,mCAET;EALY,aAAS,EAAA,MAAA,EAMD,GAAA,CAAI,MANH,EAAA,IAAA,EAMiB,MANjB,EAAA,QAAA,EAAA,MAAA,CAAA,EAM4C,OAN5C,CAAA,OAAA,CAAA;EAAA,MAAA,GAAA,EAAA;IAEjB,MAAK,EAAA,MAAA;IACL,CAAA,GAAK,EAAA,MAAA,CAAA,EAAA,OAAA;;YAGe,EAAA,OAAA,EAEN,OAFM,CAAA,EAAA,IAAA;YAAc,EAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAA2B,EAAA,QAAA,EAI7C,eAJ6C,CAAA,EAAA,IAAA;;AAI7C,UAGR,WAAA,CAHQ;EAAe,IAAA,EAAA,MAAA;EAGvB,IAAA,CAAA,EAAA,MAAA;EAMA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAW,OAAA;;AAER,UAFH,WAAA,CAEG;MAEL,EAAA,QAAA,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;EAAe,OAAA,EAAA,MAAA,GAFV,WAEU,EAAA,GAAA,IAAA;EAIb,IAAA,CAAA,EAAA,MAAA;EAMA,UAAA,CAAA,EAVF,eAUuB,EAAA;EAAA,YAAA,CAAA,EAAA,MAAA;;AAM5B,UAZO,eAAA,CAYP;EAAc,EAAA,EAAA,MAAA;EAQP,IAAA,EAAA,UAAA;EAOA,QAAA,EAAA;IAAY,IAAA,EAAA,MAAA;IACJ,SAAA,EAAA,MAAA;;;AAML,UA5BH,qBAAA,CA4BG;EAAqB,KAAA,EAAA,MAAA;EAOxB,QAAA,EAjCL,WAiCiB,EAAA;EAQZ,MAAA,CAAA,EAAA,OAAQ;EAMR,WAAA,CAAA,EAAA,MAAgB;EAKhB,UAAA,CAAA,EAAA,MAAA;EASA,KAAA,CAAA,EAzDP,cAyDoB,EAAA;EAKb,WAAA,CAAA,EAAA,MAAiB,GAAA,MAAA;EAItB,eAAA,CAAA,EAAe;IAAA,IAAA,EAAA,MAAA;IACvB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;gBAGA,CAAA,EAAA,MAAA;MACA,EAAA,MAAA,CAAA,EAAA,OAAA;;AAIa,UAnEA,cAAA,CAmEgB;EAMhB,IAAA,EAAA,UAAW;EAMhB,QAAA,EAAA;IAIK,IAAA,EAAO,MAAA;IAAA,WAAA,CAAA,EAAA,MAAA;IACf,UAAA,CAAA,EAAA,MAAA;;;AAOC,UApFO,YAAA,CAoFP;EAAW,WAAA,CAAA,EAAA,MAAA,GAnFI,MAmFJ;EAGT,SAAA,CAAA,EAAA,MAAW,GArFA,MAqFA;EAAA,UAAA,CAAA,EAAA,MAAA;UAAQ,CAAA,EAAA,MAAA;OAAL,CAAA,EAAA,MAAA,GAlFP,MAkFO;EAAI,cAAA,CAAA,EAAA,MAAA;EAClB,SAAA,CAAA,EAAA,CAAA,GAAA,EAjFQ,qBAiFY,EAAA,GAAA,OAAA;EAAA;eAAQ,CAAA,EAAA,MAAA;;AAAD,UA1EtB,YAAA,CA0EsB;EAItB,OAAA,EAAA,MAAW;EAIX,SAAA,CAAA,EAAA,MAAA;EAAgB,WAAA,CAAA,EAAA,MAAA,EAAA;MAWrB,CAAA,EAAA,MAAA;cAKS,CAAA,EAAA,MAAA;;AACA,UA3FJ,QAAA,CA2FI;EAKJ,IAAA,EAAA,MAAA;EAAY,SAAA,EAAA,MAAA;KAKlB,EAAA,MAAA;;AAKE,UApGI,gBAAA,CAoGJ;WAGK,EAtGL,QAsGK,EAAA;EAAW,YAAA,CAAA,EAAA,MAAA;AAM7B;AAQiB,UAhHA,4BAAA,CAkHA;EAIA,OAAA,EAAA,MAAQ;EAOR,SAAA,EA3HJ,QA2HoB,EAAA;EAShB,SAAA,CAAA,EAAA,MAAc;EASd,WAAA,CAAA,EAAA,MAAA,EAAA;EAMA,IAAA,CAAA,EAAA,MAAA;EAUL,YAAA,CAAA,EAAA,MAAiB;AAU7B;AAA6B,UAhKZ,aAAA,CAgKY;OACD,EAAA;IAAP,OAAA,EAAA,MAAA;IAAR,IAAA,CAAA,EAAA,MAAA;IAAO,IAAA,CAAA,EAAA,MAAA;EAMH,CAAA;EAAiB,MAAA,CAAA,EAAA,MAAA;;AAavB,UA/KM,iBAAA,CA+KN;WAUgB,EAAA,MAAA,EAAA;;AAA+C,KArL9D,eAAA,GACR,YAoLsE,GAnLtE,gBAmLsE,GAlLtE,4BAkLsE,GAjLtE,aAiLsE,GAhLtE,iBAgLsE;AAOzD,UAnLA,gBAAA,CAmLe;EAAA,IAAA,CAAA,EAAA,MAAA;KAGtB,CAAA,EAAA,MAAA;QACA,CAAA,EAAA,MAAA;;AAEC,UAnLM,WAAA,CAmLN;UAEgB,CAAA,EAAA,MAAA;eAA0B,CAAA,EAAA,MAAA;EAAqB,cAAA,CAAA,EAAA,MAAA;;KA/K9D,WAAA;UAIK,OAAA;SACR;YACG;;;;;qBAKS;UACX;;KAGE,WAAA,GAAc,KAAK;KACnB,oBAAA,GAAuB,KAAK;UAIvB,WAAA;YACL;;UAGK,gBAAA;;;;;;;;;;YAWL;;;;;qBAKS;UACX;;UAKO,YAAA;;;;;WAKN;QACH;;;;aAIK;;;kBAGK;;;UAMD,QAAA;;;;;WAKN;;UAGM,SAAA;;SAER;;;UAIQ,QAAA;;;;eAIF;;UAGE,gBAAA;;;;;;;;;UASA,cAAA;;;;;WAKN;;;;;;;UAIM,oBAAA;;WAEN;;;UAIM,qBAAA;;;;;eAKF;;KAKH,iBAAA;UAUK,YAAA;aACJ,QAAQ,OAAO;;;;;UAMX,iBAAA;;;;;;;UAOP;;;;;;WAMC;;;;;;;;;;2BAUgB,0BAA0B;;UAOpC,eAAA;;;UAGP;UACA;aACG;WACF;;2BAEgB,0BAA0B"}
package/dist/types.d.ts CHANGED
@@ -222,6 +222,8 @@ type RecordProviderKey = "openai" | "anthropic" | "gemini" | "vertexai" | "bedro
222
222
  interface RecordConfig {
223
223
  providers: Partial<Record<RecordProviderKey, string>>;
224
224
  fixturePath?: string;
225
+ /** Proxy unmatched requests without saving fixtures or caching in memory. */
226
+ proxyOnly?: boolean;
225
227
  }
226
228
  interface MockServerOptions {
227
229
  port?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}