@copilotkit/aimock 1.24.1 → 1.26.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 (206) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +38 -0
  4. package/README.md +21 -11
  5. package/dist/agui-types.d.cts.map +1 -1
  6. package/dist/agui-types.d.ts.map +1 -1
  7. package/dist/aws-event-stream.cjs +2 -1
  8. package/dist/aws-event-stream.cjs.map +1 -1
  9. package/dist/aws-event-stream.d.cts +3 -1
  10. package/dist/aws-event-stream.d.cts.map +1 -1
  11. package/dist/aws-event-stream.d.ts +3 -1
  12. package/dist/aws-event-stream.d.ts.map +1 -1
  13. package/dist/aws-event-stream.js +2 -1
  14. package/dist/aws-event-stream.js.map +1 -1
  15. package/dist/bedrock-converse.cjs +8 -2
  16. package/dist/bedrock-converse.cjs.map +1 -1
  17. package/dist/bedrock-converse.d.cts.map +1 -1
  18. package/dist/bedrock-converse.d.ts.map +1 -1
  19. package/dist/bedrock-converse.js +8 -2
  20. package/dist/bedrock-converse.js.map +1 -1
  21. package/dist/bedrock.cjs +8 -2
  22. package/dist/bedrock.cjs.map +1 -1
  23. package/dist/bedrock.d.cts.map +1 -1
  24. package/dist/bedrock.d.ts.map +1 -1
  25. package/dist/bedrock.js +8 -2
  26. package/dist/bedrock.js.map +1 -1
  27. package/dist/cli.cjs +36 -1
  28. package/dist/cli.cjs.map +1 -1
  29. package/dist/cli.js +36 -1
  30. package/dist/cli.js.map +1 -1
  31. package/dist/cohere.cjs +206 -2
  32. package/dist/cohere.cjs.map +1 -1
  33. package/dist/cohere.d.cts.map +1 -1
  34. package/dist/cohere.d.ts.map +1 -1
  35. package/dist/cohere.js +207 -4
  36. package/dist/cohere.js.map +1 -1
  37. package/dist/config-loader.d.ts.map +1 -1
  38. package/dist/elevenlabs-audio.cjs +173 -1
  39. package/dist/elevenlabs-audio.cjs.map +1 -1
  40. package/dist/elevenlabs-audio.d.cts.map +1 -1
  41. package/dist/elevenlabs-audio.d.ts.map +1 -1
  42. package/dist/elevenlabs-audio.js +173 -2
  43. package/dist/elevenlabs-audio.js.map +1 -1
  44. package/dist/embeddings.cjs +1 -1
  45. package/dist/embeddings.cjs.map +1 -1
  46. package/dist/embeddings.js +1 -1
  47. package/dist/embeddings.js.map +1 -1
  48. package/dist/fal-audio.cjs +2 -4
  49. package/dist/fal-audio.cjs.map +1 -1
  50. package/dist/fal-audio.js +2 -4
  51. package/dist/fal-audio.js.map +1 -1
  52. package/dist/fal.cjs +2 -2
  53. package/dist/fal.cjs.map +1 -1
  54. package/dist/fal.d.cts.map +1 -1
  55. package/dist/fal.d.ts.map +1 -1
  56. package/dist/fal.js +2 -2
  57. package/dist/fal.js.map +1 -1
  58. package/dist/fixture-loader.cjs +16 -3
  59. package/dist/fixture-loader.cjs.map +1 -1
  60. package/dist/fixture-loader.d.cts.map +1 -1
  61. package/dist/fixture-loader.d.ts.map +1 -1
  62. package/dist/fixture-loader.js +16 -3
  63. package/dist/fixture-loader.js.map +1 -1
  64. package/dist/gemini-embeddings.cjs +166 -0
  65. package/dist/gemini-embeddings.cjs.map +1 -0
  66. package/dist/gemini-embeddings.js +166 -0
  67. package/dist/gemini-embeddings.js.map +1 -0
  68. package/dist/gemini-interactions.cjs +10 -2
  69. package/dist/gemini-interactions.cjs.map +1 -1
  70. package/dist/gemini-interactions.d.cts.map +1 -1
  71. package/dist/gemini-interactions.d.ts.map +1 -1
  72. package/dist/gemini-interactions.js +10 -2
  73. package/dist/gemini-interactions.js.map +1 -1
  74. package/dist/gemini.cjs +12 -2
  75. package/dist/gemini.cjs.map +1 -1
  76. package/dist/gemini.d.cts.map +1 -1
  77. package/dist/gemini.d.ts.map +1 -1
  78. package/dist/gemini.js +12 -2
  79. package/dist/gemini.js.map +1 -1
  80. package/dist/helpers.cjs +70 -33
  81. package/dist/helpers.cjs.map +1 -1
  82. package/dist/helpers.d.cts +9 -5
  83. package/dist/helpers.d.cts.map +1 -1
  84. package/dist/helpers.d.ts +9 -5
  85. package/dist/helpers.d.ts.map +1 -1
  86. package/dist/helpers.js +68 -34
  87. package/dist/helpers.js.map +1 -1
  88. package/dist/images.cjs +295 -13
  89. package/dist/images.cjs.map +1 -1
  90. package/dist/images.d.cts +9 -1
  91. package/dist/images.d.cts.map +1 -1
  92. package/dist/images.d.ts +9 -1
  93. package/dist/images.d.ts.map +1 -1
  94. package/dist/images.js +294 -14
  95. package/dist/images.js.map +1 -1
  96. package/dist/index.cjs +1 -1
  97. package/dist/index.d.cts +2 -2
  98. package/dist/index.d.ts +2 -2
  99. package/dist/index.js +1 -1
  100. package/dist/llmock.cjs +16 -1
  101. package/dist/llmock.cjs.map +1 -1
  102. package/dist/llmock.d.cts +2 -0
  103. package/dist/llmock.d.cts.map +1 -1
  104. package/dist/llmock.d.ts +2 -0
  105. package/dist/llmock.d.ts.map +1 -1
  106. package/dist/llmock.js +16 -1
  107. package/dist/llmock.js.map +1 -1
  108. package/dist/messages.cjs +9 -2
  109. package/dist/messages.cjs.map +1 -1
  110. package/dist/messages.d.cts.map +1 -1
  111. package/dist/messages.d.ts.map +1 -1
  112. package/dist/messages.js +9 -2
  113. package/dist/messages.js.map +1 -1
  114. package/dist/metrics.cjs +2 -0
  115. package/dist/metrics.cjs.map +1 -1
  116. package/dist/metrics.d.cts.map +1 -1
  117. package/dist/metrics.d.ts.map +1 -1
  118. package/dist/metrics.js +2 -0
  119. package/dist/metrics.js.map +1 -1
  120. package/dist/ndjson-writer.cjs +2 -1
  121. package/dist/ndjson-writer.cjs.map +1 -1
  122. package/dist/ndjson-writer.d.cts +3 -2
  123. package/dist/ndjson-writer.d.cts.map +1 -1
  124. package/dist/ndjson-writer.d.ts +3 -2
  125. package/dist/ndjson-writer.d.ts.map +1 -1
  126. package/dist/ndjson-writer.js +2 -1
  127. package/dist/ndjson-writer.js.map +1 -1
  128. package/dist/ollama.cjs +197 -2
  129. package/dist/ollama.cjs.map +1 -1
  130. package/dist/ollama.d.cts.map +1 -1
  131. package/dist/ollama.d.ts.map +1 -1
  132. package/dist/ollama.js +198 -4
  133. package/dist/ollama.js.map +1 -1
  134. package/dist/recorder.cjs +49 -5
  135. package/dist/recorder.cjs.map +1 -1
  136. package/dist/recorder.d.cts.map +1 -1
  137. package/dist/recorder.d.ts.map +1 -1
  138. package/dist/recorder.js +49 -5
  139. package/dist/recorder.js.map +1 -1
  140. package/dist/responses.cjs +11 -2
  141. package/dist/responses.cjs.map +1 -1
  142. package/dist/responses.d.cts.map +1 -1
  143. package/dist/responses.d.ts.map +1 -1
  144. package/dist/responses.js +11 -2
  145. package/dist/responses.js.map +1 -1
  146. package/dist/server.cjs +196 -49
  147. package/dist/server.cjs.map +1 -1
  148. package/dist/server.d.cts.map +1 -1
  149. package/dist/server.d.ts.map +1 -1
  150. package/dist/server.js +201 -54
  151. package/dist/server.js.map +1 -1
  152. package/dist/speech.cjs +1 -1
  153. package/dist/speech.cjs.map +1 -1
  154. package/dist/speech.js +1 -1
  155. package/dist/speech.js.map +1 -1
  156. package/dist/sse-writer.cjs +48 -10
  157. package/dist/sse-writer.cjs.map +1 -1
  158. package/dist/sse-writer.d.cts +12 -4
  159. package/dist/sse-writer.d.cts.map +1 -1
  160. package/dist/sse-writer.d.ts +12 -4
  161. package/dist/sse-writer.d.ts.map +1 -1
  162. package/dist/sse-writer.js +48 -10
  163. package/dist/sse-writer.js.map +1 -1
  164. package/dist/transcription.cjs +9 -6
  165. package/dist/transcription.cjs.map +1 -1
  166. package/dist/transcription.d.cts +2 -2
  167. package/dist/transcription.d.cts.map +1 -1
  168. package/dist/transcription.d.ts +2 -2
  169. package/dist/transcription.d.ts.map +1 -1
  170. package/dist/transcription.js +8 -7
  171. package/dist/transcription.js.map +1 -1
  172. package/dist/types.d.cts +45 -3
  173. package/dist/types.d.cts.map +1 -1
  174. package/dist/types.d.ts +45 -3
  175. package/dist/types.d.ts.map +1 -1
  176. package/dist/video.cjs +1 -1
  177. package/dist/video.cjs.map +1 -1
  178. package/dist/video.d.cts.map +1 -1
  179. package/dist/video.d.ts.map +1 -1
  180. package/dist/video.js +1 -1
  181. package/dist/video.js.map +1 -1
  182. package/dist/ws-gemini-live.cjs +14 -4
  183. package/dist/ws-gemini-live.cjs.map +1 -1
  184. package/dist/ws-gemini-live.d.cts +1 -0
  185. package/dist/ws-gemini-live.d.cts.map +1 -1
  186. package/dist/ws-gemini-live.d.ts +1 -0
  187. package/dist/ws-gemini-live.d.ts.map +1 -1
  188. package/dist/ws-gemini-live.js +15 -5
  189. package/dist/ws-gemini-live.js.map +1 -1
  190. package/dist/ws-realtime.cjs +21 -4
  191. package/dist/ws-realtime.cjs.map +1 -1
  192. package/dist/ws-realtime.d.cts +1 -0
  193. package/dist/ws-realtime.d.cts.map +1 -1
  194. package/dist/ws-realtime.d.ts +1 -0
  195. package/dist/ws-realtime.d.ts.map +1 -1
  196. package/dist/ws-realtime.js +22 -5
  197. package/dist/ws-realtime.js.map +1 -1
  198. package/dist/ws-responses.cjs +8 -5
  199. package/dist/ws-responses.cjs.map +1 -1
  200. package/dist/ws-responses.d.cts +1 -0
  201. package/dist/ws-responses.d.cts.map +1 -1
  202. package/dist/ws-responses.d.ts +1 -0
  203. package/dist/ws-responses.d.ts.map +1 -1
  204. package/dist/ws-responses.js +9 -6
  205. package/dist/ws-responses.js.map +1 -1
  206. 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, readBody, resolveResponse, resolveStrictMode, serializeErrorResponse, strictOverrideField } from "./helpers.js";
1
+ import { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, estimatePromptTokens, estimateTokens, 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";
@@ -10,19 +10,20 @@ import { proxyAndRecord } from "./recorder.js";
10
10
  import { handleResponses } from "./responses.js";
11
11
  import { handleMessages } from "./messages.js";
12
12
  import { handleGemini } from "./gemini.js";
13
+ import { handleGeminiEmbedContent } from "./gemini-embeddings.js";
13
14
  import { handleBedrock, handleBedrockStream } from "./bedrock.js";
14
15
  import { handleConverse, handleConverseStream } from "./bedrock-converse.js";
15
16
  import { handleGeminiInteractions, resetEventIdCounter, resetInteractionCounter } from "./gemini-interactions.js";
16
17
  import { handleEmbeddings } from "./embeddings.js";
17
- import { handleImages } from "./images.js";
18
- import { handleSpeech } from "./speech.js";
19
18
  import { handleTranscription } from "./transcription.js";
19
+ import { handleImageEdit, handleImageVariations, handleImages } from "./images.js";
20
+ import { handleSpeech } from "./speech.js";
20
21
  import { VideoStateMap, handleVideoCreate, handleVideoStatus } from "./video.js";
21
- import { handleElevenLabsAudio } from "./elevenlabs-audio.js";
22
+ import { handleElevenLabsAudio, handleElevenLabsTTS } from "./elevenlabs-audio.js";
22
23
  import { falQueueStates, handleFal } from "./fal.js";
23
24
  import { falJobs, handleFalQueue } from "./fal-audio.js";
24
- import { handleOllama, handleOllamaGenerate } from "./ollama.js";
25
- import { handleCohere } from "./cohere.js";
25
+ import { handleOllama, handleOllamaEmbeddings, handleOllamaGenerate } from "./ollama.js";
26
+ import { handleCohere, handleCohereEmbed } from "./cohere.js";
26
27
  import { handleSearch } from "./search.js";
27
28
  import { handleRerank } from "./rerank.js";
28
29
  import { handleModeration } from "./moderation.js";
@@ -41,16 +42,21 @@ const GEMINI_LIVE_PATH = "/ws/google.ai.generativelanguage.v1beta.GenerativeServ
41
42
  const MESSAGES_PATH = "/v1/messages";
42
43
  const EMBEDDINGS_PATH = "/v1/embeddings";
43
44
  const COHERE_CHAT_PATH = "/v2/chat";
45
+ const COHERE_EMBED_PATH = "/v2/embed";
44
46
  const SEARCH_PATH = "/search";
45
47
  const RERANK_PATH = "/v2/rerank";
46
48
  const MODERATIONS_PATH = "/v1/moderations";
47
49
  const IMAGES_PATH = "/v1/images/generations";
50
+ const IMAGES_EDIT_PATH = "/v1/images/edit";
51
+ const IMAGES_VARIATIONS_PATH = "/v1/images/variations";
48
52
  const SPEECH_PATH = "/v1/audio/speech";
49
53
  const TRANSCRIPTIONS_PATH = "/v1/audio/transcriptions";
54
+ const TRANSLATIONS_PATH = "/v1/audio/translations";
50
55
  const VIDEOS_PATH = "/v1/videos";
51
56
  const VIDEOS_STATUS_RE = /^\/v1\/videos\/([^/]+)$/;
52
57
  const GEMINI_PREDICT_RE = /^\/v1beta\/models\/([^:]+):predict$/;
53
58
  const ELEVENLABS_SOUND_GENERATION_PATH = "/v1/sound-generation";
59
+ const ELEVENLABS_TTS_RE = /^\/v1\/text-to-speech\/([^/]+)$/;
54
60
  const ELEVENLABS_MUSIC_RE = /^\/v1\/music(?:\/(.+))?$/;
55
61
  const FAL_QUEUE_SUBMIT_RE = /^\/fal\/queue\/submit\/(.+)$/;
56
62
  const FAL_QUEUE_REQUESTS_RE = /^\/fal\/queue\/requests\/(.+)$/;
@@ -63,7 +69,10 @@ const COMPAT_SUFFIXES = [
63
69
  "/responses",
64
70
  "/audio/speech",
65
71
  "/audio/transcriptions",
66
- "/images/generations"
72
+ "/audio/translations",
73
+ "/images/generations",
74
+ "/images/edit",
75
+ "/images/variations"
67
76
  ];
68
77
  /**
69
78
  * Normalize OpenAI-compatible paths with arbitrary prefixes.
@@ -83,6 +92,7 @@ function normalizeCompatPath(pathname, logger) {
83
92
  }
84
93
  const GEMINI_INTERACTIONS_PATH = "/v1beta/interactions";
85
94
  const GEMINI_PATH_RE = /^\/v1beta\/models\/([^:]+):(generateContent|streamGenerateContent)$/;
95
+ const GEMINI_EMBED_RE = /^\/v1beta\/models\/([^:]+):embedContent$/;
86
96
  const AZURE_DEPLOYMENT_RE = /^\/openai\/deployments\/([^/]+)\/(chat\/completions|embeddings)$/;
87
97
  const BEDROCK_INVOKE_RE = /^\/model\/([^/]+)\/invoke$/;
88
98
  const BEDROCK_STREAM_RE = /^\/model\/([^/]+)\/invoke-with-response-stream$/;
@@ -91,6 +101,7 @@ const BEDROCK_CONVERSE_STREAM_RE = /^\/model\/([^/]+)\/converse-stream$/;
91
101
  const VERTEX_AI_RE = /^\/v1\/projects\/[^/]+\/locations\/[^/]+\/publishers\/google\/models\/([^/:]+):(generateContent|streamGenerateContent)$/;
92
102
  const OLLAMA_CHAT_PATH = "/api/chat";
93
103
  const OLLAMA_GENERATE_PATH = "/api/generate";
104
+ const OLLAMA_EMBEDDINGS_PATH = "/api/embeddings";
94
105
  const OLLAMA_TAGS_PATH = "/api/tags";
95
106
  const HEALTH_PATH = "/health";
96
107
  const READY_PATH = "/ready";
@@ -168,7 +179,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
168
179
  res.end(JSON.stringify({ error: "Missing or invalid \"fixtures\" array" }));
169
180
  return true;
170
181
  }
171
- const converted = parsed.fixtures.map(entryToFixture);
182
+ const converted = parsed.fixtures.map((e) => entryToFixture(e));
172
183
  const errors = validateFixtures(converted).filter((i) => i.severity === "error");
173
184
  if (errors.length > 0) {
174
185
  res.writeHead(400, { "Content-Type": "application/json" });
@@ -433,6 +444,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
433
444
  const response = await resolveResponse(fixture, body);
434
445
  const latency = fixture.latency ?? defaults.latency;
435
446
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
447
+ const includeUsage = body.stream === true && body.stream_options?.include_usage === true;
436
448
  if (isErrorResponse(response)) {
437
449
  const status = response.status ?? 500;
438
450
  journal.add({
@@ -445,7 +457,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
445
457
  fixture
446
458
  }
447
459
  });
448
- writeErrorResponse(res, status, serializeErrorResponse(response));
460
+ writeErrorResponse(res, status, serializeErrorResponse(response), { retryAfter: response.retryAfter });
449
461
  return;
450
462
  }
451
463
  if (isAudioResponse(response)) {
@@ -479,17 +491,30 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
479
491
  }
480
492
  });
481
493
  if (body.stream !== true) {
482
- const completion = buildContentWithToolCallsCompletion(response.content, response.toolCalls, body.model, response.reasoning, overrides);
494
+ const completion = buildContentWithToolCallsCompletion(response.content, response.toolCalls, body.model, response.reasoning, overrides, body.messages);
483
495
  res.writeHead(200, { "Content-Type": "application/json" });
484
496
  res.end(JSON.stringify(completion));
485
497
  } else {
486
498
  const chunks = buildContentWithToolCallsChunks(response.content, response.toolCalls, body.model, chunkSize, response.reasoning, overrides);
499
+ const completionText = response.content + response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
500
+ const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
501
+ prompt_tokens: overrides.usage.prompt_tokens ?? 0,
502
+ completion_tokens: overrides.usage.completion_tokens ?? 0,
503
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
504
+ } : {
505
+ prompt_tokens: estimatePromptTokens(body.messages),
506
+ completion_tokens: estimateTokens(completionText),
507
+ total_tokens: estimatePromptTokens(body.messages) + estimateTokens(completionText)
508
+ }, overrides?.systemFingerprint) : void 0;
487
509
  const interruption = createInterruptionSignal(fixture);
488
510
  if (!await writeSSEStream(res, chunks, {
489
511
  latency,
490
512
  streamingProfile: fixture.streamingProfile,
491
513
  signal: interruption?.signal,
492
- onChunkSent: interruption?.tick
514
+ onChunkSent: interruption?.tick,
515
+ usageChunk,
516
+ recordedTimings: fixture.recordedTimings,
517
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
493
518
  })) {
494
519
  if (!res.writableEnded) res.destroy();
495
520
  journalEntry.response.interrupted = true;
@@ -513,17 +538,29 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
513
538
  }
514
539
  });
515
540
  if (body.stream !== true) {
516
- const completion = buildTextCompletion(response.content, body.model, response.reasoning, overrides);
541
+ const completion = buildTextCompletion(response.content, body.model, response.reasoning, overrides, body.messages);
517
542
  res.writeHead(200, { "Content-Type": "application/json" });
518
543
  res.end(JSON.stringify(completion));
519
544
  } else {
520
545
  const chunks = buildTextChunks(response.content, body.model, chunkSize, response.reasoning, overrides);
546
+ const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
547
+ prompt_tokens: overrides.usage.prompt_tokens ?? 0,
548
+ completion_tokens: overrides.usage.completion_tokens ?? 0,
549
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
550
+ } : {
551
+ prompt_tokens: estimatePromptTokens(body.messages),
552
+ completion_tokens: estimateTokens(response.content),
553
+ total_tokens: estimatePromptTokens(body.messages) + estimateTokens(response.content)
554
+ }, overrides?.systemFingerprint) : void 0;
521
555
  const interruption = createInterruptionSignal(fixture);
522
556
  if (!await writeSSEStream(res, chunks, {
523
557
  latency,
524
558
  streamingProfile: fixture.streamingProfile,
525
559
  signal: interruption?.signal,
526
- onChunkSent: interruption?.tick
560
+ onChunkSent: interruption?.tick,
561
+ usageChunk,
562
+ recordedTimings: fixture.recordedTimings,
563
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
527
564
  })) {
528
565
  if (!res.writableEnded) res.destroy();
529
566
  journalEntry.response.interrupted = true;
@@ -547,17 +584,30 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
547
584
  }
548
585
  });
549
586
  if (body.stream !== true) {
550
- const completion = buildToolCallCompletion(response.toolCalls, body.model, overrides);
587
+ const completion = buildToolCallCompletion(response.toolCalls, body.model, overrides, body.messages);
551
588
  res.writeHead(200, { "Content-Type": "application/json" });
552
589
  res.end(JSON.stringify(completion));
553
590
  } else {
554
591
  const chunks = buildToolCallChunks(response.toolCalls, body.model, chunkSize, overrides);
592
+ const completionText = response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
593
+ const usageChunk = includeUsage ? buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
594
+ prompt_tokens: overrides.usage.prompt_tokens ?? 0,
595
+ completion_tokens: overrides.usage.completion_tokens ?? 0,
596
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
597
+ } : {
598
+ prompt_tokens: estimatePromptTokens(body.messages),
599
+ completion_tokens: estimateTokens(completionText),
600
+ total_tokens: estimatePromptTokens(body.messages) + estimateTokens(completionText)
601
+ }, overrides?.systemFingerprint) : void 0;
555
602
  const interruption = createInterruptionSignal(fixture);
556
603
  if (!await writeSSEStream(res, chunks, {
557
604
  latency,
558
605
  streamingProfile: fixture.streamingProfile,
559
606
  signal: interruption?.signal,
560
- onChunkSent: interruption?.tick
607
+ onChunkSent: interruption?.tick,
608
+ usageChunk,
609
+ recordedTimings: fixture.recordedTimings,
610
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
561
611
  })) {
562
612
  if (!res.writableEnded) res.destroy();
563
613
  journalEntry.response.interrupted = true;
@@ -591,6 +641,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
591
641
  const defaults = {
592
642
  latency: serverOptions.latency ?? 0,
593
643
  chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),
644
+ replaySpeed: serverOptions.replaySpeed ?? 1,
594
645
  logger,
595
646
  get chaos() {
596
647
  return serverOptions.chaos;
@@ -686,6 +737,61 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
686
737
  if (await handler.handleRequest(req, res, subPath)) return;
687
738
  }
688
739
  }
740
+ if (pathname === OLLAMA_CHAT_PATH && req.method === "POST") {
741
+ try {
742
+ await handleOllama(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
743
+ } catch (err) {
744
+ const msg = err instanceof Error ? err.message : "Internal error";
745
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
746
+ message: msg,
747
+ type: "server_error"
748
+ } }));
749
+ else if (!res.writableEnded) res.destroy();
750
+ }
751
+ return;
752
+ }
753
+ if (pathname === OLLAMA_GENERATE_PATH && req.method === "POST") {
754
+ try {
755
+ await handleOllamaGenerate(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
756
+ } catch (err) {
757
+ const msg = err instanceof Error ? err.message : "Internal error";
758
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
759
+ message: msg,
760
+ type: "server_error"
761
+ } }));
762
+ else if (!res.writableEnded) res.destroy();
763
+ }
764
+ return;
765
+ }
766
+ if (pathname === OLLAMA_EMBEDDINGS_PATH && req.method === "POST") {
767
+ try {
768
+ await handleOllamaEmbeddings(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
769
+ } catch (err) {
770
+ const msg = err instanceof Error ? err.message : "Internal error";
771
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
772
+ message: msg,
773
+ type: "server_error"
774
+ } }));
775
+ else if (!res.writableEnded) res.destroy();
776
+ }
777
+ return;
778
+ }
779
+ if (pathname === OLLAMA_TAGS_PATH && req.method === "GET") {
780
+ setCorsHeaders(res);
781
+ const modelIds = /* @__PURE__ */ new Set();
782
+ for (const f of fixtures) if (f.match.model && typeof f.match.model === "string") modelIds.add(f.match.model);
783
+ const models = (modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS).map((name) => ({
784
+ name,
785
+ model: name,
786
+ modified_at: (/* @__PURE__ */ new Date()).toISOString(),
787
+ size: 0,
788
+ digest: "",
789
+ details: {}
790
+ }));
791
+ res.writeHead(200, { "Content-Type": "application/json" });
792
+ res.end(JSON.stringify({ models }));
793
+ return;
794
+ }
689
795
  let azureDeploymentId;
690
796
  const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);
691
797
  if (azureMatch && req.method === "POST") {
@@ -832,6 +938,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
832
938
  }
833
939
  return;
834
940
  }
941
+ if (pathname === COHERE_EMBED_PATH && req.method === "POST") {
942
+ try {
943
+ await handleCohereEmbed(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
944
+ } catch (err) {
945
+ const msg = err instanceof Error ? err.message : "Internal error";
946
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
947
+ message: msg,
948
+ type: "server_error"
949
+ } }));
950
+ else if (!res.writableEnded) res.destroy();
951
+ }
952
+ return;
953
+ }
835
954
  if (pathname === EMBEDDINGS_PATH && req.method === "POST") {
836
955
  try {
837
956
  const deploymentId = azureDeploymentId;
@@ -870,6 +989,32 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
870
989
  }
871
990
  return;
872
991
  }
992
+ if (pathname === IMAGES_EDIT_PATH && req.method === "POST") {
993
+ try {
994
+ await handleImageEdit(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
995
+ } catch (err) {
996
+ const msg = err instanceof Error ? err.message : "Internal error";
997
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
998
+ message: msg,
999
+ type: "server_error"
1000
+ } }));
1001
+ else if (!res.writableEnded) res.destroy();
1002
+ }
1003
+ return;
1004
+ }
1005
+ if (pathname === IMAGES_VARIATIONS_PATH && req.method === "POST") {
1006
+ try {
1007
+ await handleImageVariations(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
1008
+ } catch (err) {
1009
+ const msg = err instanceof Error ? err.message : "Internal error";
1010
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
1011
+ message: msg,
1012
+ type: "server_error"
1013
+ } }));
1014
+ else if (!res.writableEnded) res.destroy();
1015
+ }
1016
+ return;
1017
+ }
873
1018
  if (pathname === SPEECH_PATH && req.method === "POST") {
874
1019
  try {
875
1020
  await handleSpeech(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
@@ -896,6 +1041,19 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
896
1041
  }
897
1042
  return;
898
1043
  }
1044
+ if (pathname === TRANSLATIONS_PATH && req.method === "POST") {
1045
+ try {
1046
+ await handleTranscription(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders, "translation");
1047
+ } catch (err) {
1048
+ const msg = err instanceof Error ? err.message : "Internal error";
1049
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
1050
+ message: msg,
1051
+ type: "server_error"
1052
+ } }));
1053
+ else if (!res.writableEnded) res.destroy();
1054
+ }
1055
+ return;
1056
+ }
899
1057
  if (pathname === VIDEOS_PATH && req.method === "POST") {
900
1058
  try {
901
1059
  await handleVideoCreate(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders, videoStates);
@@ -948,6 +1106,21 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
948
1106
  }
949
1107
  return;
950
1108
  }
1109
+ const geminiEmbedMatch = pathname.match(GEMINI_EMBED_RE);
1110
+ if (geminiEmbedMatch && req.method === "POST") {
1111
+ const embedModel = geminiEmbedMatch[1];
1112
+ try {
1113
+ await handleGeminiEmbedContent(req, res, await readBody(req), embedModel, fixtures, journal, defaults, setCorsHeaders);
1114
+ } catch (err) {
1115
+ const msg = err instanceof Error ? err.message : "Internal error";
1116
+ if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
1117
+ message: msg,
1118
+ type: "server_error"
1119
+ } }));
1120
+ else if (!res.writableEnded) res.destroy();
1121
+ }
1122
+ return;
1123
+ }
951
1124
  const geminiMatch = pathname.match(GEMINI_PATH_RE);
952
1125
  if (geminiMatch && req.method === "POST") {
953
1126
  const geminiModel = geminiMatch[1];
@@ -1050,22 +1223,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1050
1223
  }
1051
1224
  return;
1052
1225
  }
1053
- if (pathname === OLLAMA_CHAT_PATH && req.method === "POST") {
1054
- try {
1055
- await handleOllama(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
1056
- } catch (err) {
1057
- const msg = err instanceof Error ? err.message : "Internal error";
1058
- if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
1059
- message: msg,
1060
- type: "server_error"
1061
- } }));
1062
- else if (!res.writableEnded) res.destroy();
1063
- }
1064
- return;
1065
- }
1066
- if (pathname === OLLAMA_GENERATE_PATH && req.method === "POST") {
1226
+ if (pathname === SEARCH_PATH && req.method === "POST") {
1067
1227
  try {
1068
- await handleOllamaGenerate(req, res, await readBody(req), fixtures, journal, defaults, setCorsHeaders);
1228
+ await handleSearch(req, res, await readBody(req), serviceFixtures?.search ?? [], journal, defaults, setCorsHeaders);
1069
1229
  } catch (err) {
1070
1230
  const msg = err instanceof Error ? err.message : "Internal error";
1071
1231
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1076,25 +1236,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1076
1236
  }
1077
1237
  return;
1078
1238
  }
1079
- if (pathname === OLLAMA_TAGS_PATH && req.method === "GET") {
1080
- setCorsHeaders(res);
1081
- const modelIds = /* @__PURE__ */ new Set();
1082
- for (const f of fixtures) if (f.match.model && typeof f.match.model === "string") modelIds.add(f.match.model);
1083
- const models = (modelIds.size > 0 ? [...modelIds] : DEFAULT_MODELS).map((name) => ({
1084
- name,
1085
- model: name,
1086
- modified_at: (/* @__PURE__ */ new Date()).toISOString(),
1087
- size: 0,
1088
- digest: "",
1089
- details: {}
1090
- }));
1091
- res.writeHead(200, { "Content-Type": "application/json" });
1092
- res.end(JSON.stringify({ models }));
1093
- return;
1094
- }
1095
- if (pathname === SEARCH_PATH && req.method === "POST") {
1239
+ if (pathname === RERANK_PATH && req.method === "POST") {
1096
1240
  try {
1097
- await handleSearch(req, res, await readBody(req), serviceFixtures?.search ?? [], journal, defaults, setCorsHeaders);
1241
+ await handleRerank(req, res, await readBody(req), serviceFixtures?.rerank ?? [], journal, defaults, setCorsHeaders);
1098
1242
  } catch (err) {
1099
1243
  const msg = err instanceof Error ? err.message : "Internal error";
1100
1244
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1105,9 +1249,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1105
1249
  }
1106
1250
  return;
1107
1251
  }
1108
- if (pathname === RERANK_PATH && req.method === "POST") {
1252
+ if (pathname === MODERATIONS_PATH && req.method === "POST") {
1109
1253
  try {
1110
- await handleRerank(req, res, await readBody(req), serviceFixtures?.rerank ?? [], journal, defaults, setCorsHeaders);
1254
+ await handleModeration(req, res, await readBody(req), serviceFixtures?.moderation ?? [], journal, defaults, setCorsHeaders);
1111
1255
  } catch (err) {
1112
1256
  const msg = err instanceof Error ? err.message : "Internal error";
1113
1257
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1118,9 +1262,10 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1118
1262
  }
1119
1263
  return;
1120
1264
  }
1121
- if (pathname === MODERATIONS_PATH && req.method === "POST") {
1265
+ if (pathname === ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
1266
+ setCorsHeaders(res);
1122
1267
  try {
1123
- await handleModeration(req, res, await readBody(req), serviceFixtures?.moderation ?? [], journal, defaults, setCorsHeaders);
1268
+ await handleElevenLabsAudio(req, res, await readBody(req), fixtures, defaults, journal, "sound-generation");
1124
1269
  } catch (err) {
1125
1270
  const msg = err instanceof Error ? err.message : "Internal error";
1126
1271
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {
@@ -1131,10 +1276,12 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1131
1276
  }
1132
1277
  return;
1133
1278
  }
1134
- if (pathname === ELEVENLABS_SOUND_GENERATION_PATH && req.method === "POST") {
1279
+ const elevenLabsTTSMatch = pathname.match(ELEVENLABS_TTS_RE);
1280
+ if (elevenLabsTTSMatch && req.method === "POST") {
1135
1281
  setCorsHeaders(res);
1282
+ const voiceId = elevenLabsTTSMatch[1];
1136
1283
  try {
1137
- await handleElevenLabsAudio(req, res, await readBody(req), fixtures, defaults, journal, "sound-generation");
1284
+ await handleElevenLabsTTS(req, res, await readBody(req), fixtures, defaults, journal, voiceId);
1138
1285
  } catch (err) {
1139
1286
  const msg = err instanceof Error ? err.message : "Internal error";
1140
1287
  if (!res.headersSent) writeErrorResponse(res, 500, JSON.stringify({ error: {