@copilotkit/aimock 1.18.0 → 1.19.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 (157) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +13 -0
  4. package/dist/agui-types.d.ts.map +1 -1
  5. package/dist/bedrock-converse.cjs +2 -2
  6. package/dist/bedrock-converse.cjs.map +1 -1
  7. package/dist/bedrock-converse.d.cts.map +1 -1
  8. package/dist/bedrock-converse.d.ts.map +1 -1
  9. package/dist/bedrock-converse.js +3 -3
  10. package/dist/bedrock-converse.js.map +1 -1
  11. package/dist/bedrock.cjs +2 -2
  12. package/dist/bedrock.cjs.map +1 -1
  13. package/dist/bedrock.d.cts.map +1 -1
  14. package/dist/bedrock.d.ts.map +1 -1
  15. package/dist/bedrock.js +3 -3
  16. package/dist/bedrock.js.map +1 -1
  17. package/dist/cohere.cjs +1 -1
  18. package/dist/cohere.cjs.map +1 -1
  19. package/dist/cohere.d.cts.map +1 -1
  20. package/dist/cohere.d.ts.map +1 -1
  21. package/dist/cohere.js +2 -2
  22. package/dist/cohere.js.map +1 -1
  23. package/dist/config-loader.d.cts.map +1 -1
  24. package/dist/elevenlabs-audio.cjs +1 -1
  25. package/dist/elevenlabs-audio.cjs.map +1 -1
  26. package/dist/elevenlabs-audio.d.cts.map +1 -1
  27. package/dist/elevenlabs-audio.d.ts.map +1 -1
  28. package/dist/elevenlabs-audio.js +2 -2
  29. package/dist/elevenlabs-audio.js.map +1 -1
  30. package/dist/embeddings.cjs +1 -1
  31. package/dist/embeddings.cjs.map +1 -1
  32. package/dist/embeddings.d.cts.map +1 -1
  33. package/dist/embeddings.d.ts.map +1 -1
  34. package/dist/embeddings.js +2 -2
  35. package/dist/embeddings.js.map +1 -1
  36. package/dist/fal-audio.cjs +2 -2
  37. package/dist/fal-audio.cjs.map +1 -1
  38. package/dist/fal-audio.d.cts.map +1 -1
  39. package/dist/fal-audio.d.ts.map +1 -1
  40. package/dist/fal-audio.js +3 -3
  41. package/dist/fal-audio.js.map +1 -1
  42. package/dist/fal.cjs +1 -1
  43. package/dist/fal.cjs.map +1 -1
  44. package/dist/fal.d.cts.map +1 -1
  45. package/dist/fal.d.ts.map +1 -1
  46. package/dist/fal.js +2 -2
  47. package/dist/fal.js.map +1 -1
  48. package/dist/fixture-loader.cjs +128 -126
  49. package/dist/fixture-loader.cjs.map +1 -1
  50. package/dist/fixture-loader.js +128 -126
  51. package/dist/fixture-loader.js.map +1 -1
  52. package/dist/gemini-interactions.cjs +1 -1
  53. package/dist/gemini-interactions.cjs.map +1 -1
  54. package/dist/gemini-interactions.d.cts.map +1 -1
  55. package/dist/gemini-interactions.d.ts.map +1 -1
  56. package/dist/gemini-interactions.js +2 -2
  57. package/dist/gemini-interactions.js.map +1 -1
  58. package/dist/gemini.cjs +1 -1
  59. package/dist/gemini.cjs.map +1 -1
  60. package/dist/gemini.d.cts.map +1 -1
  61. package/dist/gemini.d.ts.map +1 -1
  62. package/dist/gemini.js +2 -2
  63. package/dist/gemini.js.map +1 -1
  64. package/dist/helpers.cjs +25 -0
  65. package/dist/helpers.cjs.map +1 -1
  66. package/dist/helpers.d.cts.map +1 -1
  67. package/dist/helpers.d.ts.map +1 -1
  68. package/dist/helpers.js +24 -1
  69. package/dist/helpers.js.map +1 -1
  70. package/dist/images.cjs +1 -1
  71. package/dist/images.cjs.map +1 -1
  72. package/dist/images.d.cts.map +1 -1
  73. package/dist/images.d.ts.map +1 -1
  74. package/dist/images.js +2 -2
  75. package/dist/images.js.map +1 -1
  76. package/dist/llmock.cjs +1 -1
  77. package/dist/llmock.cjs.map +1 -1
  78. package/dist/llmock.d.cts +8 -7
  79. package/dist/llmock.d.cts.map +1 -1
  80. package/dist/llmock.d.ts +8 -7
  81. package/dist/llmock.d.ts.map +1 -1
  82. package/dist/llmock.js +1 -1
  83. package/dist/llmock.js.map +1 -1
  84. package/dist/messages.cjs +1 -1
  85. package/dist/messages.cjs.map +1 -1
  86. package/dist/messages.d.cts.map +1 -1
  87. package/dist/messages.d.ts.map +1 -1
  88. package/dist/messages.js +2 -2
  89. package/dist/messages.js.map +1 -1
  90. package/dist/ollama.cjs +2 -2
  91. package/dist/ollama.cjs.map +1 -1
  92. package/dist/ollama.d.cts.map +1 -1
  93. package/dist/ollama.d.ts.map +1 -1
  94. package/dist/ollama.js +3 -3
  95. package/dist/ollama.js.map +1 -1
  96. package/dist/recorder.cjs +27 -4
  97. package/dist/recorder.cjs.map +1 -1
  98. package/dist/recorder.d.cts.map +1 -1
  99. package/dist/recorder.d.ts.map +1 -1
  100. package/dist/recorder.js +27 -4
  101. package/dist/recorder.js.map +1 -1
  102. package/dist/responses.cjs +1 -1
  103. package/dist/responses.cjs.map +1 -1
  104. package/dist/responses.d.cts.map +1 -1
  105. package/dist/responses.d.ts.map +1 -1
  106. package/dist/responses.js +2 -2
  107. package/dist/responses.js.map +1 -1
  108. package/dist/router.cjs +3 -1
  109. package/dist/router.cjs.map +1 -1
  110. package/dist/router.js +3 -1
  111. package/dist/router.js.map +1 -1
  112. package/dist/server.cjs +2 -2
  113. package/dist/server.cjs.map +1 -1
  114. package/dist/server.d.cts.map +1 -1
  115. package/dist/server.d.ts.map +1 -1
  116. package/dist/server.js +3 -3
  117. package/dist/server.js.map +1 -1
  118. package/dist/speech.cjs +1 -1
  119. package/dist/speech.cjs.map +1 -1
  120. package/dist/speech.d.cts.map +1 -1
  121. package/dist/speech.d.ts.map +1 -1
  122. package/dist/speech.js +2 -2
  123. package/dist/speech.js.map +1 -1
  124. package/dist/transcription.cjs +1 -1
  125. package/dist/transcription.cjs.map +1 -1
  126. package/dist/transcription.d.cts.map +1 -1
  127. package/dist/transcription.d.ts.map +1 -1
  128. package/dist/transcription.js +2 -2
  129. package/dist/transcription.js.map +1 -1
  130. package/dist/types.d.cts +3 -2
  131. package/dist/types.d.cts.map +1 -1
  132. package/dist/types.d.ts +3 -2
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/vector-types.d.ts.map +1 -1
  135. package/dist/video.cjs +1 -1
  136. package/dist/video.cjs.map +1 -1
  137. package/dist/video.d.cts.map +1 -1
  138. package/dist/video.d.ts.map +1 -1
  139. package/dist/video.js +2 -2
  140. package/dist/video.js.map +1 -1
  141. package/dist/ws-gemini-live.cjs +1 -1
  142. package/dist/ws-gemini-live.cjs.map +1 -1
  143. package/dist/ws-gemini-live.d.cts.map +1 -1
  144. package/dist/ws-gemini-live.d.ts.map +1 -1
  145. package/dist/ws-gemini-live.js +2 -2
  146. package/dist/ws-gemini-live.js.map +1 -1
  147. package/dist/ws-realtime.cjs +1 -1
  148. package/dist/ws-realtime.cjs.map +1 -1
  149. package/dist/ws-realtime.d.cts.map +1 -1
  150. package/dist/ws-realtime.d.ts.map +1 -1
  151. package/dist/ws-realtime.js +2 -2
  152. package/dist/ws-realtime.js.map +1 -1
  153. package/dist/ws-responses.cjs +1 -1
  154. package/dist/ws-responses.cjs.map +1 -1
  155. package/dist/ws-responses.js +2 -2
  156. package/dist/ws-responses.js.map +1 -1
  157. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.js","names":["crypto","http"],"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 * Captured upstream response, exposed to the `beforeWriteResponse` hook so\n * callers can decide whether to relay it or mutate it (e.g. chaos injection).\n */\nexport interface ProxyCapturedResponse {\n status: number;\n contentType: string;\n body: Buffer;\n}\n\nexport interface ProxyOptions {\n /**\n * Called after the upstream response has been captured and recorded, but\n * before the relay to the client. Contract when the hook returns `true`:\n * 1. It wrote its own response body on `res`.\n * 2. It journaled the outcome (proxyAndRecord will NOT journal it).\n * 3. proxyAndRecord skips its default relay and returns `\"handled_by_hook\"`.\n *\n * Returning `false` (or omitting the hook) lets proxyAndRecord relay the\n * upstream response normally and leaves journaling to the caller via the\n * `\"relayed\"` outcome. Rejected promises propagate and leave the response\n * unwritten.\n *\n * NOT invoked when the upstream response was streamed progressively to the\n * client (SSE) — the bytes are already on the wire and can't be mutated.\n * Callers that need to observe the bypass should pass `onHookBypassed`.\n */\n beforeWriteResponse?: (response: ProxyCapturedResponse) => boolean | Promise<boolean>;\n /**\n * Called when `beforeWriteResponse` was provided but could not be invoked\n * because the upstream response was streamed to the client progressively.\n * The hook was rolled + wired but the bytes left before it could fire.\n * Intended for observability (log/metric/journal annotation) — proxyAndRecord\n * still returns `\"relayed\"`.\n */\n onHookBypassed?: (reason: \"sse_streamed\") => void;\n}\n\n/**\n * Outcome of a proxyAndRecord call, returned so the caller can decide whether\n * to journal, fall through, or stop — without sharing a mutable flag with the\n * `beforeWriteResponse` hook.\n *\n * - `\"not_configured\"` — no upstream URL for this provider; caller should fall\n * through to its next branch (typically strict/404).\n * - `\"relayed\"` — the default code path wrote a response (upstream success or\n * synthesized 502 error). Caller should journal the outcome.\n * - `\"handled_by_hook\"` — the hook wrote + journaled its own response. Caller\n * should not double-journal.\n */\nexport type ProxyOutcome = \"not_configured\" | \"relayed\" | \"handled_by_hook\";\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 */\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 options?: ProxyOptions,\n): Promise<ProxyOutcome> {\n const record = defaults.record;\n if (!record) return \"not_configured\";\n\n const providers = record.providers;\n // gemini-interactions shares the same upstream config as gemini\n const lookupKey = providerKey === \"gemini-interactions\" ? \"gemini\" : providerKey;\n const upstreamUrl = providers[lookupKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return \"not_configured\";\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 \"relayed\";\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 // Track whether we streamed SSE progressively to the client; if so,\n // skip the final res.writeHead/res.end relay at the bottom of this fn.\n let streamedToClient = false;\n let clientDisconnected = false;\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody, res);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n streamedToClient = result.streamedToClient;\n clientDisconnected = result.clientDisconnected;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n if (!res.headersSent) {\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 } else {\n // SSE headers already sent — gracefully close the connection\n res.end();\n }\n return \"relayed\";\n }\n\n // Detect streaming response and collapse if necessary.\n // NOTE: collapse buffers the entire upstream body in memory. Fine for\n // current chat-completions traffic (responses are small), but revisit if\n // this path ever proxies long-lived or large streams — both the buffer\n // here and the hook below receive the full payload.\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 // TTS response — binary audio, not JSON\n const isAudioResponse = ctString.toLowerCase().startsWith(\"audio/\");\n if (isAudioResponse && rawBuffer.length > 0) {\n // Derive format from Content-Type (audio/mpeg→mp3, audio/opus→opus, etc.)\n const audioFormat = ctString\n .toLowerCase()\n .replace(\"audio/\", \"\")\n .replace(\"mpeg\", \"mp3\")\n .split(\";\")[0]\n .trim();\n fixtureResponse = {\n audio: rawBuffer.toString(\"base64\"),\n ...(audioFormat && audioFormat !== \"mp3\" ? { format: audioFormat } : {}),\n };\n } else 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 // Audio from streamed inlineData (e.g. Gemini SSE with audio parts)\n if (collapsed.audioB64) {\n fixtureResponse = {\n audio: {\n b64Json: collapsed.audioB64,\n contentType: collapsed.audioMimeType ?? \"audio/mpeg\",\n },\n };\n } else if (\n collapsed.content === \"\" &&\n (!collapsed.toolCalls || collapsed.toolCalls.length === 0)\n ) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n const reasoningSpread = collapsed.reasoning ? { reasoning: collapsed.reasoning } : {};\n fixtureResponse = { content: collapsed.content ?? \"\", ...reasoningSpread };\n } else {\n const reasoningSpread = collapsed.reasoning ? { reasoning: collapsed.reasoning } : {};\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n // Both content and toolCalls present — save as ContentWithToolCallsResponse\n fixtureResponse = {\n content: collapsed.content,\n toolCalls: collapsed.toolCalls,\n ...reasoningSpread,\n };\n } else {\n fixtureResponse = { toolCalls: collapsed.toolCalls, ...reasoningSpread };\n }\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\", ...reasoningSpread };\n }\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 // fal.ai returns arbitrary, model-specific JSON shapes (images, video URLs,\n // audio file objects, etc.). Round-trip the payload verbatim instead of\n // letting buildFixtureResponse mis-classify it as ImageResponse / VideoResponse.\n if (request._endpointType === \"fal\" && parsedResponse !== null) {\n const obj = parsedResponse as Record<string, unknown>;\n const isErrorShape =\n typeof obj.error === \"object\" &&\n obj.error !== null &&\n typeof (obj.error as Record<string, unknown>).message === \"string\";\n if (isErrorShape) {\n const err = obj.error as Record<string, unknown>;\n fixtureResponse = {\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: upstreamStatus,\n };\n } else {\n fixtureResponse = { json: parsedResponse, status: upstreamStatus };\n }\n } else {\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch (err) {\n defaults.logger.debug(\n `Could not parse encoding_format from raw body: ${err instanceof Error ? err.message : \"unknown error\"}`,\n );\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n }\n\n // If the client disconnected mid-stream, the collected data is likely\n // truncated. Saving a partial fixture is worse than saving none — skip\n // fixture persistence entirely.\n if (clientDisconnected) {\n defaults.logger.warn(\n \"Client disconnected mid-stream — skipping fixture save to avoid truncated data\",\n );\n return \"relayed\";\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 = {\n match: fixtureMatch,\n response: fixtureResponse,\n };\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 // NOTE: the persisted fixture is always the real upstream response, even when chaos\n // later mutates the relay (e.g. malformed via beforeWriteResponse). Chaos is a live-traffic\n // decoration; the recorded artifact must stay truthful so replay sees what upstream said.\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 if (!res.headersSent) {\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n } else {\n defaults.logger.warn(`Cannot set X-LLMock-Record-Error header — headers already sent`);\n }\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 (skip when SSE was already streamed\n // progressively by makeUpstreamRequest — headers and body are already on\n // the wire).\n if (streamedToClient) {\n // SSE: the hook can't run because the body is already on the wire. Surface\n // the bypass so the caller (typically the chaos layer) can record it —\n // otherwise a configured chaos action silently no-ops on SSE traffic.\n if (options?.beforeWriteResponse && options.onHookBypassed) {\n options.onHookBypassed(\"sse_streamed\");\n }\n } else {\n // Give the caller a chance to mutate or replace the response before relay.\n // Used by the chaos layer to turn a successful proxy into a malformed body.\n // `body` is the raw upstream bytes so binary payloads survive round-tripping.\n if (options?.beforeWriteResponse) {\n const handled = await options.beforeWriteResponse({\n status: upstreamStatus,\n contentType: ctString,\n body: rawBuffer,\n });\n if (handled) return \"handled_by_hook\";\n }\n\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n const isAudioRelay = ctString.toLowerCase().startsWith(\"audio/\");\n res.end(isBinaryStream || isAudioRelay ? rawBuffer : upstreamBody);\n }\n\n return \"relayed\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes?: http.ServerResponse,\n): Promise<{\n status: number;\n headers: http.IncomingHttpHeaders;\n body: string;\n rawBuffer: Buffer;\n streamedToClient: boolean;\n clientDisconnected: boolean;\n}> {\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 // Detect Server-Sent Events so we can tee upstream chunks to the\n // client as they arrive rather than buffering the entire stream and\n // replaying it in a single res.end() at the bottom of proxyAndRecord.\n // Buffering collapses every SSE frame into one client-visible write,\n // which defeats progressive rendering in downstream consumers.\n const ct = res.headers[\"content-type\"];\n const ctStr = Array.isArray(ct) ? ct.join(\", \") : (ct ?? \"\");\n const isSSE = ctStr.toLowerCase().includes(\"text/event-stream\");\n let streamedToClient = false;\n let clientDisconnected = false;\n if (isSSE && clientRes && !clientRes.headersSent) {\n const relayHeaders: Record<string, string> = {};\n if (ctStr) relayHeaders[\"Content-Type\"] = ctStr;\n clientRes.writeHead(res.statusCode ?? 200, relayHeaders);\n // Flush headers immediately so the client starts parsing frames\n // before the first data chunk arrives.\n if (typeof clientRes.flushHeaders === \"function\") clientRes.flushHeaders();\n streamedToClient = true;\n // Stop relaying if the client disconnects mid-stream\n clientRes.on(\"close\", () => {\n clientDisconnected = true;\n req.destroy();\n });\n }\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n if (\n streamedToClient &&\n clientRes &&\n !clientDisconnected &&\n !clientRes.destroyed &&\n !clientRes.writableEnded\n ) {\n clientRes.write(chunk);\n }\n });\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n if (\n streamedToClient &&\n clientRes &&\n !clientDisconnected &&\n !clientRes.destroyed &&\n !clientRes.writableEnded\n ) {\n clientRes.end();\n }\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n streamedToClient,\n clientDisconnected,\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 aimock 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 — only match the actual { error: { message: \"...\" } } shape\n // used by OpenAI/Anthropic/etc., not arbitrary truthy `.error` fields.\n if (\n typeof obj.error === \"object\" &&\n obj.error !== null &&\n typeof (obj.error as Record<string, unknown>).message === \"string\"\n ) {\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 const buf = Buffer.from(first.embedding, \"base64\");\n if (buf.byteLength % 4 !== 0) {\n // Malformed embedding — return a zero-dimension embedding fixture\n return { embedding: [] };\n }\n const aligned = new Uint8Array(buf).buffer; // Always offset 0\n const floats = new Float32Array(aligned, 0, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n }\n // OpenAI image generation: { created, data: [{ url, b64_json, revised_prompt }] }\n if (first.url || first.b64_json) {\n const images = (obj.data as Array<Record<string, unknown>>).map((item) => ({\n ...(item.url ? { url: String(item.url) } : {}),\n ...(item.b64_json ? { b64Json: String(item.b64_json) } : {}),\n ...(item.revised_prompt ? { revisedPrompt: String(item.revised_prompt) } : {}),\n }));\n if (images.length === 1) {\n return { image: images[0] };\n }\n return { images };\n }\n }\n\n // Gemini Imagen: { predictions: [...] }\n if (Array.isArray(obj.predictions)) {\n const images = (obj.predictions as Array<Record<string, unknown>>).map((p) => ({\n ...(p.bytesBase64Encoded ? { b64Json: String(p.bytesBase64Encoded) } : {}),\n ...(p.mimeType ? { mimeType: String(p.mimeType) } : {}),\n }));\n if (images.length === 1) {\n return { image: images[0] };\n }\n return { images };\n }\n\n // OpenAI transcription: { text: \"...\", ... }\n if (\n typeof obj.text === \"string\" &&\n (obj.task === \"transcribe\" || obj.language !== undefined || obj.duration !== undefined)\n ) {\n return {\n transcription: {\n text: obj.text as string,\n ...(obj.language ? { language: String(obj.language) } : {}),\n ...(obj.duration !== undefined ? { duration: Number(obj.duration) } : {}),\n ...(Array.isArray(obj.words) ? { words: obj.words } : {}),\n ...(Array.isArray(obj.segments) ? { segments: obj.segments } : {}),\n },\n };\n }\n\n // Gemini Interactions: { id, status, outputs: [{ type: \"text\", text }, { type: \"function_call\", name, arguments }] }\n if (Array.isArray(obj.outputs) && obj.outputs.length > 0) {\n const outputs = obj.outputs as Array<Record<string, unknown>>;\n const fnCallOutputs = outputs.filter((o) => o.type === \"function_call\");\n const textOutputs = outputs.filter((o) => o.type === \"text\" && typeof o.text === \"string\");\n const hasToolCalls = fnCallOutputs.length > 0;\n const joinedText = textOutputs.map((o) => String(o.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n\n if (hasToolCalls) {\n const toolCalls: ToolCall[] = fnCallOutputs.map((o) => ({\n name: String(o.name),\n arguments: typeof o.arguments === \"string\" ? o.arguments : JSON.stringify(o.arguments),\n ...(o.id ? { id: String(o.id) } : {}),\n }));\n if (hasContent) {\n return { content: joinedText, toolCalls };\n }\n return { toolCalls };\n }\n if (hasContent) {\n return { content: joinedText };\n }\n // Recognized Gemini Interactions shape but empty content\n return { content: \"\" };\n }\n\n // OpenAI video generation: { id, status, ... }\n // Guard against false positives: many API responses have `id` + `status` fields\n // (e.g. chat completions, Anthropic messages). Reject if the response has fields\n // that indicate a known non-video format.\n if (\n typeof obj.id === \"string\" &&\n typeof obj.status === \"string\" &&\n (obj.status === \"completed\" || obj.status === \"in_progress\" || obj.status === \"failed\") &&\n !(\"choices\" in obj) &&\n !(\"content\" in obj) &&\n !(\"candidates\" in obj) &&\n !(\"message\" in obj) &&\n !(\"data\" in obj) &&\n !(\"object\" in obj) &&\n !(\"outputs\" in obj)\n ) {\n if (obj.status === \"completed\" && obj.url) {\n return {\n video: {\n id: String(obj.id),\n status: \"completed\" as const,\n url: String(obj.url),\n },\n };\n }\n return {\n video: {\n id: String(obj.id),\n status: obj.status === \"failed\" ? (\"failed\" as const) : (\"processing\" as const),\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 const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;\n const hasContent = typeof message.content === \"string\" && message.content.length > 0;\n\n const openaiReasoning =\n typeof message.reasoning_content === \"string\" && message.reasoning_content.length > 0\n ? message.reasoning_content\n : undefined;\n\n if (hasToolCalls) {\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 ...(tc.id ? { id: String(tc.id) } : {}),\n };\n },\n );\n if (hasContent) {\n return {\n content: message.content as string,\n toolCalls,\n ...(openaiReasoning ? { reasoning: openaiReasoning } : {}),\n };\n }\n return { toolCalls, ...(openaiReasoning ? { reasoning: openaiReasoning } : {}) };\n }\n // Text content only\n if (hasContent) {\n return {\n content: message.content as string,\n ...(openaiReasoning ? { reasoning: openaiReasoning } : {}),\n };\n }\n // Recognized OpenAI shape but empty content (e.g. content filtering, zero max_tokens)\n return { content: \"\", ...(openaiReasoning ? { reasoning: openaiReasoning } : {}) };\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 const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n const textBlocks = blocks.filter((b) => b.type === \"text\" && typeof b.text === \"string\");\n const thinkingBlocks = blocks.filter((b) => b.type === \"thinking\");\n const hasToolCalls = toolUseBlocks.length > 0;\n const joinedText = textBlocks.map((b) => String(b.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const anthropicReasoning =\n thinkingBlocks.length > 0\n ? thinkingBlocks.map((b) => String(b.thinking ?? \"\")).join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 ...(b.id ? { id: String(b.id) } : {}),\n }));\n if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}),\n };\n }\n return { toolCalls, ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}),\n };\n }\n // Thinking-only response (no text, no tool calls)\n if (anthropicReasoning) {\n return { content: \"\", reasoning: anthropicReasoning };\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\n // Audio inlineData parts take priority over text\n const audioParts = parts.filter(\n (p: Record<string, unknown>) =>\n p.inlineData &&\n typeof (p.inlineData as Record<string, unknown>).mimeType === \"string\" &&\n ((p.inlineData as Record<string, unknown>).mimeType as string).startsWith(\"audio/\"),\n );\n if (audioParts.length > 0) {\n const inlineData = audioParts[0].inlineData as Record<string, unknown>;\n return {\n audio: {\n b64Json: String(inlineData.data ?? \"\"),\n contentType: String(inlineData.mimeType),\n },\n };\n }\n\n const fnCallParts = parts.filter((p) => p.functionCall);\n const textParts = parts.filter((p) => typeof p.text === \"string\" && !p.thought);\n const thoughtParts = parts.filter((p) => p.thought === true && typeof p.text === \"string\");\n const hasToolCalls = fnCallParts.length > 0;\n const joinedText = textParts.map((p) => String(p.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const geminiReasoning =\n thoughtParts.length > 0\n ? thoughtParts.map((p) => String(p.text ?? \"\")).join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(geminiReasoning ? { reasoning: geminiReasoning } : {}),\n };\n }\n return { toolCalls, ...(geminiReasoning ? { reasoning: geminiReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(geminiReasoning ? { reasoning: geminiReasoning } : {}),\n };\n }\n // Recognized Gemini shape but empty content\n return { content: \"\", ...(geminiReasoning ? { reasoning: geminiReasoning } : {}) };\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 const textBlocks = blocks.filter((b) => typeof b.text === \"string\");\n const reasoningBlocks = blocks.filter((b) => b.reasoningContent);\n const hasToolCalls = toolUseBlocks.length > 0;\n const joinedText = textBlocks.map((b) => String(b.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const bedrockReasoning =\n reasoningBlocks.length > 0\n ? reasoningBlocks\n .map((b) => {\n const rc = b.reasoningContent as Record<string, unknown>;\n const rt = rc?.reasoningText as Record<string, unknown> | undefined;\n return String(rt?.text ?? \"\");\n })\n .join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 ...(tu.toolUseId ? { id: String(tu.toolUseId) } : {}),\n };\n });\n if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}),\n };\n }\n return { toolCalls, ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}),\n };\n }\n // Recognized Bedrock Converse shape but empty content\n return { content: \"\", ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}) };\n }\n }\n\n // Cohere v2 chat: { finish_reason: \"...\", message: { content: [{ type: \"text\", text: \"...\" }] } }\n // Must come before Ollama since both have `message`, but Cohere has `finish_reason` at top level\n // (not nested in `choices`) and `message.content` as an array of typed objects.\n if (\n typeof obj.finish_reason === \"string\" &&\n obj.message &&\n typeof obj.message === \"object\" &&\n Array.isArray((obj.message as Record<string, unknown>).content)\n ) {\n const msg = obj.message as Record<string, unknown>;\n const contentBlocks = msg.content as Array<Record<string, unknown>>;\n const textBlock = contentBlocks.find((b) => b.type === \"text\" && typeof b.text === \"string\");\n const hasContent = textBlock && typeof textBlock.text === \"string\" && textBlock.text.length > 0;\n const toolCallBlocks = contentBlocks.filter((b) => b.type === \"tool_call\");\n\n // Also check message-level tool_calls (Cohere v2 puts tool calls here, not in content blocks)\n const msgToolCalls = Array.isArray(msg.tool_calls)\n ? (msg.tool_calls as Array<Record<string, unknown>>)\n : [];\n\n if (toolCallBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolCallBlocks.map((b) => ({\n name: String(b.name ?? (b.function as Record<string, unknown>)?.name ?? \"\"),\n arguments:\n typeof b.parameters === \"string\"\n ? b.parameters\n : typeof b.parameters === \"object\"\n ? JSON.stringify(b.parameters)\n : typeof (b.function as Record<string, unknown>)?.arguments === \"string\"\n ? String((b.function as Record<string, unknown>).arguments)\n : JSON.stringify((b.function as Record<string, unknown>)?.arguments),\n ...(b.id ? { id: String(b.id) } : {}),\n }));\n if (hasContent) {\n return { content: textBlock.text as string, toolCalls };\n }\n return { toolCalls };\n }\n if (msgToolCalls.length > 0) {\n const toolCalls: ToolCall[] = msgToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n return {\n name: String(tc.name ?? fn?.name ?? \"\"),\n arguments:\n typeof tc.parameters === \"string\"\n ? tc.parameters\n : typeof tc.parameters === \"object\"\n ? JSON.stringify(tc.parameters)\n : typeof fn?.arguments === \"string\"\n ? String(fn.arguments)\n : JSON.stringify(fn?.arguments),\n ...(tc.id ? { id: String(tc.id) } : {}),\n };\n });\n if (hasContent) {\n return { content: textBlock.text as string, toolCalls };\n }\n return { toolCalls };\n }\n if (hasContent) {\n return { content: textBlock.text as string };\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 const hasOllamaToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;\n const hasOllamaContent = typeof msg.content === \"string\" && msg.content.length > 0;\n\n if (hasOllamaToolCalls) {\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 if (hasOllamaContent) {\n return { content: msg.content as string, toolCalls };\n }\n return { toolCalls };\n }\n if (hasOllamaContent) {\n return { content: msg.content as string };\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 // Ollama /api/generate: { response: \"...\", done: true/false }\n if (typeof obj.response === \"string\" && \"done\" in obj) {\n return { content: obj.response };\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 */\ntype EndpointType =\n | \"chat\"\n | \"image\"\n | \"speech\"\n | \"transcription\"\n | \"video\"\n | \"embedding\"\n | \"audio-gen\"\n | \"fal-audio\"\n | \"fal\";\n\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n model?: string;\n endpoint?: EndpointType;\n} {\n const match: {\n userMessage?: string;\n inputText?: string;\n model?: string;\n endpoint?: EndpointType;\n } = {};\n\n // Include endpoint type for multimedia fixtures\n if (request._endpointType && request._endpointType !== \"chat\") {\n match.endpoint = request._endpointType as EndpointType;\n }\n\n // Embedding request\n if (request.embeddingInput) {\n match.inputText = request.embeddingInput;\n return match;\n }\n\n // Chat/multimedia 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 match.userMessage = text;\n }\n }\n\n // fal.ai fixtures are typically authored against the model id (e.g.\n // /flux/, /kling/) since the request body is opaque per-model JSON. Persist\n // the resolved model so replay matches what `onFalQueue(/flux/, ...)` writes.\n if (request._endpointType === \"fal\" && request.model) {\n match.model = request.model;\n }\n\n return match;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recorder.js","names":["crypto","http"],"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\";\nimport { getTestId, slugifyTestId } from \"./helpers.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 * Captured upstream response, exposed to the `beforeWriteResponse` hook so\n * callers can decide whether to relay it or mutate it (e.g. chaos injection).\n */\nexport interface ProxyCapturedResponse {\n status: number;\n contentType: string;\n body: Buffer;\n}\n\nexport interface ProxyOptions {\n /**\n * Called after the upstream response has been captured and recorded, but\n * before the relay to the client. Contract when the hook returns `true`:\n * 1. It wrote its own response body on `res`.\n * 2. It journaled the outcome (proxyAndRecord will NOT journal it).\n * 3. proxyAndRecord skips its default relay and returns `\"handled_by_hook\"`.\n *\n * Returning `false` (or omitting the hook) lets proxyAndRecord relay the\n * upstream response normally and leaves journaling to the caller via the\n * `\"relayed\"` outcome. Rejected promises propagate and leave the response\n * unwritten.\n *\n * NOT invoked when the upstream response was streamed progressively to the\n * client (SSE) — the bytes are already on the wire and can't be mutated.\n * Callers that need to observe the bypass should pass `onHookBypassed`.\n */\n beforeWriteResponse?: (response: ProxyCapturedResponse) => boolean | Promise<boolean>;\n /**\n * Called when `beforeWriteResponse` was provided but could not be invoked\n * because the upstream response was streamed to the client progressively.\n * The hook was rolled + wired but the bytes left before it could fire.\n * Intended for observability (log/metric/journal annotation) — proxyAndRecord\n * still returns `\"relayed\"`.\n */\n onHookBypassed?: (reason: \"sse_streamed\") => void;\n}\n\n/**\n * Outcome of a proxyAndRecord call, returned so the caller can decide whether\n * to journal, fall through, or stop — without sharing a mutable flag with the\n * `beforeWriteResponse` hook.\n *\n * - `\"not_configured\"` — no upstream URL for this provider; caller should fall\n * through to its next branch (typically strict/404).\n * - `\"relayed\"` — the default code path wrote a response (upstream success or\n * synthesized 502 error). Caller should journal the outcome.\n * - `\"handled_by_hook\"` — the hook wrote + journaled its own response. Caller\n * should not double-journal.\n */\nexport type ProxyOutcome = \"not_configured\" | \"relayed\" | \"handled_by_hook\";\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 */\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 options?: ProxyOptions,\n): Promise<ProxyOutcome> {\n const record = defaults.record;\n if (!record) return \"not_configured\";\n\n const providers = record.providers;\n // gemini-interactions shares the same upstream config as gemini\n const lookupKey = providerKey === \"gemini-interactions\" ? \"gemini\" : providerKey;\n const upstreamUrl = providers[lookupKey];\n\n if (!upstreamUrl) {\n defaults.logger.warn(`No upstream URL configured for provider \"${providerKey}\" — cannot proxy`);\n return \"not_configured\";\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 \"relayed\";\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 // Track whether we streamed SSE progressively to the client; if so,\n // skip the final res.writeHead/res.end relay at the bottom of this fn.\n let streamedToClient = false;\n let clientDisconnected = false;\n try {\n const result = await makeUpstreamRequest(target, forwardHeaders, requestBody, res);\n upstreamStatus = result.status;\n upstreamHeaders = result.headers;\n upstreamBody = result.body;\n rawBuffer = result.rawBuffer;\n streamedToClient = result.streamedToClient;\n clientDisconnected = result.clientDisconnected;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n defaults.logger.error(`Proxy request failed: ${msg}`);\n if (!res.headersSent) {\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 } else {\n // SSE headers already sent — gracefully close the connection\n res.end();\n }\n return \"relayed\";\n }\n\n // Detect streaming response and collapse if necessary.\n // NOTE: collapse buffers the entire upstream body in memory. Fine for\n // current chat-completions traffic (responses are small), but revisit if\n // this path ever proxies long-lived or large streams — both the buffer\n // here and the hook below receive the full payload.\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 // TTS response — binary audio, not JSON\n const isAudioResponse = ctString.toLowerCase().startsWith(\"audio/\");\n if (isAudioResponse && rawBuffer.length > 0) {\n // Derive format from Content-Type (audio/mpeg→mp3, audio/opus→opus, etc.)\n const audioFormat = ctString\n .toLowerCase()\n .replace(\"audio/\", \"\")\n .replace(\"mpeg\", \"mp3\")\n .split(\";\")[0]\n .trim();\n fixtureResponse = {\n audio: rawBuffer.toString(\"base64\"),\n ...(audioFormat && audioFormat !== \"mp3\" ? { format: audioFormat } : {}),\n };\n } else 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 // Audio from streamed inlineData (e.g. Gemini SSE with audio parts)\n if (collapsed.audioB64) {\n fixtureResponse = {\n audio: {\n b64Json: collapsed.audioB64,\n contentType: collapsed.audioMimeType ?? \"audio/mpeg\",\n },\n };\n } else if (\n collapsed.content === \"\" &&\n (!collapsed.toolCalls || collapsed.toolCalls.length === 0)\n ) {\n defaults.logger.warn(\"Stream collapse produced empty content — fixture may be incomplete\");\n const reasoningSpread = collapsed.reasoning ? { reasoning: collapsed.reasoning } : {};\n fixtureResponse = { content: collapsed.content ?? \"\", ...reasoningSpread };\n } else {\n const reasoningSpread = collapsed.reasoning ? { reasoning: collapsed.reasoning } : {};\n if (collapsed.toolCalls && collapsed.toolCalls.length > 0) {\n if (collapsed.content) {\n // Both content and toolCalls present — save as ContentWithToolCallsResponse\n fixtureResponse = {\n content: collapsed.content,\n toolCalls: collapsed.toolCalls,\n ...reasoningSpread,\n };\n } else {\n fixtureResponse = { toolCalls: collapsed.toolCalls, ...reasoningSpread };\n }\n } else {\n fixtureResponse = { content: collapsed.content ?? \"\", ...reasoningSpread };\n }\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 // fal.ai returns arbitrary, model-specific JSON shapes (images, video URLs,\n // audio file objects, etc.). Round-trip the payload verbatim instead of\n // letting buildFixtureResponse mis-classify it as ImageResponse / VideoResponse.\n if (request._endpointType === \"fal\" && parsedResponse !== null) {\n const obj = parsedResponse as Record<string, unknown>;\n const isErrorShape =\n typeof obj.error === \"object\" &&\n obj.error !== null &&\n typeof (obj.error as Record<string, unknown>).message === \"string\";\n if (isErrorShape) {\n const err = obj.error as Record<string, unknown>;\n fixtureResponse = {\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: upstreamStatus,\n };\n } else {\n fixtureResponse = { json: parsedResponse, status: upstreamStatus };\n }\n } else {\n let encodingFormat: string | undefined;\n try {\n encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : undefined;\n } catch (err) {\n defaults.logger.debug(\n `Could not parse encoding_format from raw body: ${err instanceof Error ? err.message : \"unknown error\"}`,\n );\n }\n fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);\n }\n }\n\n // If the client disconnected mid-stream, the collected data is likely\n // truncated. Saving a partial fixture is worse than saving none — skip\n // fixture persistence entirely.\n if (clientDisconnected) {\n defaults.logger.warn(\n \"Client disconnected mid-stream — skipping fixture save to avoid truncated data\",\n );\n return \"relayed\";\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 = {\n match: fixtureMatch,\n response: fixtureResponse,\n };\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 // Determine file path: snapshot-style (by testId) or legacy timestamp\n const testId = getTestId(req);\n let isSnapshotMode = testId !== \"__default__\";\n\n let filepath!: string;\n let mergeExisting = false;\n\n if (isSnapshotMode) {\n const slug = slugifyTestId(testId);\n if (!slug) {\n // Slug resolved to empty (e.g. testId was all punctuation) — fall back\n // to timestamp-based recording so we still capture the fixture.\n isSnapshotMode = false;\n } else {\n const dir = path.join(fixturePath, slug);\n filepath = path.join(dir, `${providerKey}.json`);\n mergeExisting = true;\n }\n }\n\n if (!isSnapshotMode) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `${providerKey}-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n filepath = path.join(fixturePath, filename);\n }\n\n let writtenToDisk = false;\n try {\n // Create the target directory (must be inside try/catch so filesystem\n // errors don't prevent the upstream response from being relayed).\n if (isSnapshotMode) {\n fs.mkdirSync(path.dirname(filepath), { recursive: true });\n } else {\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 // NOTE: the persisted fixture is always the real upstream response, even when chaos\n // later mutates the relay (e.g. malformed via beforeWriteResponse). Chaos is a live-traffic\n // decoration; the recorded artifact must stay truthful so replay sees what upstream said.\n let fileContent: { fixtures: unknown[]; _warning?: string };\n\n if (mergeExisting && fs.existsSync(filepath)) {\n try {\n const existing = JSON.parse(fs.readFileSync(filepath, \"utf-8\"));\n fileContent = { fixtures: [...(existing.fixtures ?? []), fixture] };\n } catch {\n // Corrupted file — overwrite\n fileContent = { fixtures: [fixture] };\n }\n } else {\n fileContent = { fixtures: [fixture] };\n }\n\n if (warnings.length > 0) {\n fileContent._warning = warnings.join(\"; \");\n }\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 if (!res.headersSent) {\n res.setHeader(\"X-LLMock-Record-Error\", msg);\n } else {\n defaults.logger.warn(`Cannot set X-LLMock-Record-Error header — headers already sent`);\n }\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 (skip when SSE was already streamed\n // progressively by makeUpstreamRequest — headers and body are already on\n // the wire).\n if (streamedToClient) {\n // SSE: the hook can't run because the body is already on the wire. Surface\n // the bypass so the caller (typically the chaos layer) can record it —\n // otherwise a configured chaos action silently no-ops on SSE traffic.\n if (options?.beforeWriteResponse && options.onHookBypassed) {\n options.onHookBypassed(\"sse_streamed\");\n }\n } else {\n // Give the caller a chance to mutate or replace the response before relay.\n // Used by the chaos layer to turn a successful proxy into a malformed body.\n // `body` is the raw upstream bytes so binary payloads survive round-tripping.\n if (options?.beforeWriteResponse) {\n const handled = await options.beforeWriteResponse({\n status: upstreamStatus,\n contentType: ctString,\n body: rawBuffer,\n });\n if (handled) return \"handled_by_hook\";\n }\n\n const relayHeaders: Record<string, string> = {};\n if (ctString) {\n relayHeaders[\"Content-Type\"] = ctString;\n }\n res.writeHead(upstreamStatus, relayHeaders);\n const isAudioRelay = ctString.toLowerCase().startsWith(\"audio/\");\n res.end(isBinaryStream || isAudioRelay ? rawBuffer : upstreamBody);\n }\n\n return \"relayed\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction makeUpstreamRequest(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes?: http.ServerResponse,\n): Promise<{\n status: number;\n headers: http.IncomingHttpHeaders;\n body: string;\n rawBuffer: Buffer;\n streamedToClient: boolean;\n clientDisconnected: boolean;\n}> {\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 // Detect Server-Sent Events so we can tee upstream chunks to the\n // client as they arrive rather than buffering the entire stream and\n // replaying it in a single res.end() at the bottom of proxyAndRecord.\n // Buffering collapses every SSE frame into one client-visible write,\n // which defeats progressive rendering in downstream consumers.\n const ct = res.headers[\"content-type\"];\n const ctStr = Array.isArray(ct) ? ct.join(\", \") : (ct ?? \"\");\n const isSSE = ctStr.toLowerCase().includes(\"text/event-stream\");\n let streamedToClient = false;\n let clientDisconnected = false;\n if (isSSE && clientRes && !clientRes.headersSent) {\n const relayHeaders: Record<string, string> = {};\n if (ctStr) relayHeaders[\"Content-Type\"] = ctStr;\n clientRes.writeHead(res.statusCode ?? 200, relayHeaders);\n // Flush headers immediately so the client starts parsing frames\n // before the first data chunk arrives.\n if (typeof clientRes.flushHeaders === \"function\") clientRes.flushHeaders();\n streamedToClient = true;\n // Stop relaying if the client disconnects mid-stream\n clientRes.on(\"close\", () => {\n clientDisconnected = true;\n req.destroy();\n });\n }\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n if (\n streamedToClient &&\n clientRes &&\n !clientDisconnected &&\n !clientRes.destroyed &&\n !clientRes.writableEnded\n ) {\n clientRes.write(chunk);\n }\n });\n res.on(\"error\", reject);\n res.on(\"end\", () => {\n const rawBuffer = Buffer.concat(chunks);\n if (\n streamedToClient &&\n clientRes &&\n !clientDisconnected &&\n !clientRes.destroyed &&\n !clientRes.writableEnded\n ) {\n clientRes.end();\n }\n resolve({\n status: res.statusCode ?? 500,\n headers: res.headers,\n body: rawBuffer.toString(),\n rawBuffer,\n streamedToClient,\n clientDisconnected,\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 aimock 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 — only match the actual { error: { message: \"...\" } } shape\n // used by OpenAI/Anthropic/etc., not arbitrary truthy `.error` fields.\n if (\n typeof obj.error === \"object\" &&\n obj.error !== null &&\n typeof (obj.error as Record<string, unknown>).message === \"string\"\n ) {\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 const buf = Buffer.from(first.embedding, \"base64\");\n if (buf.byteLength % 4 !== 0) {\n // Malformed embedding — return a zero-dimension embedding fixture\n return { embedding: [] };\n }\n const aligned = new Uint8Array(buf).buffer; // Always offset 0\n const floats = new Float32Array(aligned, 0, buf.byteLength / 4);\n return { embedding: Array.from(floats) };\n }\n // OpenAI image generation: { created, data: [{ url, b64_json, revised_prompt }] }\n if (first.url || first.b64_json) {\n const images = (obj.data as Array<Record<string, unknown>>).map((item) => ({\n ...(item.url ? { url: String(item.url) } : {}),\n ...(item.b64_json ? { b64Json: String(item.b64_json) } : {}),\n ...(item.revised_prompt ? { revisedPrompt: String(item.revised_prompt) } : {}),\n }));\n if (images.length === 1) {\n return { image: images[0] };\n }\n return { images };\n }\n }\n\n // Gemini Imagen: { predictions: [...] }\n if (Array.isArray(obj.predictions)) {\n const images = (obj.predictions as Array<Record<string, unknown>>).map((p) => ({\n ...(p.bytesBase64Encoded ? { b64Json: String(p.bytesBase64Encoded) } : {}),\n ...(p.mimeType ? { mimeType: String(p.mimeType) } : {}),\n }));\n if (images.length === 1) {\n return { image: images[0] };\n }\n return { images };\n }\n\n // OpenAI transcription: { text: \"...\", ... }\n if (\n typeof obj.text === \"string\" &&\n (obj.task === \"transcribe\" || obj.language !== undefined || obj.duration !== undefined)\n ) {\n return {\n transcription: {\n text: obj.text as string,\n ...(obj.language ? { language: String(obj.language) } : {}),\n ...(obj.duration !== undefined ? { duration: Number(obj.duration) } : {}),\n ...(Array.isArray(obj.words) ? { words: obj.words } : {}),\n ...(Array.isArray(obj.segments) ? { segments: obj.segments } : {}),\n },\n };\n }\n\n // Gemini Interactions: { id, status, outputs: [{ type: \"text\", text }, { type: \"function_call\", name, arguments }] }\n if (Array.isArray(obj.outputs) && obj.outputs.length > 0) {\n const outputs = obj.outputs as Array<Record<string, unknown>>;\n const fnCallOutputs = outputs.filter((o) => o.type === \"function_call\");\n const textOutputs = outputs.filter((o) => o.type === \"text\" && typeof o.text === \"string\");\n const hasToolCalls = fnCallOutputs.length > 0;\n const joinedText = textOutputs.map((o) => String(o.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n\n if (hasToolCalls) {\n const toolCalls: ToolCall[] = fnCallOutputs.map((o) => ({\n name: String(o.name),\n arguments: typeof o.arguments === \"string\" ? o.arguments : JSON.stringify(o.arguments),\n ...(o.id ? { id: String(o.id) } : {}),\n }));\n if (hasContent) {\n return { content: joinedText, toolCalls };\n }\n return { toolCalls };\n }\n if (hasContent) {\n return { content: joinedText };\n }\n // Recognized Gemini Interactions shape but empty content\n return { content: \"\" };\n }\n\n // OpenAI video generation: { id, status, ... }\n // Guard against false positives: many API responses have `id` + `status` fields\n // (e.g. chat completions, Anthropic messages). Reject if the response has fields\n // that indicate a known non-video format.\n if (\n typeof obj.id === \"string\" &&\n typeof obj.status === \"string\" &&\n (obj.status === \"completed\" || obj.status === \"in_progress\" || obj.status === \"failed\") &&\n !(\"choices\" in obj) &&\n !(\"content\" in obj) &&\n !(\"candidates\" in obj) &&\n !(\"message\" in obj) &&\n !(\"data\" in obj) &&\n !(\"object\" in obj) &&\n !(\"outputs\" in obj)\n ) {\n if (obj.status === \"completed\" && obj.url) {\n return {\n video: {\n id: String(obj.id),\n status: \"completed\" as const,\n url: String(obj.url),\n },\n };\n }\n return {\n video: {\n id: String(obj.id),\n status: obj.status === \"failed\" ? (\"failed\" as const) : (\"processing\" as const),\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 const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;\n const hasContent = typeof message.content === \"string\" && message.content.length > 0;\n\n const openaiReasoning =\n typeof message.reasoning_content === \"string\" && message.reasoning_content.length > 0\n ? message.reasoning_content\n : undefined;\n\n if (hasToolCalls) {\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 ...(tc.id ? { id: String(tc.id) } : {}),\n };\n },\n );\n if (hasContent) {\n return {\n content: message.content as string,\n toolCalls,\n ...(openaiReasoning ? { reasoning: openaiReasoning } : {}),\n };\n }\n return { toolCalls, ...(openaiReasoning ? { reasoning: openaiReasoning } : {}) };\n }\n // Text content only\n if (hasContent) {\n return {\n content: message.content as string,\n ...(openaiReasoning ? { reasoning: openaiReasoning } : {}),\n };\n }\n // Recognized OpenAI shape but empty content (e.g. content filtering, zero max_tokens)\n return { content: \"\", ...(openaiReasoning ? { reasoning: openaiReasoning } : {}) };\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 const toolUseBlocks = blocks.filter((b) => b.type === \"tool_use\");\n const textBlocks = blocks.filter((b) => b.type === \"text\" && typeof b.text === \"string\");\n const thinkingBlocks = blocks.filter((b) => b.type === \"thinking\");\n const hasToolCalls = toolUseBlocks.length > 0;\n const joinedText = textBlocks.map((b) => String(b.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const anthropicReasoning =\n thinkingBlocks.length > 0\n ? thinkingBlocks.map((b) => String(b.thinking ?? \"\")).join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 ...(b.id ? { id: String(b.id) } : {}),\n }));\n if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}),\n };\n }\n return { toolCalls, ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(anthropicReasoning ? { reasoning: anthropicReasoning } : {}),\n };\n }\n // Thinking-only response (no text, no tool calls)\n if (anthropicReasoning) {\n return { content: \"\", reasoning: anthropicReasoning };\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\n // Audio inlineData parts take priority over text\n const audioParts = parts.filter(\n (p: Record<string, unknown>) =>\n p.inlineData &&\n typeof (p.inlineData as Record<string, unknown>).mimeType === \"string\" &&\n ((p.inlineData as Record<string, unknown>).mimeType as string).startsWith(\"audio/\"),\n );\n if (audioParts.length > 0) {\n const inlineData = audioParts[0].inlineData as Record<string, unknown>;\n return {\n audio: {\n b64Json: String(inlineData.data ?? \"\"),\n contentType: String(inlineData.mimeType),\n },\n };\n }\n\n const fnCallParts = parts.filter((p) => p.functionCall);\n const textParts = parts.filter((p) => typeof p.text === \"string\" && !p.thought);\n const thoughtParts = parts.filter((p) => p.thought === true && typeof p.text === \"string\");\n const hasToolCalls = fnCallParts.length > 0;\n const joinedText = textParts.map((p) => String(p.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const geminiReasoning =\n thoughtParts.length > 0\n ? thoughtParts.map((p) => String(p.text ?? \"\")).join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(geminiReasoning ? { reasoning: geminiReasoning } : {}),\n };\n }\n return { toolCalls, ...(geminiReasoning ? { reasoning: geminiReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(geminiReasoning ? { reasoning: geminiReasoning } : {}),\n };\n }\n // Recognized Gemini shape but empty content\n return { content: \"\", ...(geminiReasoning ? { reasoning: geminiReasoning } : {}) };\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 const textBlocks = blocks.filter((b) => typeof b.text === \"string\");\n const reasoningBlocks = blocks.filter((b) => b.reasoningContent);\n const hasToolCalls = toolUseBlocks.length > 0;\n const joinedText = textBlocks.map((b) => String(b.text ?? \"\")).join(\"\");\n const hasContent = joinedText.length > 0;\n const bedrockReasoning =\n reasoningBlocks.length > 0\n ? reasoningBlocks\n .map((b) => {\n const rc = b.reasoningContent as Record<string, unknown>;\n const rt = rc?.reasoningText as Record<string, unknown> | undefined;\n return String(rt?.text ?? \"\");\n })\n .join(\"\")\n : undefined;\n\n if (hasToolCalls) {\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 ...(tu.toolUseId ? { id: String(tu.toolUseId) } : {}),\n };\n });\n if (hasContent) {\n return {\n content: joinedText,\n toolCalls,\n ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}),\n };\n }\n return { toolCalls, ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}) };\n }\n if (hasContent) {\n return {\n content: joinedText,\n ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}),\n };\n }\n // Recognized Bedrock Converse shape but empty content\n return { content: \"\", ...(bedrockReasoning ? { reasoning: bedrockReasoning } : {}) };\n }\n }\n\n // Cohere v2 chat: { finish_reason: \"...\", message: { content: [{ type: \"text\", text: \"...\" }] } }\n // Must come before Ollama since both have `message`, but Cohere has `finish_reason` at top level\n // (not nested in `choices`) and `message.content` as an array of typed objects.\n if (\n typeof obj.finish_reason === \"string\" &&\n obj.message &&\n typeof obj.message === \"object\" &&\n Array.isArray((obj.message as Record<string, unknown>).content)\n ) {\n const msg = obj.message as Record<string, unknown>;\n const contentBlocks = msg.content as Array<Record<string, unknown>>;\n const textBlock = contentBlocks.find((b) => b.type === \"text\" && typeof b.text === \"string\");\n const hasContent = textBlock && typeof textBlock.text === \"string\" && textBlock.text.length > 0;\n const toolCallBlocks = contentBlocks.filter((b) => b.type === \"tool_call\");\n\n // Also check message-level tool_calls (Cohere v2 puts tool calls here, not in content blocks)\n const msgToolCalls = Array.isArray(msg.tool_calls)\n ? (msg.tool_calls as Array<Record<string, unknown>>)\n : [];\n\n if (toolCallBlocks.length > 0) {\n const toolCalls: ToolCall[] = toolCallBlocks.map((b) => ({\n name: String(b.name ?? (b.function as Record<string, unknown>)?.name ?? \"\"),\n arguments:\n typeof b.parameters === \"string\"\n ? b.parameters\n : typeof b.parameters === \"object\"\n ? JSON.stringify(b.parameters)\n : typeof (b.function as Record<string, unknown>)?.arguments === \"string\"\n ? String((b.function as Record<string, unknown>).arguments)\n : JSON.stringify((b.function as Record<string, unknown>)?.arguments),\n ...(b.id ? { id: String(b.id) } : {}),\n }));\n if (hasContent) {\n return { content: textBlock.text as string, toolCalls };\n }\n return { toolCalls };\n }\n if (msgToolCalls.length > 0) {\n const toolCalls: ToolCall[] = msgToolCalls.map((tc) => {\n const fn = tc.function as Record<string, unknown> | undefined;\n return {\n name: String(tc.name ?? fn?.name ?? \"\"),\n arguments:\n typeof tc.parameters === \"string\"\n ? tc.parameters\n : typeof tc.parameters === \"object\"\n ? JSON.stringify(tc.parameters)\n : typeof fn?.arguments === \"string\"\n ? String(fn.arguments)\n : JSON.stringify(fn?.arguments),\n ...(tc.id ? { id: String(tc.id) } : {}),\n };\n });\n if (hasContent) {\n return { content: textBlock.text as string, toolCalls };\n }\n return { toolCalls };\n }\n if (hasContent) {\n return { content: textBlock.text as string };\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 const hasOllamaToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;\n const hasOllamaContent = typeof msg.content === \"string\" && msg.content.length > 0;\n\n if (hasOllamaToolCalls) {\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 if (hasOllamaContent) {\n return { content: msg.content as string, toolCalls };\n }\n return { toolCalls };\n }\n if (hasOllamaContent) {\n return { content: msg.content as string };\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 // Ollama /api/generate: { response: \"...\", done: true/false }\n if (typeof obj.response === \"string\" && \"done\" in obj) {\n return { content: obj.response };\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 */\ntype EndpointType =\n | \"chat\"\n | \"image\"\n | \"speech\"\n | \"transcription\"\n | \"video\"\n | \"embedding\"\n | \"audio-gen\"\n | \"fal-audio\"\n | \"fal\";\n\nfunction buildFixtureMatch(request: ChatCompletionRequest): {\n userMessage?: string;\n inputText?: string;\n model?: string;\n endpoint?: EndpointType;\n} {\n const match: {\n userMessage?: string;\n inputText?: string;\n model?: string;\n endpoint?: EndpointType;\n } = {};\n\n // Include endpoint type for multimedia fixtures\n if (request._endpointType && request._endpointType !== \"chat\") {\n match.endpoint = request._endpointType as EndpointType;\n }\n\n // Embedding request\n if (request.embeddingInput) {\n match.inputText = request.embeddingInput;\n return match;\n }\n\n // Chat/multimedia 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 match.userMessage = text;\n }\n }\n\n // fal.ai fixtures are typically authored against the model id (e.g.\n // /flux/, /kling/) since the request body is opaque per-model JSON. Persist\n // the resolved model so replay matches what `onFalQueue(/flux/, ...)` writes.\n if (request._endpointType === \"fal\" && request.model) {\n match.model = request.model;\n }\n\n return match;\n}\n"],"mappings":"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"}
@@ -714,7 +714,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
714
714
  } }));
715
715
  return;
716
716
  }
717
- const response = fixture.response;
717
+ const response = await require_helpers.resolveResponse(fixture, completionReq);
718
718
  const latency = fixture.latency ?? defaults.latency;
719
719
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
720
720
  if (require_helpers.isErrorResponse(response)) {
@@ -1 +1 @@
1
- {"version":3,"file":"responses.cjs","names":["generateToolCallId","generateId","calculateDelay","delay","flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isContentWithToolCallsResponse","extractOverrides","createInterruptionSignal","isTextResponse","isToolCallResponse"],"sources":["../src/responses.ts"],"sourcesContent":["/**\n * OpenAI Responses API support for aimock.\n *\n * Translates incoming /v1/responses requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the Responses API streaming (or non-streaming) format expected by @ai-sdk/openai.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateId,\n generateToolCallId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Responses API request types ────────────────────────────────────────────\n\ninterface ResponsesInputItem {\n role?: string;\n type?: string;\n content?: string | ResponsesContentPart[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n}\n\ninterface ResponsesContentPart {\n type: string;\n text?: string;\n}\n\ninterface ResponsesRequest {\n model: string;\n input: string | ResponsesInputItem[];\n instructions?: string;\n tools?: ResponsesToolDef[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\ninterface ResponsesToolDef {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n}\n\n// ─── Input conversion: Responses → ChatCompletions messages ─────────────────\n\nfunction extractTextContent(content: string | ResponsesContentPart[] | undefined): string {\n if (!content) return \"\";\n if (typeof content === \"string\") return content;\n return content\n .filter((p) => p.type === \"input_text\" || p.type === \"output_text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n}\n\nexport function responsesInputToMessages(req: ResponsesRequest): ChatMessage[] {\n const messages: ChatMessage[] = [];\n // Track item_reference placeholders so we can upgrade or clean them up\n const itemReferencePlaceholders = new WeakSet<ChatMessage>();\n\n // instructions field → system message\n if (req.instructions) {\n messages.push({ role: \"system\", content: req.instructions });\n }\n\n // The OpenAI Responses API accepts either a plain string or an array of input items.\n // When a string is passed, treat it as a single user message.\n if (typeof req.input === \"string\") {\n messages.push({ role: \"user\", content: req.input });\n return messages;\n }\n\n for (const item of req.input) {\n if (item.role === \"system\" || item.role === \"developer\") {\n messages.push({ role: \"system\", content: extractTextContent(item.content) });\n } else if (item.role === \"user\") {\n messages.push({ role: \"user\", content: extractTextContent(item.content) });\n } else if (item.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: extractTextContent(item.content) });\n } else if (item.type === \"function_call\") {\n // Previous assistant tool call — emit as assistant message with tool_calls\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: item.name ?? \"\", arguments: item.arguments ?? \"\" },\n },\n ],\n });\n } else if (item.type === \"function_call_output\") {\n // Bug 1 fix: If there's no preceding assistant message with a matching\n // tool_call for this call_id, synthesize one. This happens when the AI SDK\n // sends [user, item_reference, function_call_output] — the item_reference\n // placeholder (see below) has no tool_calls, so we need a real assistant\n // message with the tool_call for turnIndex counting.\n const hasMatchingToolCall = messages.some(\n (m) => m.role === \"assistant\" && m.tool_calls?.some((tc) => tc.id === item.call_id),\n );\n if (!hasMatchingToolCall) {\n // Check if the last message is an item_reference placeholder — if so,\n // upgrade it to carry the tool_call instead of synthesizing a duplicate.\n const lastMsg = messages[messages.length - 1];\n if (\n lastMsg &&\n lastMsg.role === \"assistant\" &&\n itemReferencePlaceholders.has(lastMsg) &&\n !lastMsg.tool_calls\n ) {\n lastMsg.content = null;\n lastMsg.tool_calls = [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n },\n ];\n itemReferencePlaceholders.delete(lastMsg);\n } else {\n // Multi-fco case: look for a recent assistant with tool_calls that\n // belongs to the same turn. After the first fco upgrades a placeholder,\n // subsequent fco's see [assistant(call_A), tool(call_A)] — the last\n // assistant with tool_calls (right before the trailing tool messages)\n // is the correct target.\n let appended = false;\n for (let k = messages.length - 1; k >= 0; k--) {\n const m = messages[k];\n if (m.role === \"assistant\" && m.tool_calls) {\n m.tool_calls.push({\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n });\n appended = true;\n break;\n }\n // Stop scanning if we hit a user message — different turn\n if (m.role === \"user\") break;\n }\n if (!appended) {\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n },\n ],\n });\n }\n }\n }\n messages.push({\n role: \"tool\",\n content: item.output ?? \"\",\n tool_call_id: item.call_id,\n });\n } else if (item.type === \"item_reference\") {\n // Bug 6 fix: item_reference items represent prior assistant turns (text\n // or function_call). Push a placeholder so they count in assistantCount.\n // If a subsequent function_call_output arrives, the handler above will\n // upgrade this placeholder to carry tool_calls (avoiding double-count).\n const placeholder: ChatMessage = { role: \"assistant\", content: \"\" };\n itemReferencePlaceholders.add(placeholder);\n messages.push(placeholder);\n } else {\n // Skip local_shell_call, mcp_list_tools, etc. — not needed for fixture\n // matching.\n }\n }\n\n return messages;\n}\n\nfunction responsesToolsToCompletionsTools(\n tools?: ResponsesToolDef[],\n): ToolDefinition[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools\n .filter((t) => t.type === \"function\")\n .map((t) => ({\n type: \"function\" as const,\n function: { name: t.name, description: t.description, parameters: t.parameters },\n }));\n}\n\nexport function responsesToCompletionRequest(req: ResponsesRequest): ChatCompletionRequest {\n return {\n model: req.model,\n messages: responsesInputToMessages(req),\n stream: req.stream,\n temperature: req.temperature,\n tools: responsesToolsToCompletionsTools(req.tools),\n tool_choice: req.tool_choice,\n };\n}\n\n// ─── Response building: fixture → Responses API format ──────────────────────\n\nfunction responsesStatus(finishReason: string | undefined, defaultStatus: string): string {\n if (!finishReason) return defaultStatus;\n if (finishReason === \"stop\") return \"completed\";\n if (finishReason === \"tool_calls\") return \"completed\";\n if (finishReason === \"length\") return \"incomplete\";\n if (finishReason === \"content_filter\") return \"failed\";\n return finishReason;\n}\n\nfunction responsesUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n total_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? 0,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.input_tokens ?? 0) + (overrides.usage.output_tokens ?? 0),\n };\n}\n\nfunction responseId(): string {\n return generateId(\"resp\");\n}\n\nfunction itemId(): string {\n return generateId(\"msg\");\n}\n\n// Streaming events for Responses API\n\nexport interface ResponsesSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport function buildTextStreamEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(\n model,\n chunkSize,\n reasoning,\n webSearches,\n overrides,\n );\n\n const { events: msgEvents, msgItem } = buildMessageOutputEvents(\n content,\n chunkSize,\n nextOutputIndex,\n );\n events.push(...msgEvents);\n\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: [...prefixOutputItems, msgItem],\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nexport function buildToolCallStreamEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const respId = overrides?.id ?? responseId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const events: ResponsesSSEEvent[] = [];\n\n // response.created\n events.push({\n type: \"response.created\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n events.push({\n type: \"response.in_progress\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n const outputItems: object[] = [];\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n const fcId = generateId(\"fc\");\n\n // output_item.added (function_call)\n events.push({\n type: \"response.output_item.added\",\n output_index: idx,\n item: {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n status: \"in_progress\",\n },\n });\n\n // function_call_arguments.delta\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n events.push({\n type: \"response.function_call_arguments.delta\",\n item_id: fcId,\n output_index: idx,\n delta: slice,\n });\n }\n\n // function_call_arguments.done\n events.push({\n type: \"response.function_call_arguments.done\",\n item_id: fcId,\n output_index: idx,\n arguments: args,\n });\n\n const doneItem = {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: args,\n status: \"completed\",\n };\n\n // output_item.done\n events.push({\n type: \"response.output_item.done\",\n output_index: idx,\n item: doneItem,\n });\n\n outputItems.push(doneItem);\n }\n\n // response.completed\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: outputItems,\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nfunction buildReasoningStreamEvents(\n reasoning: string,\n model: string,\n chunkSize: number,\n): ResponsesSSEEvent[] {\n const reasoningId = generateId(\"rs\");\n const events: ResponsesSSEEvent[] = [];\n\n events.push({\n type: \"response.output_item.added\",\n output_index: 0,\n item: {\n type: \"reasoning\",\n id: reasoningId,\n summary: [],\n },\n });\n\n events.push({\n type: \"response.reasoning_summary_part.added\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n part: { type: \"summary_text\", text: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"response.reasoning_summary_text.delta\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n delta: slice,\n });\n }\n\n events.push({\n type: \"response.reasoning_summary_text.done\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n text: reasoning,\n });\n\n events.push({\n type: \"response.reasoning_summary_part.done\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n part: { type: \"summary_text\", text: reasoning },\n });\n\n events.push({\n type: \"response.output_item.done\",\n output_index: 0,\n item: {\n type: \"reasoning\",\n id: reasoningId,\n summary: [{ type: \"summary_text\", text: reasoning }],\n },\n });\n\n return events;\n}\n\nfunction buildWebSearchStreamEvents(\n queries: string[],\n startOutputIndex: number,\n): ResponsesSSEEvent[] {\n const events: ResponsesSSEEvent[] = [];\n\n for (let i = 0; i < queries.length; i++) {\n const searchId = generateId(\"ws\");\n const outputIndex = startOutputIndex + i;\n\n events.push({\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: {\n type: \"web_search_call\",\n id: searchId,\n status: \"in_progress\",\n action: { type: \"search\", query: queries[i] },\n },\n });\n\n events.push({\n type: \"response.output_item.done\",\n output_index: outputIndex,\n item: {\n type: \"web_search_call\",\n id: searchId,\n status: \"completed\",\n action: { type: \"search\", query: queries[i] },\n },\n });\n }\n\n return events;\n}\n\n// ─── Shared streaming helpers ────────────────────────────────────────────────\n\ninterface PreambleResult {\n respId: string;\n created: number;\n events: ResponsesSSEEvent[];\n prefixOutputItems: object[];\n nextOutputIndex: number;\n}\n\nfunction buildResponsePreamble(\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): PreambleResult {\n const respId = overrides?.id ?? responseId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const events: ResponsesSSEEvent[] = [];\n const prefixOutputItems: object[] = [];\n let nextOutputIndex = 0;\n\n events.push({\n type: \"response.created\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n events.push({\n type: \"response.in_progress\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n if (reasoning) {\n const reasoningEvents = buildReasoningStreamEvents(reasoning, model, chunkSize);\n events.push(...reasoningEvents);\n const doneEvent = reasoningEvents.find(\n (e) =>\n e.type === \"response.output_item.done\" &&\n (e.item as { type: string })?.type === \"reasoning\",\n );\n if (doneEvent) prefixOutputItems.push(doneEvent.item as object);\n nextOutputIndex++;\n }\n\n if (webSearches && webSearches.length > 0) {\n const searchEvents = buildWebSearchStreamEvents(webSearches, nextOutputIndex);\n events.push(...searchEvents);\n const doneEvents = searchEvents.filter(\n (e) =>\n e.type === \"response.output_item.done\" &&\n (e.item as { type: string })?.type === \"web_search_call\",\n );\n for (const de of doneEvents) prefixOutputItems.push(de.item as object);\n nextOutputIndex += webSearches.length;\n }\n\n return { respId, created, events, prefixOutputItems, nextOutputIndex };\n}\n\ninterface MessageBlockResult {\n events: ResponsesSSEEvent[];\n msgItem: object;\n}\n\nfunction buildMessageOutputEvents(\n content: string,\n chunkSize: number,\n outputIndex: number,\n): MessageBlockResult {\n const msgId = itemId();\n const events: ResponsesSSEEvent[] = [];\n\n events.push({\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: { type: \"message\", id: msgId, status: \"in_progress\", role: \"assistant\", content: [] },\n });\n events.push({\n type: \"response.content_part.added\",\n output_index: outputIndex,\n content_index: 0,\n part: { type: \"output_text\", text: \"\", annotations: [] },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n events.push({\n type: \"response.output_text.delta\",\n item_id: msgId,\n output_index: outputIndex,\n content_index: 0,\n delta: content.slice(i, i + chunkSize),\n });\n }\n\n events.push({\n type: \"response.output_text.done\",\n output_index: outputIndex,\n content_index: 0,\n text: content,\n });\n events.push({\n type: \"response.content_part.done\",\n output_index: outputIndex,\n content_index: 0,\n part: { type: \"output_text\", text: content, annotations: [] },\n });\n\n const msgItem = {\n type: \"message\",\n id: msgId,\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: content, annotations: [] }],\n };\n\n events.push({ type: \"response.output_item.done\", output_index: outputIndex, item: msgItem });\n\n return { events, msgItem };\n}\n\n// ─── Non-streaming response builders ────────────────────────────────────────\n\nfunction buildOutputPrefix(content: string, reasoning?: string, webSearches?: string[]): object[] {\n const output: object[] = [];\n\n if (reasoning) {\n output.push({\n type: \"reasoning\",\n id: generateId(\"rs\"),\n summary: [{ type: \"summary_text\", text: reasoning }],\n });\n }\n\n if (webSearches && webSearches.length > 0) {\n for (const query of webSearches) {\n output.push({\n type: \"web_search_call\",\n id: generateId(\"ws\"),\n status: \"completed\",\n action: { type: \"search\", query },\n });\n }\n }\n\n output.push({\n type: \"message\",\n id: itemId(),\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: content, annotations: [] }],\n });\n\n return output;\n}\n\nfunction buildResponseEnvelope(\n model: string,\n output: object[],\n overrides?: ResponseOverrides,\n): object {\n return {\n id: overrides?.id ?? responseId(),\n object: \"response\",\n created_at: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output,\n usage: responsesUsage(overrides),\n };\n}\n\nfunction buildTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): object {\n return buildResponseEnvelope(\n model,\n buildOutputPrefix(content, reasoning, webSearches),\n overrides,\n );\n}\n\nfunction buildToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): object {\n return buildResponseEnvelope(\n model,\n toolCalls.map((tc) => ({\n type: \"function_call\",\n id: generateId(\"fc\"),\n call_id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: tc.arguments,\n status: \"completed\",\n })),\n overrides,\n );\n}\n\nexport function buildContentWithToolCallsStreamEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(\n model,\n chunkSize,\n reasoning,\n webSearches,\n overrides,\n );\n\n const { events: msgEvents, msgItem } = buildMessageOutputEvents(\n content,\n chunkSize,\n nextOutputIndex,\n );\n events.push(...msgEvents);\n\n const fcOutputItems: object[] = [];\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n const fcId = generateId(\"fc\");\n const fcOutputIndex = nextOutputIndex + 1 + idx;\n const args = tc.arguments;\n\n events.push({\n type: \"response.output_item.added\",\n output_index: fcOutputIndex,\n item: {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n status: \"in_progress\",\n },\n });\n\n for (let i = 0; i < args.length; i += chunkSize) {\n events.push({\n type: \"response.function_call_arguments.delta\",\n item_id: fcId,\n output_index: fcOutputIndex,\n delta: args.slice(i, i + chunkSize),\n });\n }\n\n events.push({\n type: \"response.function_call_arguments.done\",\n item_id: fcId,\n output_index: fcOutputIndex,\n arguments: args,\n });\n\n const doneItem = {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: args,\n status: \"completed\",\n };\n events.push({ type: \"response.output_item.done\", output_index: fcOutputIndex, item: doneItem });\n fcOutputItems.push(doneItem);\n }\n\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: [...prefixOutputItems, msgItem, ...fcOutputItems],\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nfunction buildContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): object {\n const output = buildOutputPrefix(content, reasoning, webSearches);\n for (const tc of toolCalls) {\n output.push({\n type: \"function_call\",\n id: generateId(\"fc\"),\n call_id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: tc.arguments,\n status: \"completed\",\n });\n }\n return buildResponseEnvelope(model, output, overrides);\n}\n\n// ─── SSE writer for Responses API ───────────────────────────────────────────\n\ninterface ResponsesStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeResponsesSSEStream(\n res: http.ServerResponse,\n events: ResponsesSSEEvent[],\n optionsOrLatency?: number | ResponsesStreamOptions,\n): Promise<boolean> {\n const opts: ResponsesStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleResponses(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n\n let responsesReq: ResponsesRequest;\n try {\n responsesReq = JSON.parse(raw) as ResponsesRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = responsesToCompletionRequest(responsesReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n defaults.logger.debug(\n `Responses fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n } else {\n defaults.logger.debug(\n `No responses fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"openai\",\n req.url ?? \"/v1/responses\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n defaults.logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Combined content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildContentWithToolCallsResponse(\n response.content,\n response.toolCalls,\n completionReq.model,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildContentWithToolCallsStreamEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildToolCallResponse(response.toolCalls, completionReq.model, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response did not match any known type\", type: \"server_error\" },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"responses.cjs","names":["generateToolCallId","generateId","calculateDelay","delay","flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","resolveResponse","isErrorResponse","isContentWithToolCallsResponse","extractOverrides","createInterruptionSignal","isTextResponse","isToolCallResponse"],"sources":["../src/responses.ts"],"sourcesContent":["/**\n * OpenAI Responses API support for aimock.\n *\n * Translates incoming /v1/responses requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the Responses API streaming (or non-streaming) format expected by @ai-sdk/openai.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateId,\n generateToolCallId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Responses API request types ────────────────────────────────────────────\n\ninterface ResponsesInputItem {\n role?: string;\n type?: string;\n content?: string | ResponsesContentPart[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n}\n\ninterface ResponsesContentPart {\n type: string;\n text?: string;\n}\n\ninterface ResponsesRequest {\n model: string;\n input: string | ResponsesInputItem[];\n instructions?: string;\n tools?: ResponsesToolDef[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\ninterface ResponsesToolDef {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n}\n\n// ─── Input conversion: Responses → ChatCompletions messages ─────────────────\n\nfunction extractTextContent(content: string | ResponsesContentPart[] | undefined): string {\n if (!content) return \"\";\n if (typeof content === \"string\") return content;\n return content\n .filter((p) => p.type === \"input_text\" || p.type === \"output_text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n}\n\nexport function responsesInputToMessages(req: ResponsesRequest): ChatMessage[] {\n const messages: ChatMessage[] = [];\n // Track item_reference placeholders so we can upgrade or clean them up\n const itemReferencePlaceholders = new WeakSet<ChatMessage>();\n\n // instructions field → system message\n if (req.instructions) {\n messages.push({ role: \"system\", content: req.instructions });\n }\n\n // The OpenAI Responses API accepts either a plain string or an array of input items.\n // When a string is passed, treat it as a single user message.\n if (typeof req.input === \"string\") {\n messages.push({ role: \"user\", content: req.input });\n return messages;\n }\n\n for (const item of req.input) {\n if (item.role === \"system\" || item.role === \"developer\") {\n messages.push({ role: \"system\", content: extractTextContent(item.content) });\n } else if (item.role === \"user\") {\n messages.push({ role: \"user\", content: extractTextContent(item.content) });\n } else if (item.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: extractTextContent(item.content) });\n } else if (item.type === \"function_call\") {\n // Previous assistant tool call — emit as assistant message with tool_calls\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: item.name ?? \"\", arguments: item.arguments ?? \"\" },\n },\n ],\n });\n } else if (item.type === \"function_call_output\") {\n // Bug 1 fix: If there's no preceding assistant message with a matching\n // tool_call for this call_id, synthesize one. This happens when the AI SDK\n // sends [user, item_reference, function_call_output] — the item_reference\n // placeholder (see below) has no tool_calls, so we need a real assistant\n // message with the tool_call for turnIndex counting.\n const hasMatchingToolCall = messages.some(\n (m) => m.role === \"assistant\" && m.tool_calls?.some((tc) => tc.id === item.call_id),\n );\n if (!hasMatchingToolCall) {\n // Check if the last message is an item_reference placeholder — if so,\n // upgrade it to carry the tool_call instead of synthesizing a duplicate.\n const lastMsg = messages[messages.length - 1];\n if (\n lastMsg &&\n lastMsg.role === \"assistant\" &&\n itemReferencePlaceholders.has(lastMsg) &&\n !lastMsg.tool_calls\n ) {\n lastMsg.content = null;\n lastMsg.tool_calls = [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n },\n ];\n itemReferencePlaceholders.delete(lastMsg);\n } else {\n // Multi-fco case: look for a recent assistant with tool_calls that\n // belongs to the same turn. After the first fco upgrades a placeholder,\n // subsequent fco's see [assistant(call_A), tool(call_A)] — the last\n // assistant with tool_calls (right before the trailing tool messages)\n // is the correct target.\n let appended = false;\n for (let k = messages.length - 1; k >= 0; k--) {\n const m = messages[k];\n if (m.role === \"assistant\" && m.tool_calls) {\n m.tool_calls.push({\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n });\n appended = true;\n break;\n }\n // Stop scanning if we hit a user message — different turn\n if (m.role === \"user\") break;\n }\n if (!appended) {\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? generateToolCallId(),\n type: \"function\",\n function: { name: \"\", arguments: \"\" },\n },\n ],\n });\n }\n }\n }\n messages.push({\n role: \"tool\",\n content: item.output ?? \"\",\n tool_call_id: item.call_id,\n });\n } else if (item.type === \"item_reference\") {\n // Bug 6 fix: item_reference items represent prior assistant turns (text\n // or function_call). Push a placeholder so they count in assistantCount.\n // If a subsequent function_call_output arrives, the handler above will\n // upgrade this placeholder to carry tool_calls (avoiding double-count).\n const placeholder: ChatMessage = { role: \"assistant\", content: \"\" };\n itemReferencePlaceholders.add(placeholder);\n messages.push(placeholder);\n } else {\n // Skip local_shell_call, mcp_list_tools, etc. — not needed for fixture\n // matching.\n }\n }\n\n return messages;\n}\n\nfunction responsesToolsToCompletionsTools(\n tools?: ResponsesToolDef[],\n): ToolDefinition[] | undefined {\n if (!tools || tools.length === 0) return undefined;\n return tools\n .filter((t) => t.type === \"function\")\n .map((t) => ({\n type: \"function\" as const,\n function: { name: t.name, description: t.description, parameters: t.parameters },\n }));\n}\n\nexport function responsesToCompletionRequest(req: ResponsesRequest): ChatCompletionRequest {\n return {\n model: req.model,\n messages: responsesInputToMessages(req),\n stream: req.stream,\n temperature: req.temperature,\n tools: responsesToolsToCompletionsTools(req.tools),\n tool_choice: req.tool_choice,\n };\n}\n\n// ─── Response building: fixture → Responses API format ──────────────────────\n\nfunction responsesStatus(finishReason: string | undefined, defaultStatus: string): string {\n if (!finishReason) return defaultStatus;\n if (finishReason === \"stop\") return \"completed\";\n if (finishReason === \"tool_calls\") return \"completed\";\n if (finishReason === \"length\") return \"incomplete\";\n if (finishReason === \"content_filter\") return \"failed\";\n return finishReason;\n}\n\nfunction responsesUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n total_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? 0,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.input_tokens ?? 0) + (overrides.usage.output_tokens ?? 0),\n };\n}\n\nfunction responseId(): string {\n return generateId(\"resp\");\n}\n\nfunction itemId(): string {\n return generateId(\"msg\");\n}\n\n// Streaming events for Responses API\n\nexport interface ResponsesSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\nexport function buildTextStreamEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(\n model,\n chunkSize,\n reasoning,\n webSearches,\n overrides,\n );\n\n const { events: msgEvents, msgItem } = buildMessageOutputEvents(\n content,\n chunkSize,\n nextOutputIndex,\n );\n events.push(...msgEvents);\n\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: [...prefixOutputItems, msgItem],\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nexport function buildToolCallStreamEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const respId = overrides?.id ?? responseId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const events: ResponsesSSEEvent[] = [];\n\n // response.created\n events.push({\n type: \"response.created\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n events.push({\n type: \"response.in_progress\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n const outputItems: object[] = [];\n\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n const fcId = generateId(\"fc\");\n\n // output_item.added (function_call)\n events.push({\n type: \"response.output_item.added\",\n output_index: idx,\n item: {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n status: \"in_progress\",\n },\n });\n\n // function_call_arguments.delta\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n events.push({\n type: \"response.function_call_arguments.delta\",\n item_id: fcId,\n output_index: idx,\n delta: slice,\n });\n }\n\n // function_call_arguments.done\n events.push({\n type: \"response.function_call_arguments.done\",\n item_id: fcId,\n output_index: idx,\n arguments: args,\n });\n\n const doneItem = {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: args,\n status: \"completed\",\n };\n\n // output_item.done\n events.push({\n type: \"response.output_item.done\",\n output_index: idx,\n item: doneItem,\n });\n\n outputItems.push(doneItem);\n }\n\n // response.completed\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: outputItems,\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nfunction buildReasoningStreamEvents(\n reasoning: string,\n model: string,\n chunkSize: number,\n): ResponsesSSEEvent[] {\n const reasoningId = generateId(\"rs\");\n const events: ResponsesSSEEvent[] = [];\n\n events.push({\n type: \"response.output_item.added\",\n output_index: 0,\n item: {\n type: \"reasoning\",\n id: reasoningId,\n summary: [],\n },\n });\n\n events.push({\n type: \"response.reasoning_summary_part.added\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n part: { type: \"summary_text\", text: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"response.reasoning_summary_text.delta\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n delta: slice,\n });\n }\n\n events.push({\n type: \"response.reasoning_summary_text.done\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n text: reasoning,\n });\n\n events.push({\n type: \"response.reasoning_summary_part.done\",\n item_id: reasoningId,\n output_index: 0,\n summary_index: 0,\n part: { type: \"summary_text\", text: reasoning },\n });\n\n events.push({\n type: \"response.output_item.done\",\n output_index: 0,\n item: {\n type: \"reasoning\",\n id: reasoningId,\n summary: [{ type: \"summary_text\", text: reasoning }],\n },\n });\n\n return events;\n}\n\nfunction buildWebSearchStreamEvents(\n queries: string[],\n startOutputIndex: number,\n): ResponsesSSEEvent[] {\n const events: ResponsesSSEEvent[] = [];\n\n for (let i = 0; i < queries.length; i++) {\n const searchId = generateId(\"ws\");\n const outputIndex = startOutputIndex + i;\n\n events.push({\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: {\n type: \"web_search_call\",\n id: searchId,\n status: \"in_progress\",\n action: { type: \"search\", query: queries[i] },\n },\n });\n\n events.push({\n type: \"response.output_item.done\",\n output_index: outputIndex,\n item: {\n type: \"web_search_call\",\n id: searchId,\n status: \"completed\",\n action: { type: \"search\", query: queries[i] },\n },\n });\n }\n\n return events;\n}\n\n// ─── Shared streaming helpers ────────────────────────────────────────────────\n\ninterface PreambleResult {\n respId: string;\n created: number;\n events: ResponsesSSEEvent[];\n prefixOutputItems: object[];\n nextOutputIndex: number;\n}\n\nfunction buildResponsePreamble(\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): PreambleResult {\n const respId = overrides?.id ?? responseId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const events: ResponsesSSEEvent[] = [];\n const prefixOutputItems: object[] = [];\n let nextOutputIndex = 0;\n\n events.push({\n type: \"response.created\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n events.push({\n type: \"response.in_progress\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: effectiveModel,\n status: \"in_progress\",\n output: [],\n },\n });\n\n if (reasoning) {\n const reasoningEvents = buildReasoningStreamEvents(reasoning, model, chunkSize);\n events.push(...reasoningEvents);\n const doneEvent = reasoningEvents.find(\n (e) =>\n e.type === \"response.output_item.done\" &&\n (e.item as { type: string })?.type === \"reasoning\",\n );\n if (doneEvent) prefixOutputItems.push(doneEvent.item as object);\n nextOutputIndex++;\n }\n\n if (webSearches && webSearches.length > 0) {\n const searchEvents = buildWebSearchStreamEvents(webSearches, nextOutputIndex);\n events.push(...searchEvents);\n const doneEvents = searchEvents.filter(\n (e) =>\n e.type === \"response.output_item.done\" &&\n (e.item as { type: string })?.type === \"web_search_call\",\n );\n for (const de of doneEvents) prefixOutputItems.push(de.item as object);\n nextOutputIndex += webSearches.length;\n }\n\n return { respId, created, events, prefixOutputItems, nextOutputIndex };\n}\n\ninterface MessageBlockResult {\n events: ResponsesSSEEvent[];\n msgItem: object;\n}\n\nfunction buildMessageOutputEvents(\n content: string,\n chunkSize: number,\n outputIndex: number,\n): MessageBlockResult {\n const msgId = itemId();\n const events: ResponsesSSEEvent[] = [];\n\n events.push({\n type: \"response.output_item.added\",\n output_index: outputIndex,\n item: { type: \"message\", id: msgId, status: \"in_progress\", role: \"assistant\", content: [] },\n });\n events.push({\n type: \"response.content_part.added\",\n output_index: outputIndex,\n content_index: 0,\n part: { type: \"output_text\", text: \"\", annotations: [] },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n events.push({\n type: \"response.output_text.delta\",\n item_id: msgId,\n output_index: outputIndex,\n content_index: 0,\n delta: content.slice(i, i + chunkSize),\n });\n }\n\n events.push({\n type: \"response.output_text.done\",\n output_index: outputIndex,\n content_index: 0,\n text: content,\n });\n events.push({\n type: \"response.content_part.done\",\n output_index: outputIndex,\n content_index: 0,\n part: { type: \"output_text\", text: content, annotations: [] },\n });\n\n const msgItem = {\n type: \"message\",\n id: msgId,\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: content, annotations: [] }],\n };\n\n events.push({ type: \"response.output_item.done\", output_index: outputIndex, item: msgItem });\n\n return { events, msgItem };\n}\n\n// ─── Non-streaming response builders ────────────────────────────────────────\n\nfunction buildOutputPrefix(content: string, reasoning?: string, webSearches?: string[]): object[] {\n const output: object[] = [];\n\n if (reasoning) {\n output.push({\n type: \"reasoning\",\n id: generateId(\"rs\"),\n summary: [{ type: \"summary_text\", text: reasoning }],\n });\n }\n\n if (webSearches && webSearches.length > 0) {\n for (const query of webSearches) {\n output.push({\n type: \"web_search_call\",\n id: generateId(\"ws\"),\n status: \"completed\",\n action: { type: \"search\", query },\n });\n }\n }\n\n output.push({\n type: \"message\",\n id: itemId(),\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: content, annotations: [] }],\n });\n\n return output;\n}\n\nfunction buildResponseEnvelope(\n model: string,\n output: object[],\n overrides?: ResponseOverrides,\n): object {\n return {\n id: overrides?.id ?? responseId(),\n object: \"response\",\n created_at: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output,\n usage: responsesUsage(overrides),\n };\n}\n\nfunction buildTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): object {\n return buildResponseEnvelope(\n model,\n buildOutputPrefix(content, reasoning, webSearches),\n overrides,\n );\n}\n\nfunction buildToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): object {\n return buildResponseEnvelope(\n model,\n toolCalls.map((tc) => ({\n type: \"function_call\",\n id: generateId(\"fc\"),\n call_id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: tc.arguments,\n status: \"completed\",\n })),\n overrides,\n );\n}\n\nexport function buildContentWithToolCallsStreamEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): ResponsesSSEEvent[] {\n const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(\n model,\n chunkSize,\n reasoning,\n webSearches,\n overrides,\n );\n\n const { events: msgEvents, msgItem } = buildMessageOutputEvents(\n content,\n chunkSize,\n nextOutputIndex,\n );\n events.push(...msgEvents);\n\n const fcOutputItems: object[] = [];\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const callId = tc.id || generateToolCallId();\n const fcId = generateId(\"fc\");\n const fcOutputIndex = nextOutputIndex + 1 + idx;\n const args = tc.arguments;\n\n events.push({\n type: \"response.output_item.added\",\n output_index: fcOutputIndex,\n item: {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: \"\",\n status: \"in_progress\",\n },\n });\n\n for (let i = 0; i < args.length; i += chunkSize) {\n events.push({\n type: \"response.function_call_arguments.delta\",\n item_id: fcId,\n output_index: fcOutputIndex,\n delta: args.slice(i, i + chunkSize),\n });\n }\n\n events.push({\n type: \"response.function_call_arguments.done\",\n item_id: fcId,\n output_index: fcOutputIndex,\n arguments: args,\n });\n\n const doneItem = {\n type: \"function_call\",\n id: fcId,\n call_id: callId,\n name: tc.name,\n arguments: args,\n status: \"completed\",\n };\n events.push({ type: \"response.output_item.done\", output_index: fcOutputIndex, item: doneItem });\n fcOutputItems.push(doneItem);\n }\n\n events.push({\n type: \"response.completed\",\n response: {\n id: respId,\n object: \"response\",\n created_at: created,\n model: overrides?.model ?? model,\n status: responsesStatus(overrides?.finishReason, \"completed\"),\n output: [...prefixOutputItems, msgItem, ...fcOutputItems],\n usage: responsesUsage(overrides),\n },\n });\n\n return events;\n}\n\nfunction buildContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n webSearches?: string[],\n overrides?: ResponseOverrides,\n): object {\n const output = buildOutputPrefix(content, reasoning, webSearches);\n for (const tc of toolCalls) {\n output.push({\n type: \"function_call\",\n id: generateId(\"fc\"),\n call_id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: tc.arguments,\n status: \"completed\",\n });\n }\n return buildResponseEnvelope(model, output, overrides);\n}\n\n// ─── SSE writer for Responses API ───────────────────────────────────────────\n\ninterface ResponsesStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeResponsesSSEStream(\n res: http.ServerResponse,\n events: ResponsesSSEEvent[],\n optionsOrLatency?: number | ResponsesStreamOptions,\n): Promise<boolean> {\n const opts: ResponsesStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleResponses(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n\n let responsesReq: ResponsesRequest;\n try {\n responsesReq = JSON.parse(raw) as ResponsesRequest;\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = responsesToCompletionRequest(responsesReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n defaults.logger.debug(\n `Responses fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n } else {\n defaults.logger.debug(\n `No responses fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"openai\",\n req.url ?? \"/v1/responses\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (defaults.strict) {\n defaults.logger.error(\n `STRICT: No fixture matched for ${req.method ?? \"POST\"} ${req.url ?? \"/v1/responses\"}`,\n );\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Combined content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildContentWithToolCallsResponse(\n response.content,\n response.toolCalls,\n completionReq.model,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildContentWithToolCallsStreamEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n response.webSearches,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (responsesReq.stream !== true) {\n const body = buildToolCallResponse(response.toolCalls, completionReq.model, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeResponsesSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/responses\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response did not match any known type\", type: \"server_error\" },\n }),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"responses.d.cts","names":[],"sources":["../src/responses.ts"],"sourcesContent":[],"mappings":";;;;;;UA4QiB,iBAAA;;;;iBAKD,qBAAA,4GAMF,oBACX;iBAgCa,yBAAA,YACH,0DAGC,oBACX;iBA6aa,qCAAA,6BAEH,sGAKC,oBACX;iBAqJmB,eAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"responses.d.cts","names":[],"sources":["../src/responses.ts"],"sourcesContent":[],"mappings":";;;;;;UA6QiB,iBAAA;;;;iBAKD,qBAAA,4GAMF,oBACX;iBAgCa,yBAAA,YACH,0DAGC,oBACX;iBA6aa,qCAAA,6BAEH,sGAKC,oBACX;iBAqJmB,eAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"responses.d.ts","names":[],"sources":["../src/responses.ts"],"sourcesContent":[],"mappings":";;;;;;UA4QiB,iBAAA;;;;iBAKD,qBAAA,4GAMF,oBACX;iBAgCa,yBAAA,YACH,0DAGC,oBACX;iBA6aa,qCAAA,6BAEH,sGAKC,oBACX;iBAqJmB,eAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"responses.d.ts","names":[],"sources":["../src/responses.ts"],"sourcesContent":[],"mappings":";;;;;;UA6QiB,iBAAA;;;;iBAKD,qBAAA,4GAMF,oBACX;iBAgCa,yBAAA,YACH,0DAGC,oBACX;iBA6aa,qCAAA,6BAEH,sGAKC,oBACX;iBAqJmB,eAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
package/dist/responses.js CHANGED
@@ -1,4 +1,4 @@
1
- import { extractOverrides, flattenHeaders, generateId, generateToolCallId, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse } from "./helpers.js";
1
+ import { extractOverrides, flattenHeaders, generateId, generateToolCallId, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse } from "./helpers.js";
2
2
  import { matchFixture } from "./router.js";
3
3
  import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
4
4
  import { createInterruptionSignal } from "./interruption.js";
@@ -714,7 +714,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
714
714
  } }));
715
715
  return;
716
716
  }
717
- const response = fixture.response;
717
+ const response = await resolveResponse(fixture, completionReq);
718
718
  const latency = fixture.latency ?? defaults.latency;
719
719
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
720
720
  if (isErrorResponse(response)) {