@copilotkit/aimock 1.7.0 → 1.9.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 (134) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +3 -1
  4. package/dist/bedrock-converse.cjs +13 -8
  5. package/dist/bedrock-converse.cjs.map +1 -1
  6. package/dist/bedrock-converse.d.cts.map +1 -1
  7. package/dist/bedrock-converse.d.ts.map +1 -1
  8. package/dist/bedrock-converse.js +14 -9
  9. package/dist/bedrock-converse.js.map +1 -1
  10. package/dist/bedrock.cjs +51 -15
  11. package/dist/bedrock.cjs.map +1 -1
  12. package/dist/bedrock.d.cts.map +1 -1
  13. package/dist/bedrock.d.ts.map +1 -1
  14. package/dist/bedrock.js +52 -16
  15. package/dist/bedrock.js.map +1 -1
  16. package/dist/cohere.cjs +3 -2
  17. package/dist/cohere.cjs.map +1 -1
  18. package/dist/cohere.d.cts.map +1 -1
  19. package/dist/cohere.d.ts.map +1 -1
  20. package/dist/cohere.js +4 -3
  21. package/dist/cohere.js.map +1 -1
  22. package/dist/embeddings.cjs +3 -2
  23. package/dist/embeddings.cjs.map +1 -1
  24. package/dist/embeddings.d.cts.map +1 -1
  25. package/dist/embeddings.d.ts.map +1 -1
  26. package/dist/embeddings.js +4 -3
  27. package/dist/embeddings.js.map +1 -1
  28. package/dist/gemini.cjs +129 -35
  29. package/dist/gemini.cjs.map +1 -1
  30. package/dist/gemini.d.cts.map +1 -1
  31. package/dist/gemini.d.ts.map +1 -1
  32. package/dist/gemini.js +130 -36
  33. package/dist/gemini.js.map +1 -1
  34. package/dist/helpers.cjs +154 -3
  35. package/dist/helpers.cjs.map +1 -1
  36. package/dist/helpers.d.cts +1 -1
  37. package/dist/helpers.d.cts.map +1 -1
  38. package/dist/helpers.d.ts +1 -1
  39. package/dist/helpers.d.ts.map +1 -1
  40. package/dist/helpers.js +151 -4
  41. package/dist/helpers.js.map +1 -1
  42. package/dist/index.cjs +1 -0
  43. package/dist/index.d.cts +2 -2
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.js +2 -2
  46. package/dist/journal.cjs +26 -9
  47. package/dist/journal.cjs.map +1 -1
  48. package/dist/journal.d.cts +10 -5
  49. package/dist/journal.d.cts.map +1 -1
  50. package/dist/journal.d.ts +10 -5
  51. package/dist/journal.d.ts.map +1 -1
  52. package/dist/journal.js +26 -10
  53. package/dist/journal.js.map +1 -1
  54. package/dist/llmock.cjs +2 -2
  55. package/dist/llmock.cjs.map +1 -1
  56. package/dist/llmock.d.cts +1 -1
  57. package/dist/llmock.d.ts +1 -1
  58. package/dist/llmock.js +2 -2
  59. package/dist/llmock.js.map +1 -1
  60. package/dist/messages.cjs +192 -2
  61. package/dist/messages.cjs.map +1 -1
  62. package/dist/messages.d.cts.map +1 -1
  63. package/dist/messages.d.ts.map +1 -1
  64. package/dist/messages.js +193 -3
  65. package/dist/messages.js.map +1 -1
  66. package/dist/ollama.cjs +39 -13
  67. package/dist/ollama.cjs.map +1 -1
  68. package/dist/ollama.d.cts.map +1 -1
  69. package/dist/ollama.d.ts.map +1 -1
  70. package/dist/ollama.js +40 -14
  71. package/dist/ollama.js.map +1 -1
  72. package/dist/recorder.cjs +1 -1
  73. package/dist/recorder.cjs.map +1 -1
  74. package/dist/recorder.d.cts +1 -0
  75. package/dist/recorder.d.cts.map +1 -1
  76. package/dist/recorder.d.ts +1 -0
  77. package/dist/recorder.d.ts.map +1 -1
  78. package/dist/recorder.js +1 -1
  79. package/dist/recorder.js.map +1 -1
  80. package/dist/responses.cjs +250 -126
  81. package/dist/responses.cjs.map +1 -1
  82. package/dist/responses.d.cts.map +1 -1
  83. package/dist/responses.d.ts.map +1 -1
  84. package/dist/responses.js +251 -127
  85. package/dist/responses.js.map +1 -1
  86. package/dist/router.cjs +16 -10
  87. package/dist/router.cjs.map +1 -1
  88. package/dist/router.d.cts +1 -1
  89. package/dist/router.d.cts.map +1 -1
  90. package/dist/router.d.ts +1 -1
  91. package/dist/router.d.ts.map +1 -1
  92. package/dist/router.js +16 -10
  93. package/dist/router.js.map +1 -1
  94. package/dist/server.cjs +47 -7
  95. package/dist/server.cjs.map +1 -1
  96. package/dist/server.d.cts.map +1 -1
  97. package/dist/server.d.ts.map +1 -1
  98. package/dist/server.js +48 -8
  99. package/dist/server.js.map +1 -1
  100. package/dist/stream-collapse.cjs +48 -40
  101. package/dist/stream-collapse.cjs.map +1 -1
  102. package/dist/stream-collapse.d.cts.map +1 -1
  103. package/dist/stream-collapse.d.ts.map +1 -1
  104. package/dist/stream-collapse.js +48 -40
  105. package/dist/stream-collapse.js.map +1 -1
  106. package/dist/types.d.cts +22 -1
  107. package/dist/types.d.cts.map +1 -1
  108. package/dist/types.d.ts +22 -1
  109. package/dist/types.d.ts.map +1 -1
  110. package/dist/ws-gemini-live.cjs +4 -2
  111. package/dist/ws-gemini-live.cjs.map +1 -1
  112. package/dist/ws-gemini-live.d.cts +3 -1
  113. package/dist/ws-gemini-live.d.cts.map +1 -1
  114. package/dist/ws-gemini-live.d.ts +3 -1
  115. package/dist/ws-gemini-live.d.ts.map +1 -1
  116. package/dist/ws-gemini-live.js +4 -2
  117. package/dist/ws-gemini-live.js.map +1 -1
  118. package/dist/ws-realtime.cjs +4 -2
  119. package/dist/ws-realtime.cjs.map +1 -1
  120. package/dist/ws-realtime.d.cts +3 -1
  121. package/dist/ws-realtime.d.cts.map +1 -1
  122. package/dist/ws-realtime.d.ts +3 -1
  123. package/dist/ws-realtime.d.ts.map +1 -1
  124. package/dist/ws-realtime.js +4 -2
  125. package/dist/ws-realtime.js.map +1 -1
  126. package/dist/ws-responses.cjs +4 -2
  127. package/dist/ws-responses.cjs.map +1 -1
  128. package/dist/ws-responses.d.cts +3 -1
  129. package/dist/ws-responses.d.cts.map +1 -1
  130. package/dist/ws-responses.d.ts +3 -1
  131. package/dist/ws-responses.d.ts.map +1 -1
  132. package/dist/ws-responses.js +4 -2
  133. package/dist/ws-responses.js.map +1 -1
  134. package/package.json +5 -1
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.cjs","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing either `{ content }` or `{ toolCalls }`.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\n// TODO: Consider making this a discriminated union ({ type: \"text\"; content: string }\n// | { type: \"toolCalls\"; toolCalls: ToolCall[] } | { type: \"empty\" }) to prevent\n// ambiguous results and simplify downstream consumers.\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\" && typeof item.query === \"string\") {\n webSearchQueries.push(item.query);\n continue;\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n // Handle functionCall parts\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCallMap = new Map<number, { name: string; arguments: string }>();\n for (let i = 0; i < fnCallParts.length; i++) {\n const fc = fnCallParts[i].functionCall as Record<string, unknown>;\n toolCallMap.set(i, {\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n }\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n }\n\n if (typeof parts[0].text === \"string\") {\n content += parts[0].text;\n }\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return { toolCalls, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream-collapse.cjs","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\n// TODO: Consider making this a discriminated union ({ type: \"text\"; content: string }\n// | { type: \"toolCalls\"; toolCalls: ToolCall[] } | { type: \"empty\" }) to prevent\n// ambiguous results and simplify downstream consumers.\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n toolCalls.push({\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n } else if (typeof part.text === \"string\") {\n content += part.text;\n }\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA8XgB,UA3WC,cAAA,CA2WgB;EAoKjB,OAAA,CAAA,EAAA,MAAA;EAA0B,SAAA,CAAA,EAAA,MAAA;aAAO,CAAA,EAAA,MAAA,EAAA;WAAS,CAAA,EA3gB5C,QA2gB4C,EAAA;EAAc,aAAA,CAAA,EAAA,MAAA;EAqHxD,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAhnBA,iBAAA,gBAAiC;;;;;;;;iBA2HjC,oBAAA,gBAAoC;;;;;;;iBAwFpC,iBAAA,gBAAiC;;;;;;;;;;iBAwEjC,oBAAA,gBAAoC;;;;;;;iBA4DpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA2iBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA8XgB,UA3WC,cAAA,CA2WgB;EAoKjB,OAAA,CAAA,EAAA,MAAA;EAA0B,SAAA,CAAA,EAAA,MAAA;aAAO,CAAA,EAAA,MAAA,EAAA;WAAS,CAAA,EA3gB5C,QA2gB4C,EAAA;EAAc,aAAA,CAAA,EAAA,MAAA;EAqHxD,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAhnBA,iBAAA,gBAAiC;;;;;;;;iBA2HjC,oBAAA,gBAAoC;;;;;;;iBAwFpC,iBAAA,gBAAiC;;;;;;;;;;iBAwEjC,oBAAA,gBAAoC;;;;;;;iBA4DpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AA2iBgB,UAvhBC,cAAA,CAuhBD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;aAAS,CAAA,EAAA,MAAA,EAAA;EAAc,SAAA,CAAA,EAnhB1D,QAmhB0D,EAAA;EAqHxD,aAAA,CAAA,EAAA,MAAA;EAAyB,SAAA,CAAA,EAAA,OAAA;;;;;;;;;iBAxnBzB,iBAAA,gBAAiC;;;;;;;;iBAoIjC,oBAAA,gBAAoC;;;;;;;iBAyFpC,iBAAA,gBAAiC;;;;;;;;;;iBAkEjC,oBAAA,gBAAoC;;;;;;;iBAgEpC,iBAAA,gBAAiC;;;;;;;iBAoKjC,0BAAA,OAAiC,SAAS;;;;;;iBAqH1C,yBAAA,mCAED,kCACE,iBACN,SACR"}
@@ -6,7 +6,8 @@ import { crc32 } from "node:zlib";
6
6
  *
7
7
  * Each function takes a raw streaming response body (SSE, NDJSON, or binary
8
8
  * EventStream) and collapses it into a non-streaming fixture response
9
- * containing either `{ content }` or `{ toolCalls }`.
9
+ * containing `{ content }`, `{ toolCalls }`, or both when the stream includes
10
+ * text followed by tool calls.
10
11
  */
11
12
  /**
12
13
  * Collapse OpenAI Chat Completions SSE stream into a single response.
@@ -40,9 +41,12 @@ function collapseOpenAISSE(body) {
40
41
  }
41
42
  if (parsed.type === "response.output_item.done") {
42
43
  const item = parsed.item;
43
- if (item?.type === "web_search_call" && typeof item.query === "string") {
44
- webSearchQueries.push(item.query);
45
- continue;
44
+ if (item?.type === "web_search_call") {
45
+ const action = item.action;
46
+ if (action && typeof action.query === "string") {
47
+ webSearchQueries.push(action.query);
48
+ continue;
49
+ }
46
50
  }
47
51
  }
48
52
  if (parsed.type === "response.output_text.delta" && typeof parsed.delta === "string") {
@@ -54,6 +58,7 @@ function collapseOpenAISSE(body) {
54
58
  if (!choices || choices.length === 0) continue;
55
59
  const delta = choices[0].delta;
56
60
  if (!delta) continue;
61
+ if (typeof delta.reasoning_content === "string") reasoning += delta.reasoning_content;
57
62
  if (typeof delta.content === "string") content += delta.content;
58
63
  const toolCalls = delta.tool_calls;
59
64
  if (toolCalls) for (const tc of toolCalls) {
@@ -70,14 +75,18 @@ function collapseOpenAISSE(body) {
70
75
  if (fn?.arguments && typeof fn.arguments === "string") entry.arguments += fn.arguments;
71
76
  }
72
77
  }
73
- if (toolCallMap.size > 0) return {
74
- toolCalls: Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
75
- name: tc.name,
76
- arguments: tc.arguments,
77
- ...tc.id ? { id: tc.id } : {}
78
- })),
79
- ...droppedChunks > 0 ? { droppedChunks } : {}
80
- };
78
+ if (toolCallMap.size > 0) {
79
+ const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
80
+ return {
81
+ ...content ? { content } : {},
82
+ toolCalls: sorted.map(([, tc]) => ({
83
+ name: tc.name,
84
+ arguments: tc.arguments,
85
+ ...tc.id ? { id: tc.id } : {}
86
+ })),
87
+ ...droppedChunks > 0 ? { droppedChunks } : {}
88
+ };
89
+ }
81
90
  return {
82
91
  content,
83
92
  ...reasoning ? { reasoning } : {},
@@ -133,15 +142,19 @@ function collapseAnthropicSSE(body) {
133
142
  }
134
143
  }
135
144
  }
136
- if (toolCallMap.size > 0) return {
137
- toolCalls: Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
138
- name: tc.name,
139
- arguments: tc.arguments,
140
- ...tc.id ? { id: tc.id } : {}
141
- })),
142
- ...reasoning ? { reasoning } : {},
143
- ...droppedChunks > 0 ? { droppedChunks } : {}
144
- };
145
+ if (toolCallMap.size > 0) {
146
+ const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
147
+ return {
148
+ ...content ? { content } : {},
149
+ toolCalls: sorted.map(([, tc]) => ({
150
+ name: tc.name,
151
+ arguments: tc.arguments,
152
+ ...tc.id ? { id: tc.id } : {}
153
+ })),
154
+ ...reasoning ? { reasoning } : {},
155
+ ...droppedChunks > 0 ? { droppedChunks } : {}
156
+ };
157
+ }
145
158
  return {
146
159
  content,
147
160
  ...reasoning ? { reasoning } : {},
@@ -158,6 +171,7 @@ function collapseGeminiSSE(body) {
158
171
  const lines = body.split("\n\n").filter((l) => l.trim().length > 0);
159
172
  let content = "";
160
173
  let droppedChunks = 0;
174
+ const toolCalls = [];
161
175
  for (const line of lines) {
162
176
  const dataLine = line.split("\n").find((l) => l.startsWith("data:"));
163
177
  if (!dataLine) continue;
@@ -175,26 +189,19 @@ function collapseGeminiSSE(body) {
175
189
  if (!candidateContent) continue;
176
190
  const parts = candidateContent.parts;
177
191
  if (!parts || parts.length === 0) continue;
178
- const fnCallParts = parts.filter((p) => p.functionCall);
179
- if (fnCallParts.length > 0) {
180
- const toolCallMap = /* @__PURE__ */ new Map();
181
- for (let i = 0; i < fnCallParts.length; i++) {
182
- const fc = fnCallParts[i].functionCall;
183
- toolCallMap.set(i, {
184
- name: String(fc.name ?? ""),
185
- arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args)
186
- });
187
- }
188
- if (toolCallMap.size > 0) return {
189
- toolCalls: Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
190
- name: tc.name,
191
- arguments: tc.arguments
192
- })),
193
- ...droppedChunks > 0 ? { droppedChunks } : {}
194
- };
195
- }
196
- if (typeof parts[0].text === "string") content += parts[0].text;
192
+ for (const part of parts) if (part.functionCall) {
193
+ const fc = part.functionCall;
194
+ toolCalls.push({
195
+ name: String(fc.name ?? ""),
196
+ arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args)
197
+ });
198
+ } else if (typeof part.text === "string") content += part.text;
197
199
  }
200
+ if (toolCalls.length > 0) return {
201
+ ...content ? { content } : {},
202
+ toolCalls,
203
+ ...droppedChunks > 0 ? { droppedChunks } : {}
204
+ };
198
205
  return {
199
206
  content,
200
207
  ...droppedChunks > 0 ? { droppedChunks } : {}
@@ -235,6 +242,7 @@ function collapseOllamaNDJSON(body) {
235
242
  } else if (typeof parsed.response === "string") content += parsed.response;
236
243
  }
237
244
  if (toolCalls.length > 0) return {
245
+ ...content ? { content } : {},
238
246
  toolCalls,
239
247
  ...droppedChunks > 0 ? { droppedChunks } : {}
240
248
  };
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.js","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing either `{ content }` or `{ toolCalls }`.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\n// TODO: Consider making this a discriminated union ({ type: \"text\"; content: string }\n// | { type: \"toolCalls\"; toolCalls: ToolCall[] } | { type: \"empty\" }) to prevent\n// ambiguous results and simplify downstream consumers.\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\" && typeof item.query === \"string\") {\n webSearchQueries.push(item.query);\n continue;\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n // Handle functionCall parts\n const fnCallParts = parts.filter((p) => p.functionCall);\n if (fnCallParts.length > 0) {\n const toolCallMap = new Map<number, { name: string; arguments: string }>();\n for (let i = 0; i < fnCallParts.length; i++) {\n const fc = fnCallParts[i].functionCall as Record<string, unknown>;\n toolCallMap.set(i, {\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n }\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n }\n\n if (typeof parts[0].text === \"string\") {\n content += parts[0].text;\n }\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return { toolCalls, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream-collapse.js","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\n// TODO: Consider making this a discriminated union ({ type: \"text\"; content: string }\n// | { type: \"toolCalls\"; toolCalls: ToolCall[] } | { type: \"empty\" }) to prevent\n// ambiguous results and simplify downstream consumers.\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n truncated?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const index = tc.index as number;\n const fn = tc.function as Record<string, unknown> | undefined;\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: (tc.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const index = parsed.index as number;\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n if (contentBlock?.type === \"tool_use\") {\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += delta.partial_json;\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = body.split(\"\\n\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n const dataLine = line.split(\"\\n\").find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n toolCalls.push({\n name: String(fc.name ?? \"\"),\n arguments: typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args),\n });\n } else if (typeof part.text === \"string\") {\n content += part.text;\n }\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n arguments:\n typeof fn.arguments === \"string\" ? fn.arguments : JSON.stringify(fn.arguments),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = body.split(\"\\n\\n\").filter((b) => b.trim().length > 0);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const dataLine = lines.find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = dataLine.slice(5).trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n const index = parsed.index as number;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n };\n }\n\n return { content, ...(droppedChunks > 0 ? { droppedChunks } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n\n while (hOffset < headersEnd) {\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n let droppedChunks = 0;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frame.payload.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n droppedChunks++;\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n if (index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) entry.arguments += delta.partial_json;\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\" && index !== undefined) {\n const entry = toolCallMap.get(index);\n if (entry) {\n entry.arguments += toolUseDelta.input;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
package/dist/types.d.cts CHANGED
@@ -87,6 +87,14 @@ interface ToolCallResponse {
87
87
  toolCalls: ToolCall[];
88
88
  finishReason?: string;
89
89
  }
90
+ interface ContentWithToolCallsResponse {
91
+ content: string;
92
+ toolCalls: ToolCall[];
93
+ reasoning?: string;
94
+ webSearches?: string[];
95
+ role?: string;
96
+ finishReason?: string;
97
+ }
90
98
  interface ErrorResponse {
91
99
  error: {
92
100
  message: string;
@@ -98,7 +106,7 @@ interface ErrorResponse {
98
106
  interface EmbeddingResponse {
99
107
  embedding: number[];
100
108
  }
101
- type FixtureResponse = TextResponse | ToolCallResponse | ErrorResponse | EmbeddingResponse;
109
+ type FixtureResponse = TextResponse | ToolCallResponse | ContentWithToolCallsResponse | ErrorResponse | EmbeddingResponse;
102
110
  interface StreamingProfile {
103
111
  ttft?: number;
104
112
  tps?: number;
@@ -174,6 +182,7 @@ interface SSEChoice {
174
182
  interface SSEDelta {
175
183
  role?: string;
176
184
  content?: string | null;
185
+ reasoning_content?: string;
177
186
  tool_calls?: SSEToolCallDelta[];
178
187
  }
179
188
  interface SSEToolCallDelta {
@@ -206,6 +215,7 @@ interface ChatCompletionMessage {
206
215
  role: "assistant";
207
216
  content: string | null;
208
217
  refusal: string | null;
218
+ reasoning_content?: string;
209
219
  tool_calls?: ToolCallMessage[];
210
220
  }
211
221
  type RecordProviderKey = "openai" | "anthropic" | "gemini" | "vertexai" | "bedrock" | "azure" | "ollama" | "cohere";
@@ -227,6 +237,16 @@ interface MockServerOptions {
227
237
  strict?: boolean;
228
238
  /** Record-and-replay: proxy unmatched requests to upstream and save fixtures. */
229
239
  record?: RecordConfig;
240
+ /**
241
+ * Normalize requests before matching and recording. Useful for stripping
242
+ * dynamic data (timestamps, UUIDs, session IDs) that would cause fixture
243
+ * mismatches on replay.
244
+ *
245
+ * When set, string matching for `userMessage` and `inputText` uses exact
246
+ * equality (`===`) instead of substring (`includes`) to prevent false
247
+ * positives from shortened keys.
248
+ */
249
+ requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
230
250
  }
231
251
  interface HandlerDefaults {
232
252
  latency: number;
@@ -236,6 +256,7 @@ interface HandlerDefaults {
236
256
  registry?: MetricsRegistry;
237
257
  record?: RecordConfig;
238
258
  strict?: boolean;
259
+ requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
239
260
  }
240
261
  //# sourceMappingURL=types.d.ts.map
241
262
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}