@copilotkit/aimock 1.21.0 → 1.22.1

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 (230) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +56 -0
  4. package/README.md +1 -0
  5. package/dist/a2a-mock.cjs +1 -1
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts.map +1 -1
  8. package/dist/a2a-mock.d.ts.map +1 -1
  9. package/dist/a2a-mock.js +1 -1
  10. package/dist/a2a-mock.js.map +1 -1
  11. package/dist/agui-recorder.cjs +25 -12
  12. package/dist/agui-recorder.cjs.map +1 -1
  13. package/dist/agui-recorder.js +25 -12
  14. package/dist/agui-recorder.js.map +1 -1
  15. package/dist/agui-types.d.cts.map +1 -1
  16. package/dist/aimock-cli.cjs +0 -0
  17. package/dist/aimock-cli.js +0 -0
  18. package/dist/bedrock-converse.cjs +72 -26
  19. package/dist/bedrock-converse.cjs.map +1 -1
  20. package/dist/bedrock-converse.d.cts.map +1 -1
  21. package/dist/bedrock-converse.d.ts.map +1 -1
  22. package/dist/bedrock-converse.js +73 -27
  23. package/dist/bedrock-converse.js.map +1 -1
  24. package/dist/bedrock.cjs +69 -24
  25. package/dist/bedrock.cjs.map +1 -1
  26. package/dist/bedrock.d.cts.map +1 -1
  27. package/dist/bedrock.d.ts.map +1 -1
  28. package/dist/bedrock.js +70 -25
  29. package/dist/bedrock.js.map +1 -1
  30. package/dist/cli.cjs +2 -2
  31. package/dist/cli.cjs.map +1 -1
  32. package/dist/cli.js +2 -2
  33. package/dist/cli.js.map +1 -1
  34. package/dist/cohere.cjs +34 -11
  35. package/dist/cohere.cjs.map +1 -1
  36. package/dist/cohere.d.cts.map +1 -1
  37. package/dist/cohere.d.ts.map +1 -1
  38. package/dist/cohere.js +35 -12
  39. package/dist/cohere.js.map +1 -1
  40. package/dist/config-loader.d.ts.map +1 -1
  41. package/dist/constants.cjs +8 -0
  42. package/dist/constants.cjs.map +1 -0
  43. package/dist/constants.d.cts +8 -0
  44. package/dist/constants.d.cts.map +1 -0
  45. package/dist/constants.d.ts +8 -0
  46. package/dist/constants.d.ts.map +1 -0
  47. package/dist/constants.js +7 -0
  48. package/dist/constants.js.map +1 -0
  49. package/dist/elevenlabs-audio.cjs +46 -20
  50. package/dist/elevenlabs-audio.cjs.map +1 -1
  51. package/dist/elevenlabs-audio.d.cts.map +1 -1
  52. package/dist/elevenlabs-audio.d.ts.map +1 -1
  53. package/dist/elevenlabs-audio.js +47 -21
  54. package/dist/elevenlabs-audio.js.map +1 -1
  55. package/dist/embeddings.cjs +25 -21
  56. package/dist/embeddings.cjs.map +1 -1
  57. package/dist/embeddings.d.cts.map +1 -1
  58. package/dist/embeddings.d.ts.map +1 -1
  59. package/dist/embeddings.js +26 -22
  60. package/dist/embeddings.js.map +1 -1
  61. package/dist/fal-audio.cjs +138 -43
  62. package/dist/fal-audio.cjs.map +1 -1
  63. package/dist/fal-audio.d.cts.map +1 -1
  64. package/dist/fal-audio.d.ts.map +1 -1
  65. package/dist/fal-audio.js +139 -44
  66. package/dist/fal-audio.js.map +1 -1
  67. package/dist/fal.cjs +27 -8
  68. package/dist/fal.cjs.map +1 -1
  69. package/dist/fal.d.cts.map +1 -1
  70. package/dist/fal.d.ts.map +1 -1
  71. package/dist/fal.js +28 -9
  72. package/dist/fal.js.map +1 -1
  73. package/dist/fixture-loader.cjs +9 -1
  74. package/dist/fixture-loader.cjs.map +1 -1
  75. package/dist/fixture-loader.js +9 -1
  76. package/dist/fixture-loader.js.map +1 -1
  77. package/dist/gemini-interactions.cjs +34 -9
  78. package/dist/gemini-interactions.cjs.map +1 -1
  79. package/dist/gemini-interactions.d.cts.map +1 -1
  80. package/dist/gemini-interactions.d.ts.map +1 -1
  81. package/dist/gemini-interactions.js +34 -11
  82. package/dist/gemini-interactions.js.map +1 -1
  83. package/dist/gemini.cjs +50 -21
  84. package/dist/gemini.cjs.map +1 -1
  85. package/dist/gemini.d.cts.map +1 -1
  86. package/dist/gemini.d.ts.map +1 -1
  87. package/dist/gemini.js +51 -22
  88. package/dist/gemini.js.map +1 -1
  89. package/dist/helpers.cjs +82 -8
  90. package/dist/helpers.cjs.map +1 -1
  91. package/dist/helpers.d.cts +7 -0
  92. package/dist/helpers.d.cts.map +1 -1
  93. package/dist/helpers.d.ts +7 -0
  94. package/dist/helpers.d.ts.map +1 -1
  95. package/dist/helpers.js +80 -9
  96. package/dist/helpers.js.map +1 -1
  97. package/dist/images.cjs +31 -10
  98. package/dist/images.cjs.map +1 -1
  99. package/dist/images.d.cts.map +1 -1
  100. package/dist/images.d.ts.map +1 -1
  101. package/dist/images.js +32 -11
  102. package/dist/images.js.map +1 -1
  103. package/dist/index.cjs +2 -1
  104. package/dist/index.d.cts +2 -1
  105. package/dist/index.d.ts +2 -1
  106. package/dist/index.js +2 -1
  107. package/dist/journal.cjs +17 -7
  108. package/dist/journal.cjs.map +1 -1
  109. package/dist/journal.d.cts +2 -3
  110. package/dist/journal.d.cts.map +1 -1
  111. package/dist/journal.d.ts +2 -3
  112. package/dist/journal.d.ts.map +1 -1
  113. package/dist/journal.js +15 -4
  114. package/dist/journal.js.map +1 -1
  115. package/dist/mcp-mock.cjs +1 -1
  116. package/dist/mcp-mock.cjs.map +1 -1
  117. package/dist/mcp-mock.d.cts.map +1 -1
  118. package/dist/mcp-mock.d.ts.map +1 -1
  119. package/dist/mcp-mock.js +1 -1
  120. package/dist/mcp-mock.js.map +1 -1
  121. package/dist/messages.cjs +38 -14
  122. package/dist/messages.cjs.map +1 -1
  123. package/dist/messages.d.cts.map +1 -1
  124. package/dist/messages.d.ts.map +1 -1
  125. package/dist/messages.js +39 -15
  126. package/dist/messages.js.map +1 -1
  127. package/dist/moderation.cjs +3 -2
  128. package/dist/moderation.cjs.map +1 -1
  129. package/dist/moderation.js +3 -2
  130. package/dist/moderation.js.map +1 -1
  131. package/dist/ollama.cjs +69 -22
  132. package/dist/ollama.cjs.map +1 -1
  133. package/dist/ollama.d.cts.map +1 -1
  134. package/dist/ollama.d.ts.map +1 -1
  135. package/dist/ollama.js +70 -23
  136. package/dist/ollama.js.map +1 -1
  137. package/dist/recorder.cjs +89 -41
  138. package/dist/recorder.cjs.map +1 -1
  139. package/dist/recorder.d.cts +3 -2
  140. package/dist/recorder.d.cts.map +1 -1
  141. package/dist/recorder.d.ts +3 -2
  142. package/dist/recorder.d.ts.map +1 -1
  143. package/dist/recorder.js +89 -41
  144. package/dist/recorder.js.map +1 -1
  145. package/dist/rerank.cjs +3 -2
  146. package/dist/rerank.cjs.map +1 -1
  147. package/dist/rerank.js +3 -2
  148. package/dist/rerank.js.map +1 -1
  149. package/dist/responses.cjs +66 -54
  150. package/dist/responses.cjs.map +1 -1
  151. package/dist/responses.d.cts +1 -1
  152. package/dist/responses.d.cts.map +1 -1
  153. package/dist/responses.d.ts +1 -1
  154. package/dist/responses.d.ts.map +1 -1
  155. package/dist/responses.js +67 -55
  156. package/dist/responses.js.map +1 -1
  157. package/dist/search.cjs +3 -2
  158. package/dist/search.cjs.map +1 -1
  159. package/dist/search.js +3 -2
  160. package/dist/search.js.map +1 -1
  161. package/dist/server.cjs +117 -171
  162. package/dist/server.cjs.map +1 -1
  163. package/dist/server.d.cts.map +1 -1
  164. package/dist/server.d.ts.map +1 -1
  165. package/dist/server.js +95 -149
  166. package/dist/server.js.map +1 -1
  167. package/dist/speech.cjs +31 -10
  168. package/dist/speech.cjs.map +1 -1
  169. package/dist/speech.d.cts.map +1 -1
  170. package/dist/speech.d.ts.map +1 -1
  171. package/dist/speech.js +32 -11
  172. package/dist/speech.js.map +1 -1
  173. package/dist/stream-collapse.cjs +51 -21
  174. package/dist/stream-collapse.cjs.map +1 -1
  175. package/dist/stream-collapse.d.cts +1 -0
  176. package/dist/stream-collapse.d.cts.map +1 -1
  177. package/dist/stream-collapse.d.ts +1 -0
  178. package/dist/stream-collapse.d.ts.map +1 -1
  179. package/dist/stream-collapse.js +51 -21
  180. package/dist/stream-collapse.js.map +1 -1
  181. package/dist/transcription.cjs +59 -19
  182. package/dist/transcription.cjs.map +1 -1
  183. package/dist/transcription.d.cts.map +1 -1
  184. package/dist/transcription.d.ts.map +1 -1
  185. package/dist/transcription.js +60 -20
  186. package/dist/transcription.js.map +1 -1
  187. package/dist/types.d.cts +4 -0
  188. package/dist/types.d.cts.map +1 -1
  189. package/dist/types.d.ts +4 -0
  190. package/dist/types.d.ts.map +1 -1
  191. package/dist/vector-mock.cjs +10 -8
  192. package/dist/vector-mock.cjs.map +1 -1
  193. package/dist/vector-mock.d.cts.map +1 -1
  194. package/dist/vector-mock.d.ts.map +1 -1
  195. package/dist/vector-mock.js +10 -8
  196. package/dist/vector-mock.js.map +1 -1
  197. package/dist/vector-types.d.ts.map +1 -1
  198. package/dist/video.cjs +55 -16
  199. package/dist/video.cjs.map +1 -1
  200. package/dist/video.d.cts +8 -1
  201. package/dist/video.d.cts.map +1 -1
  202. package/dist/video.d.ts +8 -1
  203. package/dist/video.d.ts.map +1 -1
  204. package/dist/video.js +56 -17
  205. package/dist/video.js.map +1 -1
  206. package/dist/ws-gemini-live.cjs +40 -31
  207. package/dist/ws-gemini-live.cjs.map +1 -1
  208. package/dist/ws-gemini-live.d.cts +2 -0
  209. package/dist/ws-gemini-live.d.cts.map +1 -1
  210. package/dist/ws-gemini-live.d.ts +2 -0
  211. package/dist/ws-gemini-live.d.ts.map +1 -1
  212. package/dist/ws-gemini-live.js +40 -31
  213. package/dist/ws-gemini-live.js.map +1 -1
  214. package/dist/ws-realtime.cjs +257 -16
  215. package/dist/ws-realtime.cjs.map +1 -1
  216. package/dist/ws-realtime.d.cts +2 -0
  217. package/dist/ws-realtime.d.cts.map +1 -1
  218. package/dist/ws-realtime.d.ts +2 -0
  219. package/dist/ws-realtime.d.ts.map +1 -1
  220. package/dist/ws-realtime.js +257 -16
  221. package/dist/ws-realtime.js.map +1 -1
  222. package/dist/ws-responses.cjs +54 -16
  223. package/dist/ws-responses.cjs.map +1 -1
  224. package/dist/ws-responses.d.cts +2 -0
  225. package/dist/ws-responses.d.cts.map +1 -1
  226. package/dist/ws-responses.d.ts +2 -0
  227. package/dist/ws-responses.d.ts.map +1 -1
  228. package/dist/ws-responses.js +55 -17
  229. package/dist/ws-responses.js.map +1 -1
  230. package/package.json +2 -2
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, getTestId, isAudioResponse, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse } from "./helpers.js";
1
+ import { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, getTestId, isAudioResponse, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, readBody, resolveResponse, resolveStrictMode, serializeErrorResponse, strictOverrideField } from "./helpers.js";
2
2
  import { Logger } from "./logger.js";
3
3
  import { Journal } from "./journal.js";
4
4
  import { matchFixture } from "./router.js";
@@ -12,7 +12,7 @@ import { handleMessages } from "./messages.js";
12
12
  import { handleGemini } from "./gemini.js";
13
13
  import { handleBedrock, handleBedrockStream } from "./bedrock.js";
14
14
  import { handleConverse, handleConverseStream } from "./bedrock-converse.js";
15
- import { handleGeminiInteractions } from "./gemini-interactions.js";
15
+ import { handleGeminiInteractions, resetEventIdCounter, resetInteractionCounter } from "./gemini-interactions.js";
16
16
  import { handleEmbeddings } from "./embeddings.js";
17
17
  import { handleImages } from "./images.js";
18
18
  import { handleSpeech } from "./speech.js";
@@ -111,21 +111,6 @@ const CORS_HEADERS = {
111
111
  function setCorsHeaders(res) {
112
112
  for (const [key, value] of Object.entries(CORS_HEADERS)) res.setHeader(key, value);
113
113
  }
114
- const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;
115
- async function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
116
- const buffers = [];
117
- let totalBytes = 0;
118
- for await (const chunk of req) {
119
- const buf = chunk;
120
- totalBytes += buf.length;
121
- if (totalBytes > maxBytes) {
122
- req.destroy();
123
- throw new Error(`Request body exceeded size limit of ${maxBytes} bytes`);
124
- }
125
- buffers.push(buf);
126
- }
127
- return Buffer.concat(buffers).toString();
128
- }
129
114
  function handleOptions(res) {
130
115
  setCorsHeaders(res);
131
116
  res.writeHead(204);
@@ -212,6 +197,8 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
212
197
  videoStates.clear();
213
198
  falJobs.clear();
214
199
  falQueueStates.clear();
200
+ resetInteractionCounter();
201
+ resetEventIdCounter();
215
202
  if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
216
203
  res.writeHead(200, { "Content-Type": "application/json" });
217
204
  res.end(JSON.stringify({ reset: true }));
@@ -252,12 +239,17 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
252
239
  }
253
240
  };
254
241
  fixtures.unshift(errorFixture);
242
+ let consumed = false;
255
243
  const original = errorFixture.match.predicate;
256
244
  errorFixture.match.predicate = (req) => {
245
+ if (consumed) return false;
257
246
  const result = original(req);
258
247
  if (result) {
259
- const idx = fixtures.indexOf(errorFixture);
260
- if (idx !== -1) fixtures.splice(idx, 1);
248
+ consumed = true;
249
+ queueMicrotask(() => {
250
+ const idx = fixtures.indexOf(errorFixture);
251
+ if (idx !== -1) fixtures.splice(idx, 1);
252
+ });
261
253
  }
262
254
  return result;
263
255
  };
@@ -295,7 +287,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
295
287
  try {
296
288
  body = JSON.parse(raw);
297
289
  if (modelFallback && !body.model) body.model = modelFallback;
298
- } catch {
290
+ } catch (parseErr) {
291
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown parse error";
299
292
  journal.add({
300
293
  method: req.method ?? "POST",
301
294
  path: req.url ?? COMPLETIONS_PATH,
@@ -307,7 +300,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
307
300
  }
308
301
  });
309
302
  writeErrorResponse(res, 400, JSON.stringify({ error: {
310
- message: "Malformed JSON",
303
+ message: `Malformed JSON: ${detail}`,
311
304
  type: "invalid_request_error",
312
305
  param: null,
313
306
  code: "invalid_json"
@@ -363,6 +356,29 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
363
356
  return;
364
357
  }
365
358
  if (!fixture) {
359
+ if (resolveStrictMode(defaults.strict, req.headers)) {
360
+ const strictStatus = 503;
361
+ const strictMessage = "Strict mode: no fixture matched";
362
+ defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`);
363
+ journal.add({
364
+ method: req.method ?? "POST",
365
+ path: req.url ?? COMPLETIONS_PATH,
366
+ headers: flattenHeaders(req.headers),
367
+ body,
368
+ response: {
369
+ status: strictStatus,
370
+ fixture: null,
371
+ ...strictOverrideField(defaults.strict, req.headers)
372
+ }
373
+ });
374
+ writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
375
+ message: strictMessage,
376
+ type: "invalid_request_error",
377
+ param: null,
378
+ code: "no_fixture_match"
379
+ } }));
380
+ return;
381
+ }
366
382
  if (defaults.record && providerKey) {
367
383
  const hookOptions = chaosAction === "malformed" ? {
368
384
  beforeWriteResponse: () => {
@@ -370,7 +386,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
370
386
  return true;
371
387
  },
372
388
  onHookBypassed: (reason) => {
373
- defaults.logger.warn(`[chaos] malformed bypassed on proxy: upstream returned SSE (${reason})`);
389
+ defaults.logger.warn(`[chaos] malformed bypassed on proxy: upstream returned streaming response (${reason})`);
374
390
  defaults.registry?.incrementCounter("aimock_chaos_bypassed_total", {
375
391
  action: "malformed",
376
392
  source: "proxy",
@@ -380,7 +396,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
380
396
  } : void 0;
381
397
  const outcome = await proxyAndRecord(req, res, body, providerKey, req.url ?? COMPLETIONS_PATH, fixtures, defaults, raw, hookOptions);
382
398
  if (outcome === "handled_by_hook") return;
383
- if (outcome === "relayed") {
399
+ if (outcome !== "not_configured") {
384
400
  journal.add({
385
401
  method: req.method ?? "POST",
386
402
  path: req.url ?? COMPLETIONS_PATH,
@@ -395,21 +411,19 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
395
411
  return;
396
412
  }
397
413
  }
398
- const strictStatus = defaults.strict ? 503 : 404;
399
- const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
400
- if (defaults.strict) defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`);
401
414
  journal.add({
402
415
  method: req.method ?? "POST",
403
416
  path: req.url ?? COMPLETIONS_PATH,
404
417
  headers: flattenHeaders(req.headers),
405
418
  body,
406
419
  response: {
407
- status: strictStatus,
408
- fixture: null
420
+ status: 404,
421
+ fixture: null,
422
+ ...strictOverrideField(defaults.strict, req.headers)
409
423
  }
410
424
  });
411
- writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
412
- message: strictMessage,
425
+ writeErrorResponse(res, 404, JSON.stringify({ error: {
426
+ message: "No fixture matched",
413
427
  type: "invalid_request_error",
414
428
  param: null,
415
429
  code: "no_fixture_match"
@@ -431,13 +445,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
431
445
  fixture
432
446
  }
433
447
  });
434
- const errorBody = { error: {
435
- message: response.error.message,
436
- type: response.error.type ?? "server_error",
437
- param: null,
438
- code: response.error.code ?? null
439
- } };
440
- writeErrorResponse(res, status, JSON.stringify(errorBody));
448
+ writeErrorResponse(res, status, serializeErrorResponse(response));
441
449
  return;
442
450
  }
443
451
  if (isAudioResponse(response)) {
@@ -526,6 +534,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
526
534
  return;
527
535
  }
528
536
  if (isToolCallResponse(response)) {
537
+ if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
529
538
  const overrides = extractOverrides(response);
530
539
  const journalEntry = journal.add({
531
540
  method: req.method ?? "POST",
@@ -634,7 +643,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
634
643
  message: msg,
635
644
  type: "server_error"
636
645
  } }));
637
- }
646
+ } else if (!res.writableEnded) res.end();
638
647
  });
639
648
  });
640
649
  async function handleHttpRequest(req, res) {
@@ -775,13 +784,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
775
784
  message: msg,
776
785
  type: "server_error"
777
786
  } }));
778
- else if (!res.writableEnded) {
779
- try {
780
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
781
- } catch (writeErr) {
782
- logger.debug("Failed to write error recovery response:", writeErr);
783
- }
787
+ else if (!res.writableEnded) try {
788
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
784
789
  res.end();
790
+ } catch (writeErr) {
791
+ logger.debug("Failed to write error recovery response:", writeErr);
785
792
  }
786
793
  }
787
794
  return;
@@ -795,13 +802,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
795
802
  message: msg,
796
803
  type: "server_error"
797
804
  } }));
798
- else if (!res.writableEnded) {
799
- try {
800
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
801
- } catch (writeErr) {
802
- logger.debug("Failed to write error recovery response:", writeErr);
803
- }
805
+ else if (!res.writableEnded) try {
806
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
804
807
  res.end();
808
+ } catch (writeErr) {
809
+ logger.debug("Failed to write error recovery response:", writeErr);
805
810
  }
806
811
  }
807
812
  return;
@@ -815,13 +820,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
815
820
  message: msg,
816
821
  type: "server_error"
817
822
  } }));
818
- else if (!res.writableEnded) {
819
- try {
820
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
821
- } catch (writeErr) {
822
- logger.debug("Failed to write error recovery response:", writeErr);
823
- }
823
+ else if (!res.writableEnded) try {
824
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
824
825
  res.end();
826
+ } catch (writeErr) {
827
+ logger.debug("Failed to write error recovery response:", writeErr);
825
828
  }
826
829
  }
827
830
  return;
@@ -837,7 +840,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
837
840
  parsed.model = deploymentId;
838
841
  raw = JSON.stringify(parsed);
839
842
  }
840
- } catch {}
843
+ } catch (err) {
844
+ if (!(err instanceof SyntaxError)) defaults.logger.error(`Unexpected error in Azure model injection: ${err instanceof Error ? err.message : String(err)}`);
845
+ }
841
846
  await handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider);
842
847
  } catch (err) {
843
848
  const msg = err instanceof Error ? err.message : "Internal error";
@@ -931,13 +936,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
931
936
  message: msg,
932
937
  type: "server_error"
933
938
  } }));
934
- else if (!res.writableEnded) {
935
- try {
936
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
937
- } catch (writeErr) {
938
- logger.debug("Failed to write error recovery response:", writeErr);
939
- }
939
+ else if (!res.writableEnded) try {
940
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
940
941
  res.end();
942
+ } catch (writeErr) {
943
+ logger.debug("Failed to write error recovery response:", writeErr);
941
944
  }
942
945
  }
943
946
  return;
@@ -954,13 +957,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
954
957
  message: msg,
955
958
  type: "server_error"
956
959
  } }));
957
- else if (!res.writableEnded) {
958
- try {
959
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
960
- } catch (writeErr) {
961
- logger.debug("Failed to write error recovery response:", writeErr);
962
- }
960
+ else if (!res.writableEnded) try {
961
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
963
962
  res.end();
963
+ } catch (writeErr) {
964
+ logger.debug("Failed to write error recovery response:", writeErr);
964
965
  }
965
966
  }
966
967
  return;
@@ -977,13 +978,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
977
978
  message: msg,
978
979
  type: "server_error"
979
980
  } }));
980
- else if (!res.writableEnded) {
981
- try {
982
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
983
- } catch (writeErr) {
984
- logger.debug("Failed to write error recovery response:", writeErr);
985
- }
981
+ else if (!res.writableEnded) try {
982
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
986
983
  res.end();
984
+ } catch (writeErr) {
985
+ logger.debug("Failed to write error recovery response:", writeErr);
987
986
  }
988
987
  }
989
988
  return;
@@ -1132,21 +1131,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1132
1131
  if (pathname === ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
1133
1132
  setCorsHeaders(res);
1134
1133
  try {
1135
- const raw = await readBody(req);
1136
- const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1137
- if (chaosAction) {
1138
- applyChaosAction(chaosAction, res, null, journal, {
1139
- method: req.method ?? "POST",
1140
- path: pathname,
1141
- headers: flattenHeaders(req.headers),
1142
- body: {
1143
- model: "",
1144
- messages: []
1145
- }
1146
- }, "fixture", defaults.registry);
1147
- return;
1148
- }
1149
- await handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, "sound-generation");
1134
+ await handleElevenLabsAudio(req, res, await readBody(req), fixtures, defaults, journal, "sound-generation");
1150
1135
  } catch (err) {
1151
1136
  const msg = err instanceof Error ? err.message : "Internal error";
1152
1137
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1162,21 +1147,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1162
1147
  setCorsHeaders(res);
1163
1148
  const musicSubType = musicMatch[1] ?? "music";
1164
1149
  try {
1165
- const raw = await readBody(req);
1166
- const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1167
- if (chaosAction) {
1168
- applyChaosAction(chaosAction, res, null, journal, {
1169
- method: req.method ?? "POST",
1170
- path: pathname,
1171
- headers: flattenHeaders(req.headers),
1172
- body: {
1173
- model: "",
1174
- messages: []
1175
- }
1176
- }, "fixture", defaults.registry);
1177
- return;
1178
- }
1179
- await handleElevenLabsAudio(req, res, raw, fixtures, defaults, journal, musicSubType);
1150
+ await handleElevenLabsAudio(req, res, await readBody(req), fixtures, defaults, journal, musicSubType);
1180
1151
  } catch (err) {
1181
1152
  const msg = err instanceof Error ? err.message : "Internal error";
1182
1153
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1187,10 +1158,12 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1187
1158
  }
1188
1159
  return;
1189
1160
  }
1161
+ let falBody;
1190
1162
  if (FAL_PREFIX_RE.test(pathname) && req.headers["x-fal-target-host"]) {
1191
1163
  setCorsHeaders(res);
1192
1164
  try {
1193
- const raw = req.method === "POST" || req.method === "PUT" ? await readBody(req) : "";
1165
+ falBody = req.method === "POST" || req.method === "PUT" ? await readBody(req) : "";
1166
+ const raw = falBody;
1194
1167
  const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1195
1168
  if (chaosAction) {
1196
1169
  applyChaosAction(chaosAction, res, null, journal, {
@@ -1218,21 +1191,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1218
1191
  if (pathname.match(FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
1219
1192
  setCorsHeaders(res);
1220
1193
  try {
1221
- const raw = await readBody(req);
1222
- const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1223
- if (chaosAction) {
1224
- applyChaosAction(chaosAction, res, null, journal, {
1225
- method: req.method ?? "POST",
1226
- path: pathname,
1227
- headers: flattenHeaders(req.headers),
1228
- body: {
1229
- model: "",
1230
- messages: []
1231
- }
1232
- }, "fixture", defaults.registry);
1233
- return;
1234
- }
1235
- await handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
1194
+ await handleFalQueue(req, res, falBody ?? await readBody(req), pathname, fixtures, defaults, journal);
1236
1195
  } catch (err) {
1237
1196
  const msg = err instanceof Error ? err.message : "Internal error";
1238
1197
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1246,7 +1205,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1246
1205
  if (pathname.match(FAL_QUEUE_REQUESTS_RE) && (req.method === "GET" || req.method === "POST" || req.method === "PUT")) {
1247
1206
  setCorsHeaders(res);
1248
1207
  try {
1249
- const raw = req.method === "POST" ? await readBody(req) : "{}";
1208
+ const raw = req.method === "POST" || req.method === "PUT" ? falBody ?? await readBody(req) : "{}";
1250
1209
  const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1251
1210
  if (chaosAction) {
1252
1211
  applyChaosAction(chaosAction, res, null, journal, {
@@ -1274,21 +1233,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1274
1233
  if (pathname.match(FAL_RUN_RE) && req.method === "POST") {
1275
1234
  setCorsHeaders(res);
1276
1235
  try {
1277
- const raw = await readBody(req);
1278
- const chaosAction = evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1279
- if (chaosAction) {
1280
- applyChaosAction(chaosAction, res, null, journal, {
1281
- method: req.method ?? "POST",
1282
- path: pathname,
1283
- headers: flattenHeaders(req.headers),
1284
- body: {
1285
- model: "",
1286
- messages: []
1287
- }
1288
- }, "fixture", defaults.registry);
1289
- return;
1290
- }
1291
- await handleFalQueue(req, res, raw, pathname, fixtures, defaults, journal);
1236
+ await handleFalQueue(req, res, falBody ?? await readBody(req), pathname, fixtures, defaults, journal);
1292
1237
  } catch (err) {
1293
1238
  const msg = err instanceof Error ? err.message : "Internal error";
1294
1239
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1316,16 +1261,14 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1316
1261
  message: msg,
1317
1262
  type: "server_error"
1318
1263
  } }));
1319
- else if (!res.writableEnded) {
1320
- try {
1321
- res.write(`data: ${JSON.stringify({ error: {
1322
- message: msg,
1323
- type: "server_error"
1324
- } })}\n\n`);
1325
- } catch (writeErr) {
1326
- logger.debug("Failed to write error recovery response:", writeErr);
1327
- }
1264
+ else if (!res.writableEnded) try {
1265
+ res.write(`data: ${JSON.stringify({ error: {
1266
+ message: msg,
1267
+ type: "server_error"
1268
+ } })}\n\n`);
1328
1269
  res.end();
1270
+ } catch (writeErr) {
1271
+ logger.debug("Failed to write error recovery response:", writeErr);
1329
1272
  }
1330
1273
  }
1331
1274
  }
@@ -1374,19 +1317,22 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1374
1317
  if (pathname === RESPONSES_PATH) handleWebSocketResponses(ws, fixtures, journal, {
1375
1318
  ...defaults,
1376
1319
  model: "gpt-4",
1377
- testId: wsTestId
1320
+ testId: wsTestId,
1321
+ upgradeHeaders: req.headers
1378
1322
  });
1379
1323
  else if (pathname === REALTIME_PATH) {
1380
1324
  const model = parsedUrl.searchParams.get("model") ?? "gpt-4o-realtime";
1381
1325
  handleWebSocketRealtime(ws, fixtures, journal, {
1382
1326
  ...defaults,
1383
1327
  model,
1384
- testId: wsTestId
1328
+ testId: wsTestId,
1329
+ upgradeHeaders: req.headers
1385
1330
  });
1386
1331
  } else if (pathname === GEMINI_LIVE_PATH) handleWebSocketGeminiLive(ws, fixtures, journal, {
1387
1332
  ...defaults,
1388
1333
  model: "gemini-2.0-flash",
1389
- testId: wsTestId
1334
+ testId: wsTestId,
1335
+ upgradeHeaders: req.headers
1390
1336
  });
1391
1337
  }
1392
1338
  const originalClose = server.close.bind(server);