@copilotkit/aimock 1.16.3 → 1.17.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 (268) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README.md +10 -10
  5. package/dist/a2a-mock.d.cts +2 -2
  6. package/dist/a2a-mock.d.cts.map +1 -1
  7. package/dist/a2a-mock.d.ts +2 -2
  8. package/dist/a2a-mock.d.ts.map +1 -1
  9. package/dist/a2a-mock.js +2 -2
  10. package/dist/a2a-mock.js.map +1 -1
  11. package/dist/agui-handler.cjs +120 -5
  12. package/dist/agui-handler.cjs.map +1 -1
  13. package/dist/agui-handler.d.cts +41 -5
  14. package/dist/agui-handler.d.cts.map +1 -1
  15. package/dist/agui-handler.d.ts +41 -5
  16. package/dist/agui-handler.d.ts.map +1 -1
  17. package/dist/agui-handler.js +114 -6
  18. package/dist/agui-handler.js.map +1 -1
  19. package/dist/agui-mock.cjs +18 -7
  20. package/dist/agui-mock.cjs.map +1 -1
  21. package/dist/agui-mock.d.cts +2 -2
  22. package/dist/agui-mock.d.cts.map +1 -1
  23. package/dist/agui-mock.d.ts +2 -2
  24. package/dist/agui-mock.d.ts.map +1 -1
  25. package/dist/agui-mock.js +20 -9
  26. package/dist/agui-mock.js.map +1 -1
  27. package/dist/agui-recorder.cjs +43 -22
  28. package/dist/agui-recorder.cjs.map +1 -1
  29. package/dist/agui-recorder.d.cts +4 -3
  30. package/dist/agui-recorder.d.cts.map +1 -1
  31. package/dist/agui-recorder.d.ts +4 -3
  32. package/dist/agui-recorder.d.ts.map +1 -1
  33. package/dist/agui-recorder.js +45 -24
  34. package/dist/agui-recorder.js.map +1 -1
  35. package/dist/agui-stub.cjs +28 -0
  36. package/dist/agui-stub.d.cts +5 -0
  37. package/dist/agui-stub.d.ts +5 -0
  38. package/dist/agui-stub.js +5 -0
  39. package/dist/agui-types.d.cts +33 -6
  40. package/dist/agui-types.d.cts.map +1 -1
  41. package/dist/agui-types.d.ts +33 -6
  42. package/dist/agui-types.d.ts.map +1 -1
  43. package/dist/aimock-cli.cjs +1 -1
  44. package/dist/aimock-cli.js +1 -1
  45. package/dist/aws-event-stream.d.cts +2 -2
  46. package/dist/aws-event-stream.d.cts.map +1 -1
  47. package/dist/aws-event-stream.d.ts +2 -2
  48. package/dist/aws-event-stream.d.ts.map +1 -1
  49. package/dist/bedrock-converse.d.cts +3 -3
  50. package/dist/bedrock-converse.d.cts.map +1 -1
  51. package/dist/bedrock-converse.d.ts +3 -3
  52. package/dist/bedrock-converse.d.ts.map +1 -1
  53. package/dist/bedrock.d.cts +3 -3
  54. package/dist/bedrock.d.cts.map +1 -1
  55. package/dist/bedrock.d.ts +3 -3
  56. package/dist/bedrock.d.ts.map +1 -1
  57. package/dist/chaos.d.cts +3 -3
  58. package/dist/chaos.d.cts.map +1 -1
  59. package/dist/chaos.d.ts +3 -3
  60. package/dist/chaos.d.ts.map +1 -1
  61. package/dist/cli.cjs +6 -5
  62. package/dist/cli.cjs.map +1 -1
  63. package/dist/cli.js +6 -5
  64. package/dist/cli.js.map +1 -1
  65. package/dist/cohere.d.cts +2 -2
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts +2 -2
  68. package/dist/cohere.d.ts.map +1 -1
  69. package/dist/config-loader.cjs +3 -3
  70. package/dist/config-loader.d.cts +1 -1
  71. package/dist/config-loader.d.cts.map +1 -1
  72. package/dist/config-loader.d.ts +1 -1
  73. package/dist/config-loader.js +2 -2
  74. package/dist/convert-vidaimock.cjs +1 -1
  75. package/dist/convert-vidaimock.js +1 -1
  76. package/dist/convert.cjs +1 -1
  77. package/dist/convert.js +1 -1
  78. package/dist/elevenlabs-audio.cjs +209 -0
  79. package/dist/elevenlabs-audio.cjs.map +1 -0
  80. package/dist/elevenlabs-audio.d.cts +11 -0
  81. package/dist/elevenlabs-audio.d.cts.map +1 -0
  82. package/dist/elevenlabs-audio.d.ts +11 -0
  83. package/dist/elevenlabs-audio.d.ts.map +1 -0
  84. package/dist/elevenlabs-audio.js +209 -0
  85. package/dist/elevenlabs-audio.js.map +1 -0
  86. package/dist/embeddings.d.cts +2 -2
  87. package/dist/embeddings.d.cts.map +1 -1
  88. package/dist/embeddings.d.ts +2 -2
  89. package/dist/embeddings.d.ts.map +1 -1
  90. package/dist/fal-audio.cjs +477 -0
  91. package/dist/fal-audio.cjs.map +1 -0
  92. package/dist/fal-audio.d.cts +10 -0
  93. package/dist/fal-audio.d.cts.map +1 -0
  94. package/dist/fal-audio.d.ts +10 -0
  95. package/dist/fal-audio.d.ts.map +1 -0
  96. package/dist/fal-audio.js +474 -0
  97. package/dist/fal-audio.js.map +1 -0
  98. package/dist/fixture-loader.cjs +14 -1
  99. package/dist/fixture-loader.cjs.map +1 -1
  100. package/dist/fixture-loader.js +14 -1
  101. package/dist/fixture-loader.js.map +1 -1
  102. package/dist/fixtures-remote.cjs +1 -1
  103. package/dist/fixtures-remote.js +1 -1
  104. package/dist/gemini-interactions.cjs +617 -0
  105. package/dist/gemini-interactions.cjs.map +1 -0
  106. package/dist/gemini-interactions.d.cts +46 -0
  107. package/dist/gemini-interactions.d.cts.map +1 -0
  108. package/dist/gemini-interactions.d.ts +46 -0
  109. package/dist/gemini-interactions.d.ts.map +1 -0
  110. package/dist/gemini-interactions.js +616 -0
  111. package/dist/gemini-interactions.js.map +1 -0
  112. package/dist/gemini.cjs +76 -0
  113. package/dist/gemini.cjs.map +1 -1
  114. package/dist/gemini.d.cts +2 -2
  115. package/dist/gemini.d.cts.map +1 -1
  116. package/dist/gemini.d.ts +2 -2
  117. package/dist/gemini.d.ts.map +1 -1
  118. package/dist/gemini.js +77 -1
  119. package/dist/gemini.js.map +1 -1
  120. package/dist/helpers.cjs +24 -1
  121. package/dist/helpers.cjs.map +1 -1
  122. package/dist/helpers.d.cts +13 -3
  123. package/dist/helpers.d.cts.map +1 -1
  124. package/dist/helpers.d.ts +13 -3
  125. package/dist/helpers.d.ts.map +1 -1
  126. package/dist/helpers.js +23 -2
  127. package/dist/helpers.js.map +1 -1
  128. package/dist/images.d.cts +2 -2
  129. package/dist/images.d.cts.map +1 -1
  130. package/dist/images.d.ts +2 -2
  131. package/dist/images.d.ts.map +1 -1
  132. package/dist/index.cjs +21 -4
  133. package/dist/index.d.cts +10 -7
  134. package/dist/index.d.ts +10 -7
  135. package/dist/index.js +10 -7
  136. package/dist/jest.cjs +1 -1
  137. package/dist/jest.js +1 -1
  138. package/dist/jsonrpc.d.cts +3 -3
  139. package/dist/jsonrpc.d.cts.map +1 -1
  140. package/dist/jsonrpc.d.ts +3 -3
  141. package/dist/jsonrpc.d.ts.map +1 -1
  142. package/dist/llmock.cjs +38 -2
  143. package/dist/llmock.cjs.map +1 -1
  144. package/dist/llmock.d.cts +4 -0
  145. package/dist/llmock.d.cts.map +1 -1
  146. package/dist/llmock.d.ts +4 -0
  147. package/dist/llmock.d.ts.map +1 -1
  148. package/dist/llmock.js +38 -2
  149. package/dist/llmock.js.map +1 -1
  150. package/dist/logger.cjs +5 -4
  151. package/dist/logger.cjs.map +1 -1
  152. package/dist/logger.d.cts +1 -1
  153. package/dist/logger.d.cts.map +1 -1
  154. package/dist/logger.d.ts +1 -1
  155. package/dist/logger.d.ts.map +1 -1
  156. package/dist/logger.js +5 -4
  157. package/dist/logger.js.map +1 -1
  158. package/dist/mcp-mock.d.cts +2 -2
  159. package/dist/mcp-mock.d.cts.map +1 -1
  160. package/dist/mcp-mock.d.ts +2 -2
  161. package/dist/mcp-mock.d.ts.map +1 -1
  162. package/dist/mcp-mock.js +2 -2
  163. package/dist/mcp-mock.js.map +1 -1
  164. package/dist/messages.d.cts +2 -2
  165. package/dist/messages.d.cts.map +1 -1
  166. package/dist/messages.d.ts +2 -2
  167. package/dist/messages.d.ts.map +1 -1
  168. package/dist/moderation.d.cts +3 -3
  169. package/dist/moderation.d.cts.map +1 -1
  170. package/dist/moderation.d.ts +3 -3
  171. package/dist/moderation.d.ts.map +1 -1
  172. package/dist/ndjson-writer.d.cts +2 -2
  173. package/dist/ndjson-writer.d.cts.map +1 -1
  174. package/dist/ndjson-writer.d.ts +2 -2
  175. package/dist/ndjson-writer.d.ts.map +1 -1
  176. package/dist/ollama.d.cts +3 -3
  177. package/dist/ollama.d.cts.map +1 -1
  178. package/dist/ollama.d.ts +3 -3
  179. package/dist/ollama.d.ts.map +1 -1
  180. package/dist/recorder.cjs +64 -21
  181. package/dist/recorder.cjs.map +1 -1
  182. package/dist/recorder.d.cts +2 -2
  183. package/dist/recorder.d.cts.map +1 -1
  184. package/dist/recorder.d.ts +2 -2
  185. package/dist/recorder.d.ts.map +1 -1
  186. package/dist/recorder.js +66 -23
  187. package/dist/recorder.js.map +1 -1
  188. package/dist/rerank.d.cts +3 -3
  189. package/dist/rerank.d.cts.map +1 -1
  190. package/dist/rerank.d.ts +3 -3
  191. package/dist/rerank.d.ts.map +1 -1
  192. package/dist/responses.d.cts +2 -2
  193. package/dist/responses.d.cts.map +1 -1
  194. package/dist/responses.d.ts +2 -2
  195. package/dist/responses.d.ts.map +1 -1
  196. package/dist/router.cjs +3 -3
  197. package/dist/router.cjs.map +1 -1
  198. package/dist/router.js +3 -3
  199. package/dist/router.js.map +1 -1
  200. package/dist/search.d.cts +3 -3
  201. package/dist/search.d.cts.map +1 -1
  202. package/dist/search.d.ts +3 -3
  203. package/dist/search.d.ts.map +1 -1
  204. package/dist/server.cjs +170 -1
  205. package/dist/server.cjs.map +1 -1
  206. package/dist/server.d.cts +2 -2
  207. package/dist/server.d.cts.map +1 -1
  208. package/dist/server.d.ts +2 -2
  209. package/dist/server.d.ts.map +1 -1
  210. package/dist/server.js +173 -4
  211. package/dist/server.js.map +1 -1
  212. package/dist/speech.cjs +18 -9
  213. package/dist/speech.cjs.map +1 -1
  214. package/dist/speech.d.cts +2 -2
  215. package/dist/speech.d.cts.map +1 -1
  216. package/dist/speech.d.ts +2 -2
  217. package/dist/speech.d.ts.map +1 -1
  218. package/dist/speech.js +18 -9
  219. package/dist/speech.js.map +1 -1
  220. package/dist/sse-writer.d.cts +3 -3
  221. package/dist/sse-writer.d.cts.map +1 -1
  222. package/dist/sse-writer.d.ts +3 -3
  223. package/dist/sse-writer.d.ts.map +1 -1
  224. package/dist/stream-collapse.cjs +80 -9
  225. package/dist/stream-collapse.cjs.map +1 -1
  226. package/dist/stream-collapse.d.cts +11 -1
  227. package/dist/stream-collapse.d.cts.map +1 -1
  228. package/dist/stream-collapse.d.ts +11 -1
  229. package/dist/stream-collapse.d.ts.map +1 -1
  230. package/dist/stream-collapse.js +80 -10
  231. package/dist/stream-collapse.js.map +1 -1
  232. package/dist/suite.cjs +1 -1
  233. package/dist/suite.d.cts +2 -2
  234. package/dist/suite.d.ts +2 -2
  235. package/dist/suite.js +1 -1
  236. package/dist/transcription.d.cts +2 -2
  237. package/dist/transcription.d.cts.map +1 -1
  238. package/dist/transcription.d.ts +2 -2
  239. package/dist/transcription.d.ts.map +1 -1
  240. package/dist/types.d.cts +10 -7
  241. package/dist/types.d.cts.map +1 -1
  242. package/dist/types.d.ts +10 -7
  243. package/dist/types.d.ts.map +1 -1
  244. package/dist/vector-mock.d.cts +2 -2
  245. package/dist/vector-mock.d.cts.map +1 -1
  246. package/dist/vector-mock.d.ts +2 -2
  247. package/dist/vector-mock.d.ts.map +1 -1
  248. package/dist/vector-mock.js +2 -2
  249. package/dist/vector-mock.js.map +1 -1
  250. package/dist/vector-types.d.ts.map +1 -1
  251. package/dist/video.d.cts +3 -3
  252. package/dist/video.d.cts.map +1 -1
  253. package/dist/video.d.ts +3 -3
  254. package/dist/video.d.ts.map +1 -1
  255. package/dist/vitest.cjs +1 -1
  256. package/dist/vitest.js +1 -1
  257. package/dist/ws-framing.d.cts +2 -2
  258. package/dist/ws-framing.d.cts.map +1 -1
  259. package/dist/ws-framing.d.ts +2 -2
  260. package/dist/ws-framing.d.ts.map +1 -1
  261. package/dist/ws-gemini-live.cjs +145 -2
  262. package/dist/ws-gemini-live.cjs.map +1 -1
  263. package/dist/ws-gemini-live.d.cts.map +1 -1
  264. package/dist/ws-gemini-live.d.ts.map +1 -1
  265. package/dist/ws-gemini-live.js +146 -3
  266. package/dist/ws-gemini-live.js.map +1 -1
  267. package/package.json +16 -2
  268. package/skills/write-fixtures/SKILL.md +10 -10
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","names":[],"sources":["../src/router.ts"],"sourcesContent":["import type { ChatCompletionRequest, ChatMessage, ContentPart, Fixture } from \"./types.js\";\nimport {\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n} from \"./helpers.js\";\n\nexport function getLastMessageByRole(messages: ChatMessage[], role: string): ChatMessage | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === role) return messages[i];\n }\n return null;\n}\n\n/**\n * Extract the text content from a message's content field.\n * Handles both plain string content and array-of-parts content\n * (e.g. `[{type: \"text\", text: \"...\"}]` as sent by some SDKs).\n */\nexport function getTextContent(content: string | ContentPart[] | null): string | null {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const texts = content\n .filter((p) => p.type === \"text\" && typeof p.text === \"string\" && p.text !== \"\")\n .map((p) => p.text as string);\n return texts.length > 0 ? texts.join(\"\") : null;\n }\n return null;\n}\n\nexport function matchFixture(\n fixtures: Fixture[],\n req: ChatCompletionRequest,\n matchCounts?: Map<Fixture, number>,\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest,\n): Fixture | null {\n // Apply transform once before matching — used for stripping dynamic data\n const effective = requestTransform ? requestTransform(req) : req;\n const useExactMatch = !!requestTransform;\n\n for (const fixture of fixtures) {\n const { match } = fixture;\n\n // predicate — if present, must return true (receives original request)\n if (match.predicate !== undefined) {\n if (!match.predicate(req)) continue;\n }\n\n // endpoint — bidirectional filtering:\n // 1. If fixture has endpoint set, only match requests of that type\n // 2. If request has _endpointType but fixture doesn't, skip fixtures\n // whose response type is incompatible (prevents generic chat fixtures\n // from matching image/speech/video requests and causing 500s)\n const reqEndpoint = effective._endpointType as string | undefined;\n if (match.endpoint !== undefined) {\n if (match.endpoint !== reqEndpoint) continue;\n } else if (reqEndpoint && reqEndpoint !== \"chat\" && reqEndpoint !== \"embedding\") {\n // Fixture has no endpoint restriction but request is multimedia —\n // only match if the response type is compatible\n const r = fixture.response;\n const compatible =\n (reqEndpoint === \"image\" && isImageResponse(r)) ||\n (reqEndpoint === \"speech\" && isAudioResponse(r)) ||\n (reqEndpoint === \"transcription\" && isTranscriptionResponse(r)) ||\n (reqEndpoint === \"video\" && isVideoResponse(r));\n if (!compatible) continue;\n }\n\n // userMessage — case-sensitive match against the last user message content.\n // String matching is intentionally case-sensitive so fixture authors can\n // rely on exact string values. This differs from the case-insensitive\n // matchesPattern() in helpers.ts, which is used for search/rerank/moderation\n // where exact casing rarely matters.\n if (match.userMessage !== undefined) {\n const msg = getLastMessageByRole(effective.messages, \"user\");\n const text = msg ? getTextContent(msg.content) : null;\n if (!text) continue;\n if (typeof match.userMessage === \"string\") {\n if (useExactMatch) {\n if (text !== match.userMessage) continue;\n } else {\n if (!text.includes(match.userMessage)) continue;\n }\n } else {\n match.userMessage.lastIndex = 0;\n if (!match.userMessage.test(text)) continue;\n }\n }\n\n // toolCallId — match against the last tool message's tool_call_id\n if (match.toolCallId !== undefined) {\n const msg = getLastMessageByRole(effective.messages, \"tool\");\n if (!msg || msg.tool_call_id !== match.toolCallId) continue;\n }\n\n // toolName — match against any tool definition by function.name\n if (match.toolName !== undefined) {\n const tools = effective.tools ?? [];\n const found = tools.some((t) => t.function.name === match.toolName);\n if (!found) continue;\n }\n\n // inputText — case-sensitive match against the embedding input text.\n // Same rationale as userMessage above: fixture authors specify exact strings.\n if (match.inputText !== undefined) {\n const embeddingInput = effective.embeddingInput;\n if (!embeddingInput) continue;\n if (typeof match.inputText === \"string\") {\n if (useExactMatch) {\n if (embeddingInput !== match.inputText) continue;\n } else {\n if (!embeddingInput.includes(match.inputText)) continue;\n }\n } else {\n match.inputText.lastIndex = 0;\n if (!match.inputText.test(embeddingInput)) continue;\n }\n }\n\n // responseFormat — exact string match against request response_format.type\n if (match.responseFormat !== undefined) {\n const reqType = effective.response_format?.type;\n if (reqType !== match.responseFormat) continue;\n }\n\n // model — exact string or regexp\n if (match.model !== undefined) {\n if (typeof match.model === \"string\") {\n if (effective.model !== match.model) continue;\n } else {\n match.model.lastIndex = 0;\n if (!match.model.test(effective.model)) continue;\n }\n }\n\n // sequenceIndex — check against the fixture's match count\n if (match.sequenceIndex !== undefined && matchCounts !== undefined) {\n const count = matchCounts.get(fixture) ?? 0;\n if (count !== match.sequenceIndex) continue;\n }\n\n if (match.turnIndex !== undefined) {\n const assistantCount = effective.messages.filter((m) => m.role === \"assistant\").length;\n if (assistantCount !== match.turnIndex) continue;\n }\n\n if (match.hasToolResult !== undefined) {\n const hasTool = effective.messages.some((m) => m.role === \"tool\");\n if (hasTool !== match.hasToolResult) continue;\n }\n\n return fixture;\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"router.js","names":[],"sources":["../src/router.ts"],"sourcesContent":["import type { ChatCompletionRequest, ChatMessage, ContentPart, Fixture } from \"./types.js\";\nimport {\n isImageResponse,\n isAudioResponse,\n isTranscriptionResponse,\n isVideoResponse,\n} from \"./helpers.js\";\n\nexport function getLastMessageByRole(messages: ChatMessage[], role: string): ChatMessage | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === role) return messages[i];\n }\n return null;\n}\n\n/**\n * Extract the text content from a message's content field.\n * Handles both plain string content and array-of-parts content\n * (e.g. `[{type: \"text\", text: \"...\"}]` as sent by some SDKs).\n */\nexport function getTextContent(content: string | ContentPart[] | null): string | null {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n const texts = content\n .filter((p) => p.type === \"text\" && typeof p.text === \"string\" && p.text !== \"\")\n .map((p) => p.text as string);\n return texts.length > 0 ? texts.join(\"\") : null;\n }\n return null;\n}\n\nexport function matchFixture(\n fixtures: Fixture[],\n req: ChatCompletionRequest,\n matchCounts?: Map<Fixture, number>,\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest,\n): Fixture | null {\n // Apply transform once before matching — used for stripping dynamic data\n const effective = requestTransform ? requestTransform(req) : req;\n const useExactMatch = !!requestTransform;\n\n for (const fixture of fixtures) {\n const { match } = fixture;\n\n // predicate — if present, must return true (receives original request)\n if (match.predicate !== undefined) {\n if (!match.predicate(req)) continue;\n }\n\n // endpoint — bidirectional filtering:\n // 1. If fixture has endpoint set, only match requests of that type\n // 2. If request has _endpointType but fixture doesn't, skip fixtures\n // whose response type is incompatible (prevents generic chat fixtures\n // from matching image/speech/video requests and causing 500s)\n const reqEndpoint = effective._endpointType as string | undefined;\n if (match.endpoint !== undefined) {\n if (match.endpoint !== reqEndpoint) continue;\n } else if (reqEndpoint && reqEndpoint !== \"chat\" && reqEndpoint !== \"embedding\") {\n // Fixture has no endpoint restriction but request is multimedia —\n // only match if the response type is compatible\n const r = fixture.response;\n const compatible =\n (reqEndpoint === \"image\" && isImageResponse(r)) ||\n (reqEndpoint === \"speech\" && isAudioResponse(r)) ||\n (reqEndpoint === \"audio-gen\" && isAudioResponse(r)) ||\n (reqEndpoint === \"fal-audio\" && isAudioResponse(r)) ||\n (reqEndpoint === \"transcription\" && isTranscriptionResponse(r)) ||\n (reqEndpoint === \"video\" && isVideoResponse(r));\n if (!compatible) continue;\n }\n\n // userMessage — case-sensitive match against the last user message content.\n // String matching is intentionally case-sensitive so fixture authors can\n // rely on exact string values. This differs from the case-insensitive\n // matchesPattern() in helpers.ts, which is used for search/rerank/moderation\n // where exact casing rarely matters.\n if (match.userMessage !== undefined) {\n const msg = getLastMessageByRole(effective.messages, \"user\");\n const text = msg ? getTextContent(msg.content) : null;\n if (!text) continue;\n if (typeof match.userMessage === \"string\") {\n if (useExactMatch) {\n if (text !== match.userMessage) continue;\n } else {\n if (!text.includes(match.userMessage)) continue;\n }\n } else {\n match.userMessage.lastIndex = 0;\n if (!match.userMessage.test(text)) continue;\n }\n }\n\n // toolCallId — a toolCallId fixture answers the model's response to a tool\n // result, which by API contract only happens when the conversation's LAST\n // message is a tool result. If a newer user (or other) turn follows the\n // tool message, the stale tool_call_id must not shadow userMessage matchers.\n if (match.toolCallId !== undefined) {\n const last = effective.messages[effective.messages.length - 1];\n if (!last || last.role !== \"tool\" || last.tool_call_id !== match.toolCallId) continue;\n }\n\n // toolName — match against any tool definition by function.name\n if (match.toolName !== undefined) {\n const tools = effective.tools ?? [];\n const found = tools.some((t) => t.function.name === match.toolName);\n if (!found) continue;\n }\n\n // inputText — case-sensitive match against the embedding input text.\n // Same rationale as userMessage above: fixture authors specify exact strings.\n if (match.inputText !== undefined) {\n const embeddingInput = effective.embeddingInput;\n if (!embeddingInput) continue;\n if (typeof match.inputText === \"string\") {\n if (useExactMatch) {\n if (embeddingInput !== match.inputText) continue;\n } else {\n if (!embeddingInput.includes(match.inputText)) continue;\n }\n } else {\n match.inputText.lastIndex = 0;\n if (!match.inputText.test(embeddingInput)) continue;\n }\n }\n\n // responseFormat — exact string match against request response_format.type\n if (match.responseFormat !== undefined) {\n const reqType = effective.response_format?.type;\n if (reqType !== match.responseFormat) continue;\n }\n\n // model — exact string or regexp\n if (match.model !== undefined) {\n if (typeof match.model === \"string\") {\n if (effective.model !== match.model) continue;\n } else {\n match.model.lastIndex = 0;\n if (!match.model.test(effective.model)) continue;\n }\n }\n\n // sequenceIndex — check against the fixture's match count\n if (match.sequenceIndex !== undefined && matchCounts !== undefined) {\n const count = matchCounts.get(fixture) ?? 0;\n if (count !== match.sequenceIndex) continue;\n }\n\n if (match.turnIndex !== undefined) {\n const assistantCount = effective.messages.filter((m) => m.role === \"assistant\").length;\n if (assistantCount !== match.turnIndex) continue;\n }\n\n if (match.hasToolResult !== undefined) {\n const hasTool = effective.messages.some((m) => m.role === \"tool\");\n if (hasTool !== match.hasToolResult) continue;\n }\n\n return fixture;\n }\n\n return null;\n}\n"],"mappings":"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"}
package/dist/search.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Journal } from "./journal.cjs";
2
2
  import { Logger } from "./logger.cjs";
3
- import * as http from "node:http";
3
+ import * as http$1 from "node:http";
4
4
 
5
5
  //#region src/search.d.ts
6
6
 
@@ -14,9 +14,9 @@ interface SearchFixture {
14
14
  match: string | RegExp;
15
15
  results: SearchResult[];
16
16
  }
17
- declare function handleSearch(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: SearchFixture[], journal: Journal, defaults: {
17
+ declare function handleSearch(req: http$1.IncomingMessage, res: http$1.ServerResponse, raw: string, fixtures: SearchFixture[], journal: Journal, defaults: {
18
18
  logger: Logger;
19
- }, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
19
+ }, setCorsHeaders: (res: http$1.ServerResponse) => void): Promise<void>;
20
20
  //# sourceMappingURL=search.d.ts.map
21
21
  //#endregion
22
22
  export { SearchFixture, SearchResult, handleSearch };
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.cts","names":[],"sources":["../src/search.ts"],"sourcesContent":[],"mappings":";;;;;;AAwBuB,UATN,YAAA,CASM;EAKD,KAAA,EAAA,MAAA;EAAY,GAAA,EAAA,MAAA;SAC3B,EAAK,MAAA;OACL,CAAA,EAAK,MAAA;;AAGD,UAZM,aAAA,CAYN;OACW,EAAA,MAAA,GAZJ,MAYI;SACE,EAZb,YAYkB,EAAA;;AACnB,iBARY,YAAA,CAQZ,GAAA,EAPH,IAAA,CAAK,eAOF,EAAA,GAAA,EANH,IAAA,CAAK,cAMF,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAJE,aAIF,EAAA,EAAA,OAAA,EAHC,OAGD,EAAA,QAAA,EAAA;UAFY;yBACE,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"search.d.cts","names":[],"sources":["../src/search.ts"],"sourcesContent":[],"mappings":";;;;;;AAwBuB,UATN,YAAA,CASM;EAKD,KAAA,EAAA,MAAA;EAAY,GAAA,EAAA,MAAA;SAC3B,EAAA,MAAK;OACL,CAAA,EAAA,MAAK;;AAGD,UAZM,aAAA,CAYN;OACW,EAAA,MAAA,GAZJ,MAYI;SACE,EAZb,YAYkB,EAAA;;AACnB,iBARY,YAAA,CAQZ,GAAA,EAPH,MAAA,CAAK,eAOF,EAAA,GAAA,EANH,MAAA,CAAK,cAMF,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAJE,aAIF,EAAA,EAAA,OAAA,EAHC,OAGD,EAAA,QAAA,EAAA;UAFY;yBACE,MAAA,CAAK,0BAC1B"}
package/dist/search.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Journal } from "./journal.js";
2
2
  import { Logger } from "./logger.js";
3
- import * as http from "node:http";
3
+ import * as http$1 from "node:http";
4
4
 
5
5
  //#region src/search.d.ts
6
6
 
@@ -14,9 +14,9 @@ interface SearchFixture {
14
14
  match: string | RegExp;
15
15
  results: SearchResult[];
16
16
  }
17
- declare function handleSearch(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: SearchFixture[], journal: Journal, defaults: {
17
+ declare function handleSearch(req: http$1.IncomingMessage, res: http$1.ServerResponse, raw: string, fixtures: SearchFixture[], journal: Journal, defaults: {
18
18
  logger: Logger;
19
- }, setCorsHeaders: (res: http.ServerResponse) => void): Promise<void>;
19
+ }, setCorsHeaders: (res: http$1.ServerResponse) => void): Promise<void>;
20
20
  //# sourceMappingURL=search.d.ts.map
21
21
  //#endregion
22
22
  export { SearchFixture, SearchResult, handleSearch };
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","names":[],"sources":["../src/search.ts"],"sourcesContent":[],"mappings":";;;;;;AAwBuB,UATN,YAAA,CASM;EAKD,KAAA,EAAA,MAAA;EAAY,GAAA,EAAA,MAAA;SAC3B,EAAK,MAAA;OACL,CAAA,EAAK,MAAA;;AAGD,UAZM,aAAA,CAYN;OACW,EAAA,MAAA,GAZJ,MAYI;SACE,EAZb,YAYkB,EAAA;;AACnB,iBARY,YAAA,CAQZ,GAAA,EAPH,IAAA,CAAK,eAOF,EAAA,GAAA,EANH,IAAA,CAAK,cAMF,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAJE,aAIF,EAAA,EAAA,OAAA,EAHC,OAGD,EAAA,QAAA,EAAA;UAFY;yBACE,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"search.d.ts","names":[],"sources":["../src/search.ts"],"sourcesContent":[],"mappings":";;;;;;AAwBuB,UATN,YAAA,CASM;EAKD,KAAA,EAAA,MAAA;EAAY,GAAA,EAAA,MAAA;SAC3B,EAAA,MAAK;OACL,CAAA,EAAA,MAAK;;AAGD,UAZM,aAAA,CAYN;OACW,EAAA,MAAA,GAZJ,MAYI;SACE,EAZb,YAYkB,EAAA;;AACnB,iBARY,YAAA,CAQZ,GAAA,EAPH,MAAA,CAAK,eAOF,EAAA,GAAA,EANH,MAAA,CAAK,cAMF,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAJE,aAIF,EAAA,EAAA,OAAA,EAHC,OAGD,EAAA,QAAA,EAAA;UAFY;yBACE,MAAA,CAAK,0BAC1B"}
package/dist/server.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
2
  const require_helpers = require('./helpers.cjs');
3
+ const require_logger = require('./logger.cjs');
3
4
  const require_journal = require('./journal.cjs');
4
5
  const require_router = require('./router.cjs');
5
6
  const require_fixture_loader = require('./fixture-loader.cjs');
@@ -12,11 +13,14 @@ const require_messages = require('./messages.cjs');
12
13
  const require_gemini = require('./gemini.cjs');
13
14
  const require_bedrock = require('./bedrock.cjs');
14
15
  const require_bedrock_converse = require('./bedrock-converse.cjs');
16
+ const require_gemini_interactions = require('./gemini-interactions.cjs');
15
17
  const require_embeddings = require('./embeddings.cjs');
16
18
  const require_images = require('./images.cjs');
17
19
  const require_speech = require('./speech.cjs');
18
20
  const require_transcription = require('./transcription.cjs');
19
21
  const require_video = require('./video.cjs');
22
+ const require_elevenlabs_audio = require('./elevenlabs-audio.cjs');
23
+ const require_fal_audio = require('./fal-audio.cjs');
20
24
  const require_ollama = require('./ollama.cjs');
21
25
  const require_cohere = require('./cohere.cjs');
22
26
  const require_search = require('./search.cjs');
@@ -26,7 +30,6 @@ const require_ws_framing = require('./ws-framing.cjs');
26
30
  const require_ws_responses = require('./ws-responses.cjs');
27
31
  const require_ws_realtime = require('./ws-realtime.cjs');
28
32
  const require_ws_gemini_live = require('./ws-gemini-live.cjs');
29
- const require_logger = require('./logger.cjs');
30
33
  const require_metrics = require('./metrics.cjs');
31
34
  let node_http = require("node:http");
32
35
  node_http = require_runtime.__toESM(node_http);
@@ -48,6 +51,11 @@ const TRANSCRIPTIONS_PATH = "/v1/audio/transcriptions";
48
51
  const VIDEOS_PATH = "/v1/videos";
49
52
  const VIDEOS_STATUS_RE = /^\/v1\/videos\/([^/]+)$/;
50
53
  const GEMINI_PREDICT_RE = /^\/v1beta\/models\/([^:]+):predict$/;
54
+ const ELEVENLABS_SOUND_GENERATION_PATH = "/v1/sound-generation";
55
+ const ELEVENLABS_MUSIC_RE = /^\/v1\/music(?:\/(.+))?$/;
56
+ const FAL_QUEUE_SUBMIT_RE = /^\/fal\/queue\/submit\/(.+)$/;
57
+ const FAL_QUEUE_REQUESTS_RE = /^\/fal\/queue\/requests\/(.+)$/;
58
+ const FAL_RUN_RE = /^\/fal\/run\/(.+)$/;
51
59
  const DEFAULT_CHUNK_SIZE = 20;
52
60
  const COMPAT_SUFFIXES = [
53
61
  "/chat/completions",
@@ -73,6 +81,7 @@ function normalizeCompatPath(pathname, logger) {
73
81
  }
74
82
  return pathname;
75
83
  }
84
+ const GEMINI_INTERACTIONS_PATH = "/v1beta/interactions";
76
85
  const GEMINI_PATH_RE = /^\/v1beta\/models\/([^:]+):(generateContent|streamGenerateContent)$/;
77
86
  const AZURE_DEPLOYMENT_RE = /^\/openai\/deployments\/([^/]+)\/(chat\/completions|embeddings)$/;
78
87
  const BEDROCK_INVOKE_RE = /^\/model\/([^/]+)\/invoke$/;
@@ -201,6 +210,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
201
210
  fixtures.length = 0;
202
211
  journal.clear();
203
212
  videoStates.clear();
213
+ require_fal_audio.falJobs.clear();
204
214
  if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
205
215
  res.writeHead(200, { "Content-Type": "application/json" });
206
216
  res.end(JSON.stringify({ reset: true }));
@@ -394,6 +404,23 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
394
404
  require_sse_writer.writeErrorResponse(res, status, JSON.stringify(response));
395
405
  return;
396
406
  }
407
+ if (require_helpers.isAudioResponse(response)) {
408
+ journal.add({
409
+ method: req.method ?? "POST",
410
+ path: req.url ?? COMPLETIONS_PATH,
411
+ headers: require_helpers.flattenHeaders(req.headers),
412
+ body,
413
+ response: {
414
+ status: 422,
415
+ fixture
416
+ }
417
+ });
418
+ require_sse_writer.writeErrorResponse(res, 422, JSON.stringify({ error: {
419
+ message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
420
+ type: "invalid_request_error"
421
+ } }));
422
+ return;
423
+ }
397
424
  if (require_helpers.isContentWithToolCallsResponse(response)) {
398
425
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
399
426
  const overrides = require_helpers.extractOverrides(response);
@@ -859,6 +886,26 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
859
886
  }
860
887
  return;
861
888
  }
889
+ if (pathname === GEMINI_INTERACTIONS_PATH && req.method === "POST") {
890
+ try {
891
+ await require_gemini_interactions.handleGeminiInteractions(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
892
+ } catch (err) {
893
+ const msg = err instanceof Error ? err.message : "Internal error";
894
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
895
+ message: msg,
896
+ type: "server_error"
897
+ } }));
898
+ else if (!res.writableEnded) {
899
+ try {
900
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
901
+ } catch (writeErr) {
902
+ logger.debug("Failed to write error recovery response:", writeErr);
903
+ }
904
+ res.end();
905
+ }
906
+ }
907
+ return;
908
+ }
862
909
  const geminiMatch = pathname.match(GEMINI_PATH_RE);
863
910
  if (geminiMatch && req.method === "POST") {
864
911
  const geminiModel = geminiMatch[1];
@@ -1046,6 +1093,128 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1046
1093
  }
1047
1094
  return;
1048
1095
  }
1096
+ if (pathname === ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
1097
+ setCorsHeaders(res);
1098
+ try {
1099
+ const raw = await readBody(req);
1100
+ if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
1101
+ method: req.method ?? "POST",
1102
+ path: pathname,
1103
+ headers: require_helpers.flattenHeaders(req.headers),
1104
+ body: {
1105
+ model: "",
1106
+ messages: []
1107
+ }
1108
+ }, defaults.registry, defaults.logger)) return;
1109
+ await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, "sound-generation");
1110
+ } catch (err) {
1111
+ const msg = err instanceof Error ? err.message : "Internal error";
1112
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1113
+ message: msg,
1114
+ type: "server_error"
1115
+ } }));
1116
+ else if (!res.writableEnded) res.destroy();
1117
+ }
1118
+ return;
1119
+ }
1120
+ const musicMatch = pathname.match(ELEVENLABS_MUSIC_RE);
1121
+ if (musicMatch && req.method === "POST") {
1122
+ setCorsHeaders(res);
1123
+ const musicSubType = musicMatch[1] ?? "music";
1124
+ try {
1125
+ const raw = await readBody(req);
1126
+ if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
1127
+ method: req.method ?? "POST",
1128
+ path: pathname,
1129
+ headers: require_helpers.flattenHeaders(req.headers),
1130
+ body: {
1131
+ model: "",
1132
+ messages: []
1133
+ }
1134
+ }, defaults.registry, defaults.logger)) return;
1135
+ await require_elevenlabs_audio.handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, musicSubType);
1136
+ } catch (err) {
1137
+ const msg = err instanceof Error ? err.message : "Internal error";
1138
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1139
+ message: msg,
1140
+ type: "server_error"
1141
+ } }));
1142
+ else if (!res.writableEnded) res.destroy();
1143
+ }
1144
+ return;
1145
+ }
1146
+ if (pathname.match(FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
1147
+ setCorsHeaders(res);
1148
+ try {
1149
+ const raw = await readBody(req);
1150
+ if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
1151
+ method: req.method ?? "POST",
1152
+ path: pathname,
1153
+ headers: require_helpers.flattenHeaders(req.headers),
1154
+ body: {
1155
+ model: "",
1156
+ messages: []
1157
+ }
1158
+ }, defaults.registry, defaults.logger)) return;
1159
+ await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
1160
+ } catch (err) {
1161
+ const msg = err instanceof Error ? err.message : "Internal error";
1162
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1163
+ message: msg,
1164
+ type: "server_error"
1165
+ } }));
1166
+ else if (!res.writableEnded) res.destroy();
1167
+ }
1168
+ return;
1169
+ }
1170
+ if (pathname.match(FAL_QUEUE_REQUESTS_RE) && (req.method === "GET" || req.method === "POST" || req.method === "PUT")) {
1171
+ setCorsHeaders(res);
1172
+ try {
1173
+ const raw = req.method === "POST" ? await readBody(req) : "{}";
1174
+ if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
1175
+ method: req.method ?? "GET",
1176
+ path: pathname,
1177
+ headers: require_helpers.flattenHeaders(req.headers),
1178
+ body: {
1179
+ model: "",
1180
+ messages: []
1181
+ }
1182
+ }, defaults.registry, defaults.logger)) return;
1183
+ await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
1184
+ } catch (err) {
1185
+ const msg = err instanceof Error ? err.message : "Internal error";
1186
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1187
+ message: msg,
1188
+ type: "server_error"
1189
+ } }));
1190
+ else if (!res.writableEnded) res.destroy();
1191
+ }
1192
+ return;
1193
+ }
1194
+ if (pathname.match(FAL_RUN_RE) && req.method === "POST") {
1195
+ setCorsHeaders(res);
1196
+ try {
1197
+ const raw = await readBody(req);
1198
+ if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
1199
+ method: req.method ?? "POST",
1200
+ path: pathname,
1201
+ headers: require_helpers.flattenHeaders(req.headers),
1202
+ body: {
1203
+ model: "",
1204
+ messages: []
1205
+ }
1206
+ }, defaults.registry, defaults.logger)) return;
1207
+ await require_fal_audio.handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
1208
+ } catch (err) {
1209
+ const msg = err instanceof Error ? err.message : "Internal error";
1210
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1211
+ message: msg,
1212
+ type: "server_error"
1213
+ } }));
1214
+ else if (!res.writableEnded) res.destroy();
1215
+ }
1216
+ return;
1217
+ }
1049
1218
  if (pathname !== COMPLETIONS_PATH) {
1050
1219
  handleNotFound(res, "Not found");
1051
1220
  return;