@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/helpers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AudioResponse, ChatCompletion, ContentWithToolCallsResponse, EmbeddingResponse, ErrorResponse, FixtureResponse, ImageResponse, ResponseOverrides, SSEChunk, TextResponse, ToolCall, ToolCallResponse, TranscriptionResponse, VideoResponse } from "./types.js";
1
+ import { AudioResponse, ChatCompletion, ChatCompletionRequest, ContentWithToolCallsResponse, EmbeddingResponse, ErrorResponse, FixtureResponse, ImageResponse, ResponseOverrides, SSEChunk, TextResponse, ToolCall, ToolCallResponse, TranscriptionResponse, VideoResponse } from "./types.js";
2
2
  import * as http$1 from "node:http";
3
3
 
4
4
  //#region src/helpers.d.ts
@@ -34,12 +34,17 @@ declare function formatToMime(format: string): string;
34
34
  declare function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse;
35
35
  declare function isVideoResponse(r: FixtureResponse): r is VideoResponse;
36
36
  declare function extractOverrides(response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse): ResponseOverrides;
37
+ /**
38
+ * Rough token count estimation based on character length.
39
+ * Uses the ~4 characters per token heuristic common for English text.
40
+ */
41
+
37
42
  declare function buildTextChunks(content: string, model: string, chunkSize: number, reasoning?: string, overrides?: ResponseOverrides): SSEChunk[];
38
43
  declare function buildToolCallChunks(toolCalls: ToolCall[], model: string, chunkSize: number, overrides?: ResponseOverrides): SSEChunk[];
39
- declare function buildTextCompletion(content: string, model: string, reasoning?: string, overrides?: ResponseOverrides): ChatCompletion;
40
- declare function buildToolCallCompletion(toolCalls: ToolCall[], model: string, overrides?: ResponseOverrides): ChatCompletion;
44
+ declare function buildTextCompletion(content: string, model: string, reasoning?: string, overrides?: ResponseOverrides, requestMessages?: ChatCompletionRequest["messages"]): ChatCompletion;
45
+ declare function buildToolCallCompletion(toolCalls: ToolCall[], model: string, overrides?: ResponseOverrides, requestMessages?: ChatCompletionRequest["messages"]): ChatCompletion;
41
46
  declare function buildContentWithToolCallsChunks(content: string, toolCalls: ToolCall[], model: string, chunkSize: number, reasoning?: string, overrides?: ResponseOverrides): SSEChunk[];
42
- declare function buildContentWithToolCallsCompletion(content: string, toolCalls: ToolCall[], model: string, reasoning?: string, overrides?: ResponseOverrides): ChatCompletion;
47
+ declare function buildContentWithToolCallsCompletion(content: string, toolCalls: ToolCall[], model: string, reasoning?: string, overrides?: ResponseOverrides, requestMessages?: ChatCompletionRequest["messages"]): ChatCompletion;
43
48
  /**
44
49
  * Generate a deterministic embedding vector from input text.
45
50
  * Hashes the input with SHA-256 and spreads the hash bytes across
@@ -64,7 +69,6 @@ interface EmbeddingAPIResponse {
64
69
  */
65
70
  declare function buildEmbeddingResponse(embeddings: number[][], model: string): EmbeddingAPIResponse;
66
71
  //# sourceMappingURL=helpers.d.ts.map
67
-
68
72
  //#endregion
69
73
  export { EmbeddingAPIResponse, FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse };
70
74
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/helpers.js CHANGED
@@ -154,6 +154,62 @@ function extractOverrides(response) {
154
154
  ...r.role !== void 0 && { role: r.role }
155
155
  };
156
156
  }
157
+ /**
158
+ * Rough token count estimation based on character length.
159
+ * Uses the ~4 characters per token heuristic common for English text.
160
+ */
161
+ function estimateTokens(text) {
162
+ return Math.max(1, Math.ceil(text.length / 4));
163
+ }
164
+ /**
165
+ * Estimate prompt tokens from a request's messages array.
166
+ */
167
+ function estimatePromptTokens(messages) {
168
+ let totalChars = 0;
169
+ for (const msg of messages) if (typeof msg.content === "string") totalChars += msg.content.length;
170
+ else if (Array.isArray(msg.content)) {
171
+ for (const part of msg.content) if (part.text) totalChars += part.text.length;
172
+ }
173
+ return Math.max(1, Math.ceil(totalChars / 4));
174
+ }
175
+ /**
176
+ * Build usage object: use explicit overrides if provided, otherwise estimate.
177
+ */
178
+ function resolveUsage(overrides, promptText, completionText) {
179
+ if (overrides?.usage) {
180
+ const u = overrides.usage;
181
+ const prompt = u.prompt_tokens ?? 0;
182
+ const completion = u.completion_tokens ?? 0;
183
+ return {
184
+ prompt_tokens: prompt,
185
+ completion_tokens: completion,
186
+ total_tokens: u.total_tokens ?? prompt + completion
187
+ };
188
+ }
189
+ const prompt = estimateTokens(promptText || "x");
190
+ const completion = estimateTokens(completionText || "x");
191
+ return {
192
+ prompt_tokens: prompt,
193
+ completion_tokens: completion,
194
+ total_tokens: prompt + completion
195
+ };
196
+ }
197
+ /**
198
+ * Build an SSE usage chunk for streaming responses.
199
+ * OpenAI emits this as the final chunk before [DONE] when
200
+ * stream_options.include_usage is true. It has an empty choices array.
201
+ */
202
+ function buildUsageChunk(id, model, created, usage, fingerprint) {
203
+ return {
204
+ id,
205
+ object: "chat.completion.chunk",
206
+ created,
207
+ model,
208
+ choices: [],
209
+ usage,
210
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
211
+ };
212
+ }
157
213
  function buildTextChunks(content, model, chunkSize, reasoning, overrides) {
158
214
  const id = overrides?.id ?? generateId();
159
215
  const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
@@ -305,12 +361,8 @@ function buildToolCallChunks(toolCalls, model, chunkSize, overrides) {
305
361
  });
306
362
  return chunks;
307
363
  }
308
- function buildTextCompletion(content, model, reasoning, overrides) {
309
- const defaultUsage = {
310
- prompt_tokens: 0,
311
- completion_tokens: 0,
312
- total_tokens: 0
313
- };
364
+ function buildTextCompletion(content, model, reasoning, overrides, requestMessages) {
365
+ const promptText = requestMessages ? requestMessages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("") : "";
314
366
  return {
315
367
  id: overrides?.id ?? generateId(),
316
368
  object: "chat.completion",
@@ -327,20 +379,13 @@ function buildTextCompletion(content, model, reasoning, overrides) {
327
379
  logprobs: null,
328
380
  finish_reason: overrides?.finishReason ?? "stop"
329
381
  }],
330
- usage: overrides?.usage ? {
331
- prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
332
- completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
333
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
334
- } : defaultUsage,
382
+ usage: resolveUsage(overrides, promptText, content),
335
383
  ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
336
384
  };
337
385
  }
338
- function buildToolCallCompletion(toolCalls, model, overrides) {
339
- const defaultUsage = {
340
- prompt_tokens: 0,
341
- completion_tokens: 0,
342
- total_tokens: 0
343
- };
386
+ function buildToolCallCompletion(toolCalls, model, overrides, requestMessages) {
387
+ const promptText = requestMessages ? requestMessages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("") : "";
388
+ const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join("");
344
389
  return {
345
390
  id: overrides?.id ?? generateId(),
346
391
  object: "chat.completion",
@@ -364,11 +409,7 @@ function buildToolCallCompletion(toolCalls, model, overrides) {
364
409
  logprobs: null,
365
410
  finish_reason: overrides?.finishReason ?? "tool_calls"
366
411
  }],
367
- usage: overrides?.usage ? {
368
- prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
369
- completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
370
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
371
- } : defaultUsage,
412
+ usage: resolveUsage(overrides, promptText, completionText),
372
413
  ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
373
414
  };
374
415
  }
@@ -486,12 +527,9 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, r
486
527
  });
487
528
  return chunks;
488
529
  }
489
- function buildContentWithToolCallsCompletion(content, toolCalls, model, reasoning, overrides) {
490
- const defaultUsage = {
491
- prompt_tokens: 0,
492
- completion_tokens: 0,
493
- total_tokens: 0
494
- };
530
+ function buildContentWithToolCallsCompletion(content, toolCalls, model, reasoning, overrides, requestMessages) {
531
+ const promptText = requestMessages ? requestMessages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("") : "";
532
+ const completionText = content + toolCalls.map((tc) => tc.name + tc.arguments).join("");
495
533
  return {
496
534
  id: overrides?.id ?? generateId(),
497
535
  object: "chat.completion",
@@ -516,11 +554,7 @@ function buildContentWithToolCallsCompletion(content, toolCalls, model, reasonin
516
554
  logprobs: null,
517
555
  finish_reason: overrides?.finishReason ?? "tool_calls"
518
556
  }],
519
- usage: overrides?.usage ? {
520
- prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
521
- completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
522
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
523
- } : defaultUsage,
557
+ usage: resolveUsage(overrides, promptText, completionText),
524
558
  ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
525
559
  };
526
560
  }
@@ -624,5 +658,5 @@ function buildEmbeddingResponse(embeddings, model) {
624
658
  }
625
659
 
626
660
  //#endregion
627
- export { FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody, resolveResponse, resolveStrictMode, serializeErrorResponse, slugifyTestId, strictOverrideField };
661
+ export { FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, estimatePromptTokens, estimateTokens, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody, resolveResponse, resolveStrictMode, serializeErrorResponse, slugifyTestId, strictOverrideField };
628
662
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { DEFAULT_TEST_ID } from \"./constants.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\n/**\n * Resolve effective strict mode from per-request header and server default.\n * Header values override the server default — same precedence pattern as chaos\n * config headers (see resolveChaosConfig in chaos.ts).\n *\n * Header: `X-AIMock-Strict` — \"true\"/\"1\" → strict on, \"false\"/\"0\" → strict off.\n * When absent or unrecognised, falls back to the server-level default.\n */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n if (rawHeaders) {\n const header = rawHeaders[\"x-aimock-strict\"];\n const val = typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n if (val === \"true\" || val === \"1\") return true;\n if (val === \"false\" || val === \"0\") return false;\n }\n return serverDefault ?? false;\n}\n\n/**\n * Returns `true` or `false` when the X-AIMock-Strict header overrides the\n * server default, or `undefined` when it doesn't. Designed to be spread\n * directly into a journal entry's `response` object:\n *\n * response: { status, fixture, ...strictOverrideField(defaults.strict, req.headers) }\n */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n const effective = resolveStrictMode(serverDefault, rawHeaders);\n if (effective !== (serverDefault ?? false)) {\n return { strictOverride: effective };\n }\n return {};\n}\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n if (typeof fixture.response === \"function\") {\n try {\n const raw = await fixture.response(request);\n return normalizeFactoryResponse(raw);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Response factory threw: ${msg}`);\n }\n }\n return fixture.response;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return { ...tc };\n });\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\" &&\n \"message\" in ((r as ErrorResponse).error as Record<string, unknown>) &&\n typeof ((r as ErrorResponse).error as Record<string, unknown>).message === \"string\"\n );\n}\n\n/**\n * Serialize an ErrorResponse to JSON, stripping the internal-only `status`\n * field that controls the HTTP status code but should never appear in the\n * response body. Real LLM APIs don't include it.\n */\nexport function serializeErrorResponse(response: ErrorResponse): string {\n return JSON.stringify({\n error: {\n message: response.error.message,\n type: response.error.type ?? \"server_error\",\n param: response.error.param ?? null,\n code: response.error.code ?? null,\n },\n });\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: {}, logprobs: null, finish_reason: overrides?.finishReason ?? \"stop\" },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): ChatCompletion {\n const defaultUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: overrides?.usage\n ? {\n prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,\n completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,\n total_tokens:\n overrides.usage.total_tokens ??\n (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0),\n }\n : defaultUsage,\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB\n\nexport function readBody(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (totalBytes > maxBytes) {\n settled = true;\n req.destroy();\n reject(new Error(`Request body exceeded size limit of ${maxBytes} bytes`));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (!settled) {\n settled = true;\n resolve(Buffer.concat(chunks).toString());\n }\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n pattern.lastIndex = 0;\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n return DEFAULT_TEST_ID;\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { DEFAULT_TEST_ID } from \"./constants.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n ResponseOverrides,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\n\n/**\n * Resolve effective strict mode from per-request header and server default.\n * Header values override the server default — same precedence pattern as chaos\n * config headers (see resolveChaosConfig in chaos.ts).\n *\n * Header: `X-AIMock-Strict` — \"true\"/\"1\" → strict on, \"false\"/\"0\" → strict off.\n * When absent or unrecognised, falls back to the server-level default.\n */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n if (rawHeaders) {\n const header = rawHeaders[\"x-aimock-strict\"];\n const val = typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n if (val === \"true\" || val === \"1\") return true;\n if (val === \"false\" || val === \"0\") return false;\n }\n return serverDefault ?? false;\n}\n\n/**\n * Returns `true` or `false` when the X-AIMock-Strict header overrides the\n * server default, or `undefined` when it doesn't. Designed to be spread\n * directly into a journal entry's `response` object:\n *\n * response: { status, fixture, ...strictOverrideField(defaults.strict, req.headers) }\n */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n const effective = resolveStrictMode(serverDefault, rawHeaders);\n if (effective !== (serverDefault ?? false)) {\n return { strictOverride: effective };\n }\n return {};\n}\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (REDACTED_HEADERS.has(key.toLowerCase())) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n if (typeof fixture.response === \"function\") {\n try {\n const raw = await fixture.response(request);\n return normalizeFactoryResponse(raw);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Response factory threw: ${msg}`);\n }\n }\n return fixture.response;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return { ...tc };\n });\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n return (\n \"content\" in r &&\n typeof (r as ContentWithToolCallsResponse).content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray((r as ContentWithToolCallsResponse).toolCalls)\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\" &&\n \"message\" in ((r as ErrorResponse).error as Record<string, unknown>) &&\n typeof ((r as ErrorResponse).error as Record<string, unknown>).message === \"string\"\n );\n}\n\n/**\n * Serialize an ErrorResponse to JSON, stripping the internal-only `status`\n * field that controls the HTTP status code but should never appear in the\n * response body. Real LLM APIs don't include it.\n */\nexport function serializeErrorResponse(response: ErrorResponse): string {\n return JSON.stringify({\n error: {\n message: response.error.message,\n type: response.error.type ?? \"server_error\",\n param: response.error.param ?? null,\n code: response.error.code ?? null,\n },\n });\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n };\n}\n\n// ─── Token estimation ────────────────────────────────────────────────────\n\n/**\n * Rough token count estimation based on character length.\n * Uses the ~4 characters per token heuristic common for English text.\n */\nexport function estimateTokens(text: string): number {\n return Math.max(1, Math.ceil(text.length / 4));\n}\n\n/**\n * Estimate prompt tokens from a request's messages array.\n */\nexport function estimatePromptTokens(messages: ChatCompletionRequest[\"messages\"]): number {\n let totalChars = 0;\n for (const msg of messages) {\n if (typeof msg.content === \"string\") {\n totalChars += msg.content.length;\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.text) totalChars += part.text.length;\n }\n }\n }\n return Math.max(1, Math.ceil(totalChars / 4));\n}\n\n/**\n * Build usage object: use explicit overrides if provided, otherwise estimate.\n */\nfunction resolveUsage(\n overrides: ResponseOverrides | undefined,\n promptText: string,\n completionText: string,\n): { prompt_tokens: number; completion_tokens: number; total_tokens: number } {\n if (overrides?.usage) {\n const u = overrides.usage;\n const prompt = u.prompt_tokens ?? 0;\n const completion = u.completion_tokens ?? 0;\n return {\n prompt_tokens: prompt,\n completion_tokens: completion,\n total_tokens: u.total_tokens ?? prompt + completion,\n };\n }\n const prompt = estimateTokens(promptText || \"x\");\n const completion = estimateTokens(completionText || \"x\");\n return {\n prompt_tokens: prompt,\n completion_tokens: completion,\n total_tokens: prompt + completion,\n };\n}\n\n/**\n * Build an SSE usage chunk for streaming responses.\n * OpenAI emits this as the final chunk before [DONE] when\n * stream_options.include_usage is true. It has an empty choices array.\n */\nexport function buildUsageChunk(\n id: string,\n model: string,\n created: number,\n usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number },\n fingerprint?: string,\n): SSEChunk {\n return {\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [],\n usage,\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: {}, logprobs: null, finish_reason: overrides?.finishReason ?? \"stop\" },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: resolveUsage(overrides, promptText, content),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = content + toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB\n\nexport function readBody(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (totalBytes > maxBytes) {\n settled = true;\n req.destroy();\n reject(new Error(`Request body exceeded size limit of ${maxBytes} bytes`));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (!settled) {\n settled = true;\n resolve(Buffer.concat(chunks).toString());\n }\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n pattern.lastIndex = 0;\n return pattern.test(text);\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n const headerValue = req.headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams(url.slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n return DEFAULT_TEST_ID;\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n };\n}\n"],"mappings":"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"}