@copilotkit/aimock 1.13.0 → 1.14.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 (85) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -4
  3. package/README.md +6 -1
  4. package/dist/cli.cjs +1 -1
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +1 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config-loader.d.cts.map +1 -1
  9. package/dist/fixture-loader.cjs +131 -29
  10. package/dist/fixture-loader.cjs.map +1 -1
  11. package/dist/fixture-loader.d.cts +9 -2
  12. package/dist/fixture-loader.d.cts.map +1 -1
  13. package/dist/fixture-loader.d.ts +9 -2
  14. package/dist/fixture-loader.d.ts.map +1 -1
  15. package/dist/fixture-loader.js +132 -31
  16. package/dist/fixture-loader.js.map +1 -1
  17. package/dist/gemini.cjs +76 -55
  18. package/dist/gemini.cjs.map +1 -1
  19. package/dist/gemini.d.cts.map +1 -1
  20. package/dist/gemini.d.ts.map +1 -1
  21. package/dist/gemini.js +77 -56
  22. package/dist/gemini.js.map +1 -1
  23. package/dist/helpers.cjs +142 -76
  24. package/dist/helpers.cjs.map +1 -1
  25. package/dist/helpers.d.cts +14 -4
  26. package/dist/helpers.d.cts.map +1 -1
  27. package/dist/helpers.d.ts +14 -4
  28. package/dist/helpers.d.ts.map +1 -1
  29. package/dist/helpers.js +142 -77
  30. package/dist/helpers.js.map +1 -1
  31. package/dist/index.cjs +10 -0
  32. package/dist/index.d.cts +4 -4
  33. package/dist/index.d.ts +4 -4
  34. package/dist/index.js +3 -3
  35. package/dist/llmock.cjs +1 -1
  36. package/dist/llmock.cjs.map +1 -1
  37. package/dist/llmock.d.cts +6 -6
  38. package/dist/llmock.d.cts.map +1 -1
  39. package/dist/llmock.d.ts +6 -6
  40. package/dist/llmock.d.ts.map +1 -1
  41. package/dist/llmock.js +2 -2
  42. package/dist/llmock.js.map +1 -1
  43. package/dist/messages.cjs +69 -63
  44. package/dist/messages.cjs.map +1 -1
  45. package/dist/messages.d.cts.map +1 -1
  46. package/dist/messages.d.ts.map +1 -1
  47. package/dist/messages.js +70 -64
  48. package/dist/messages.js.map +1 -1
  49. package/dist/recorder.cjs +1 -1
  50. package/dist/recorder.cjs.map +1 -1
  51. package/dist/recorder.js +1 -1
  52. package/dist/recorder.js.map +1 -1
  53. package/dist/responses.cjs +66 -57
  54. package/dist/responses.cjs.map +1 -1
  55. package/dist/responses.d.cts +3 -3
  56. package/dist/responses.d.cts.map +1 -1
  57. package/dist/responses.d.ts +3 -3
  58. package/dist/responses.d.ts.map +1 -1
  59. package/dist/responses.js +67 -58
  60. package/dist/responses.js.map +1 -1
  61. package/dist/server.cjs +57 -30
  62. package/dist/server.cjs.map +1 -1
  63. package/dist/server.d.cts.map +1 -1
  64. package/dist/server.d.ts.map +1 -1
  65. package/dist/server.js +58 -31
  66. package/dist/server.js.map +1 -1
  67. package/dist/stream-collapse.cjs.map +1 -1
  68. package/dist/stream-collapse.d.cts.map +1 -1
  69. package/dist/stream-collapse.d.ts.map +1 -1
  70. package/dist/stream-collapse.js.map +1 -1
  71. package/dist/types.d.cts +64 -11
  72. package/dist/types.d.cts.map +1 -1
  73. package/dist/types.d.ts +64 -11
  74. package/dist/types.d.ts.map +1 -1
  75. package/fixtures/example-multi-turn.json +1 -1
  76. package/fixtures/example-tool-call.json +1 -1
  77. package/fixtures/examples/adk/gemini-agent.json +47 -0
  78. package/fixtures/examples/crewai/multi-agent-crew.json +16 -0
  79. package/fixtures/examples/langchain/agent-loop.json +27 -0
  80. package/fixtures/examples/llamaindex/aimock-config.json +62 -0
  81. package/fixtures/examples/llamaindex/rag-pipeline.json +34 -0
  82. package/fixtures/examples/mastra/agent-workflow.json +32 -0
  83. package/fixtures/examples/pydanticai/structured-output.json +15 -0
  84. package/package.json +2 -1
  85. package/skills/write-fixtures/SKILL.md +148 -22
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AudioResponse, EmbeddingResponse, FixtureResponse, ImageResponse, SSEChunk, ToolCall, TranscriptionResponse, VideoResponse } from "./types.js";
1
+ import { AudioResponse, ChatCompletion, ContentWithToolCallsResponse, EmbeddingResponse, ErrorResponse, FixtureResponse, ImageResponse, ResponseOverrides, SSEChunk, TextResponse, ToolCall, ToolCallResponse, TranscriptionResponse, VideoResponse } from "./types.js";
2
2
  import * as http from "node:http";
3
3
 
4
4
  //#region src/helpers.d.ts
@@ -7,13 +7,22 @@ declare function generateId(prefix?: string): string;
7
7
  declare function generateToolCallId(): string;
8
8
  declare function generateMessageId(): string;
9
9
  declare function generateToolUseId(): string;
10
+ declare function isTextResponse(r: FixtureResponse): r is TextResponse;
11
+ declare function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse;
12
+ declare function isContentWithToolCallsResponse(r: FixtureResponse): r is ContentWithToolCallsResponse;
13
+ declare function isErrorResponse(r: FixtureResponse): r is ErrorResponse;
10
14
  declare function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse;
11
15
  declare function isImageResponse(r: FixtureResponse): r is ImageResponse;
12
16
  declare function isAudioResponse(r: FixtureResponse): r is AudioResponse;
13
17
  declare function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse;
14
18
  declare function isVideoResponse(r: FixtureResponse): r is VideoResponse;
15
- declare function buildTextChunks(content: string, model: string, chunkSize: number, reasoning?: string): SSEChunk[];
16
- declare function buildToolCallChunks(toolCalls: ToolCall[], model: string, chunkSize: number): SSEChunk[];
19
+ declare function extractOverrides(response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse): ResponseOverrides;
20
+ declare function buildTextChunks(content: string, model: string, chunkSize: number, reasoning?: string, overrides?: ResponseOverrides): SSEChunk[];
21
+ declare function buildToolCallChunks(toolCalls: ToolCall[], model: string, chunkSize: number, overrides?: ResponseOverrides): SSEChunk[];
22
+ declare function buildTextCompletion(content: string, model: string, reasoning?: string, overrides?: ResponseOverrides): ChatCompletion;
23
+ declare function buildToolCallCompletion(toolCalls: ToolCall[], model: string, overrides?: ResponseOverrides): ChatCompletion;
24
+ declare function buildContentWithToolCallsChunks(content: string, toolCalls: ToolCall[], model: string, chunkSize: number, reasoning?: string, overrides?: ResponseOverrides): SSEChunk[];
25
+ declare function buildContentWithToolCallsCompletion(content: string, toolCalls: ToolCall[], model: string, reasoning?: string, overrides?: ResponseOverrides): ChatCompletion;
17
26
  /**
18
27
  * Generate a deterministic embedding vector from input text.
19
28
  * Hashes the input with SHA-256 and spreads the hash bytes across
@@ -38,6 +47,7 @@ interface EmbeddingAPIResponse {
38
47
  */
39
48
  declare function buildEmbeddingResponse(embeddings: number[][], model: string): EmbeddingAPIResponse;
40
49
  //# sourceMappingURL=helpers.d.ts.map
50
+
41
51
  //#endregion
42
- export { EmbeddingAPIResponse, buildEmbeddingResponse, buildTextChunks, buildToolCallChunks, flattenHeaders, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, isAudioResponse, isEmbeddingResponse, isImageResponse, isTranscriptionResponse, isVideoResponse };
52
+ export { EmbeddingAPIResponse, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse };
43
53
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":";;;;iBAoBgB,cAAA,UAAwB,IAAA,CAAK,sBAAsB;iBAanD,UAAA;AAbA,iBAiBA,kBAAA,CAAA,CAjBc,EAAA,MAAA;AAAA,iBAqBd,iBAAA,CAAA,CArBc,EAAA,MAAA;AAAU,iBAyBxB,iBAAA,CAAA,CAzB6B,EAAA,MAAA;AAqB7B,iBAmCA,mBAAA,CAnCiB,CAAA,EAmCM,eAnCN,CAAA,EAAA,CAAA,IAmC6B,iBAnC7B;AAIjB,iBAmCA,eAAA,CAnCiB,CAAA,EAmCE,eAnCF,CAAA,EAAA,CAAA,IAmCyB,aAnCzB;AA+BjB,iBAWA,eAAA,CAXmB,CAAA,EAWA,eAXA,CAAA,EAAA,CAAA,IAWuB,aAXvB;AAAA,iBAenB,uBAAA,CAfmB,CAAA,EAeQ,eAfR,CAAA,EAAA,CAAA,IAe+B,qBAf/B;AAAI,iBAuBvB,eAAA,CAvBuB,CAAA,EAuBJ,eAvBI,CAAA,EAAA,CAAA,IAuBmB,aAvBnB;AAAuB,iBA+B9C,eAAA,CA/B8C,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAAA,MAAA,CAAA,EAoC3D,QApC2D,EAAA;AAAiB,iBAwF/D,mBAAA,CAxF+D,SAAA,EAyFlE,QAzFkE,EAAA,EAAA,KAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,CAAA,EA4F5E,QA5F4E,EAAA;;;;AAe/E;;AAA2C,iBA8X3B,8BAAA,CA9X2B,KAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA,EAAA;AAAuB,UA8YjD,oBAAA,CA9YiD;EAAqB,MAAA,EAAA,MAAA;EAQvE,IAAA,EAAA;IAAe,MAAA,EAAA,WAAA;IAAI,KAAA,EAAA,MAAA;IAAuB,SAAA,EAAA,MAAA,EAAA;EAAa,CAAA,EAAA;EAQvD,KAAA,EAAA,MAAA;EAyDA,KAAA,EAAA;IAAmB,aAAA,EAAA,MAAA;IACtB,YAAA,EAAA,MAAA;;;AAoTb;AAgBA;AAUA;iBAAgB,sBAAA,yCAGb"}
1
+ {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/helpers.js CHANGED
@@ -28,10 +28,10 @@ function generateToolUseId() {
28
28
  return `toolu_${randomBytes(12).toString("base64url")}`;
29
29
  }
30
30
  function isTextResponse(r) {
31
- return "content" in r && typeof r.content === "string";
31
+ return "content" in r && typeof r.content === "string" && !("toolCalls" in r);
32
32
  }
33
33
  function isToolCallResponse(r) {
34
- return "toolCalls" in r && Array.isArray(r.toolCalls);
34
+ return "toolCalls" in r && Array.isArray(r.toolCalls) && !("content" in r && typeof r.content === "string");
35
35
  }
36
36
  function isContentWithToolCallsResponse(r) {
37
37
  return "content" in r && typeof r.content === "string" && "toolCalls" in r && Array.isArray(r.toolCalls);
@@ -54,37 +54,53 @@ function isTranscriptionResponse(r) {
54
54
  function isVideoResponse(r) {
55
55
  return "video" in r && r.video != null && typeof r.video === "object";
56
56
  }
57
- function buildTextChunks(content, model, chunkSize, reasoning) {
58
- const id = generateId();
59
- const created = Math.floor(Date.now() / 1e3);
57
+ function extractOverrides(response) {
58
+ const r = response;
59
+ return {
60
+ ...r.id !== void 0 && { id: r.id },
61
+ ...r.created !== void 0 && { created: r.created },
62
+ ...r.model !== void 0 && { model: r.model },
63
+ ...r.usage !== void 0 && { usage: r.usage },
64
+ ...r.systemFingerprint !== void 0 && { systemFingerprint: r.systemFingerprint },
65
+ ...r.finishReason !== void 0 && { finishReason: r.finishReason },
66
+ ...r.role !== void 0 && { role: r.role }
67
+ };
68
+ }
69
+ function buildTextChunks(content, model, chunkSize, reasoning, overrides) {
70
+ const id = overrides?.id ?? generateId();
71
+ const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
72
+ const effectiveModel = overrides?.model ?? model;
60
73
  const chunks = [];
74
+ const fingerprint = overrides?.systemFingerprint;
61
75
  if (reasoning) for (let i = 0; i < reasoning.length; i += chunkSize) {
62
76
  const slice = reasoning.slice(i, i + chunkSize);
63
77
  chunks.push({
64
78
  id,
65
79
  object: "chat.completion.chunk",
66
80
  created,
67
- model,
81
+ model: effectiveModel,
68
82
  choices: [{
69
83
  index: 0,
70
84
  delta: { reasoning_content: slice },
71
85
  finish_reason: null
72
- }]
86
+ }],
87
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
73
88
  });
74
89
  }
75
90
  chunks.push({
76
91
  id,
77
92
  object: "chat.completion.chunk",
78
93
  created,
79
- model,
94
+ model: effectiveModel,
80
95
  choices: [{
81
96
  index: 0,
82
97
  delta: {
83
- role: "assistant",
98
+ role: overrides?.role ?? "assistant",
84
99
  content: ""
85
100
  },
86
101
  finish_reason: null
87
- }]
102
+ }],
103
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
88
104
  });
89
105
  for (let i = 0; i < content.length; i += chunkSize) {
90
106
  const slice = content.slice(i, i + chunkSize);
@@ -92,44 +108,49 @@ function buildTextChunks(content, model, chunkSize, reasoning) {
92
108
  id,
93
109
  object: "chat.completion.chunk",
94
110
  created,
95
- model,
111
+ model: effectiveModel,
96
112
  choices: [{
97
113
  index: 0,
98
114
  delta: { content: slice },
99
115
  finish_reason: null
100
- }]
116
+ }],
117
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
101
118
  });
102
119
  }
103
120
  chunks.push({
104
121
  id,
105
122
  object: "chat.completion.chunk",
106
123
  created,
107
- model,
124
+ model: effectiveModel,
108
125
  choices: [{
109
126
  index: 0,
110
127
  delta: {},
111
- finish_reason: "stop"
112
- }]
128
+ finish_reason: overrides?.finishReason ?? "stop"
129
+ }],
130
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
113
131
  });
114
132
  return chunks;
115
133
  }
116
- function buildToolCallChunks(toolCalls, model, chunkSize) {
117
- const id = generateId();
118
- const created = Math.floor(Date.now() / 1e3);
134
+ function buildToolCallChunks(toolCalls, model, chunkSize, overrides) {
135
+ const id = overrides?.id ?? generateId();
136
+ const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
137
+ const effectiveModel = overrides?.model ?? model;
119
138
  const chunks = [];
139
+ const fingerprint = overrides?.systemFingerprint;
120
140
  chunks.push({
121
141
  id,
122
142
  object: "chat.completion.chunk",
123
143
  created,
124
- model,
144
+ model: effectiveModel,
125
145
  choices: [{
126
146
  index: 0,
127
147
  delta: {
128
- role: "assistant",
148
+ role: overrides?.role ?? "assistant",
129
149
  content: null
130
150
  },
131
151
  finish_reason: null
132
- }]
152
+ }],
153
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
133
154
  });
134
155
  for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {
135
156
  const tc = toolCalls[tcIdx];
@@ -138,7 +159,7 @@ function buildToolCallChunks(toolCalls, model, chunkSize) {
138
159
  id,
139
160
  object: "chat.completion.chunk",
140
161
  created,
141
- model,
162
+ model: effectiveModel,
142
163
  choices: [{
143
164
  index: 0,
144
165
  delta: { tool_calls: [{
@@ -151,7 +172,8 @@ function buildToolCallChunks(toolCalls, model, chunkSize) {
151
172
  }
152
173
  }] },
153
174
  finish_reason: null
154
- }]
175
+ }],
176
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
155
177
  });
156
178
  const args = tc.arguments;
157
179
  for (let i = 0; i < args.length; i += chunkSize) {
@@ -160,7 +182,7 @@ function buildToolCallChunks(toolCalls, model, chunkSize) {
160
182
  id,
161
183
  object: "chat.completion.chunk",
162
184
  created,
163
- model,
185
+ model: effectiveModel,
164
186
  choices: [{
165
187
  index: 0,
166
188
  delta: { tool_calls: [{
@@ -168,7 +190,8 @@ function buildToolCallChunks(toolCalls, model, chunkSize) {
168
190
  function: { arguments: slice }
169
191
  }] },
170
192
  finish_reason: null
171
- }]
193
+ }],
194
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
172
195
  });
173
196
  }
174
197
  }
@@ -176,48 +199,60 @@ function buildToolCallChunks(toolCalls, model, chunkSize) {
176
199
  id,
177
200
  object: "chat.completion.chunk",
178
201
  created,
179
- model,
202
+ model: effectiveModel,
180
203
  choices: [{
181
204
  index: 0,
182
205
  delta: {},
183
- finish_reason: "tool_calls"
184
- }]
206
+ finish_reason: overrides?.finishReason ?? "tool_calls"
207
+ }],
208
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
185
209
  });
186
210
  return chunks;
187
211
  }
188
- function buildTextCompletion(content, model, reasoning) {
212
+ function buildTextCompletion(content, model, reasoning, overrides) {
213
+ const defaultUsage = {
214
+ prompt_tokens: 0,
215
+ completion_tokens: 0,
216
+ total_tokens: 0
217
+ };
189
218
  return {
190
- id: generateId(),
219
+ id: overrides?.id ?? generateId(),
191
220
  object: "chat.completion",
192
- created: Math.floor(Date.now() / 1e3),
193
- model,
221
+ created: overrides?.created ?? Math.floor(Date.now() / 1e3),
222
+ model: overrides?.model ?? model,
194
223
  choices: [{
195
224
  index: 0,
196
225
  message: {
197
- role: "assistant",
226
+ role: overrides?.role ?? "assistant",
198
227
  content,
199
228
  refusal: null,
200
229
  ...reasoning ? { reasoning_content: reasoning } : {}
201
230
  },
202
- finish_reason: "stop"
231
+ finish_reason: overrides?.finishReason ?? "stop"
203
232
  }],
204
- usage: {
205
- prompt_tokens: 0,
206
- completion_tokens: 0,
207
- total_tokens: 0
208
- }
233
+ usage: overrides?.usage ? {
234
+ prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
235
+ completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
236
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
237
+ } : defaultUsage,
238
+ ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
209
239
  };
210
240
  }
211
- function buildToolCallCompletion(toolCalls, model) {
241
+ function buildToolCallCompletion(toolCalls, model, overrides) {
242
+ const defaultUsage = {
243
+ prompt_tokens: 0,
244
+ completion_tokens: 0,
245
+ total_tokens: 0
246
+ };
212
247
  return {
213
- id: generateId(),
248
+ id: overrides?.id ?? generateId(),
214
249
  object: "chat.completion",
215
- created: Math.floor(Date.now() / 1e3),
216
- model,
250
+ created: overrides?.created ?? Math.floor(Date.now() / 1e3),
251
+ model: overrides?.model ?? model,
217
252
  choices: [{
218
253
  index: 0,
219
254
  message: {
220
- role: "assistant",
255
+ role: overrides?.role ?? "assistant",
221
256
  content: null,
222
257
  refusal: null,
223
258
  tool_calls: toolCalls.map((tc) => ({
@@ -229,32 +264,51 @@ function buildToolCallCompletion(toolCalls, model) {
229
264
  }
230
265
  }))
231
266
  },
232
- finish_reason: "tool_calls"
267
+ finish_reason: overrides?.finishReason ?? "tool_calls"
233
268
  }],
234
- usage: {
235
- prompt_tokens: 0,
236
- completion_tokens: 0,
237
- total_tokens: 0
238
- }
269
+ usage: overrides?.usage ? {
270
+ prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
271
+ completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
272
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
273
+ } : defaultUsage,
274
+ ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
239
275
  };
240
276
  }
241
- function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
242
- const id = generateId();
243
- const created = Math.floor(Date.now() / 1e3);
277
+ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, reasoning, overrides) {
278
+ const id = overrides?.id ?? generateId();
279
+ const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
280
+ const effectiveModel = overrides?.model ?? model;
244
281
  const chunks = [];
282
+ const fingerprint = overrides?.systemFingerprint;
283
+ if (reasoning) for (let i = 0; i < reasoning.length; i += chunkSize) {
284
+ const slice = reasoning.slice(i, i + chunkSize);
285
+ chunks.push({
286
+ id,
287
+ object: "chat.completion.chunk",
288
+ created,
289
+ model: effectiveModel,
290
+ choices: [{
291
+ index: 0,
292
+ delta: { reasoning_content: slice },
293
+ finish_reason: null
294
+ }],
295
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
296
+ });
297
+ }
245
298
  chunks.push({
246
299
  id,
247
300
  object: "chat.completion.chunk",
248
301
  created,
249
- model,
302
+ model: effectiveModel,
250
303
  choices: [{
251
304
  index: 0,
252
305
  delta: {
253
- role: "assistant",
306
+ role: overrides?.role ?? "assistant",
254
307
  content: ""
255
308
  },
256
309
  finish_reason: null
257
- }]
310
+ }],
311
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
258
312
  });
259
313
  for (let i = 0; i < content.length; i += chunkSize) {
260
314
  const slice = content.slice(i, i + chunkSize);
@@ -262,12 +316,13 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
262
316
  id,
263
317
  object: "chat.completion.chunk",
264
318
  created,
265
- model,
319
+ model: effectiveModel,
266
320
  choices: [{
267
321
  index: 0,
268
322
  delta: { content: slice },
269
323
  finish_reason: null
270
- }]
324
+ }],
325
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
271
326
  });
272
327
  }
273
328
  for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {
@@ -277,7 +332,7 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
277
332
  id,
278
333
  object: "chat.completion.chunk",
279
334
  created,
280
- model,
335
+ model: effectiveModel,
281
336
  choices: [{
282
337
  index: 0,
283
338
  delta: { tool_calls: [{
@@ -290,7 +345,8 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
290
345
  }
291
346
  }] },
292
347
  finish_reason: null
293
- }]
348
+ }],
349
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
294
350
  });
295
351
  const args = tc.arguments;
296
352
  for (let i = 0; i < args.length; i += chunkSize) {
@@ -299,7 +355,7 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
299
355
  id,
300
356
  object: "chat.completion.chunk",
301
357
  created,
302
- model,
358
+ model: effectiveModel,
303
359
  choices: [{
304
360
  index: 0,
305
361
  delta: { tool_calls: [{
@@ -307,7 +363,8 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
307
363
  function: { arguments: slice }
308
364
  }] },
309
365
  finish_reason: null
310
- }]
366
+ }],
367
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
311
368
  });
312
369
  }
313
370
  }
@@ -315,27 +372,34 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize) {
315
372
  id,
316
373
  object: "chat.completion.chunk",
317
374
  created,
318
- model,
375
+ model: effectiveModel,
319
376
  choices: [{
320
377
  index: 0,
321
378
  delta: {},
322
- finish_reason: "tool_calls"
323
- }]
379
+ finish_reason: overrides?.finishReason ?? "tool_calls"
380
+ }],
381
+ ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
324
382
  });
325
383
  return chunks;
326
384
  }
327
- function buildContentWithToolCallsCompletion(content, toolCalls, model) {
385
+ function buildContentWithToolCallsCompletion(content, toolCalls, model, reasoning, overrides) {
386
+ const defaultUsage = {
387
+ prompt_tokens: 0,
388
+ completion_tokens: 0,
389
+ total_tokens: 0
390
+ };
328
391
  return {
329
- id: generateId(),
392
+ id: overrides?.id ?? generateId(),
330
393
  object: "chat.completion",
331
- created: Math.floor(Date.now() / 1e3),
332
- model,
394
+ created: overrides?.created ?? Math.floor(Date.now() / 1e3),
395
+ model: overrides?.model ?? model,
333
396
  choices: [{
334
397
  index: 0,
335
398
  message: {
336
- role: "assistant",
399
+ role: overrides?.role ?? "assistant",
337
400
  content,
338
401
  refusal: null,
402
+ ...reasoning ? { reasoning_content: reasoning } : {},
339
403
  tool_calls: toolCalls.map((tc) => ({
340
404
  id: tc.id || generateToolCallId(),
341
405
  type: "function",
@@ -345,13 +409,14 @@ function buildContentWithToolCallsCompletion(content, toolCalls, model) {
345
409
  }
346
410
  }))
347
411
  },
348
- finish_reason: "tool_calls"
412
+ finish_reason: overrides?.finishReason ?? "tool_calls"
349
413
  }],
350
- usage: {
351
- prompt_tokens: 0,
352
- completion_tokens: 0,
353
- total_tokens: 0
354
- }
414
+ usage: overrides?.usage ? {
415
+ prompt_tokens: overrides.usage.prompt_tokens ?? defaultUsage.prompt_tokens,
416
+ completion_tokens: overrides.usage.completion_tokens ?? defaultUsage.completion_tokens,
417
+ total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
418
+ } : defaultUsage,
419
+ ...overrides?.systemFingerprint !== void 0 && { system_fingerprint: overrides.systemFingerprint }
355
420
  };
356
421
  }
357
422
  function readBody(req) {
@@ -414,5 +479,5 @@ function buildEmbeddingResponse(embeddings, model) {
414
479
  }
415
480
 
416
481
  //#endregion
417
- export { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, flattenHeaders, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody };
482
+ export { buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, readBody };
418
483
  //# 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 {\n FixtureResponse,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n SSEChunk,\n ToolCall,\n ChatCompletion,\n} from \"./types.js\";\n\nconst REDACTED_HEADERS = new Set([\"authorization\", \"x-api-key\", \"api-key\"]);\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 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\";\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return \"toolCalls\" in r && Array.isArray((r as ToolCallResponse).toolCalls);\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 );\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 return \"audio\" in r && typeof (r as AudioResponse).audio === \"string\";\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 buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n): SSEChunk[] {\n const id = generateId();\n const created = Math.floor(Date.now() / 1000);\n const chunks: SSEChunk[] = [];\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,\n choices: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: { role: \"assistant\", content: \"\" }, finish_reason: null }],\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,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: {}, finish_reason: \"stop\" }],\n });\n\n return chunks;\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n): SSEChunk[] {\n const id = generateId();\n const created = Math.floor(Date.now() / 1000);\n const chunks: SSEChunk[] = [];\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: { role: \"assistant\", content: null }, finish_reason: null }],\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,\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 finish_reason: null,\n },\n ],\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,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: {}, finish_reason: \"tool_calls\" }],\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): ChatCompletion {\n return {\n id: generateId(),\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n finish_reason: \"stop\",\n },\n ],\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n };\n}\n\nexport function buildToolCallCompletion(toolCalls: ToolCall[], model: string): ChatCompletion {\n return {\n id: generateId(),\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n message: {\n 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 finish_reason: \"tool_calls\",\n },\n ],\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n): SSEChunk[] {\n const id = generateId();\n const created = Math.floor(Date.now() / 1000);\n const chunks: SSEChunk[] = [];\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: { role: \"assistant\", content: \"\" }, finish_reason: null }],\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,\n choices: [{ index: 0, delta: { content: slice }, finish_reason: null }],\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,\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 finish_reason: null,\n },\n ],\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,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n finish_reason: null,\n },\n ],\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [{ index: 0, delta: {}, finish_reason: \"tool_calls\" }],\n });\n\n return chunks;\n}\n\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n): ChatCompletion {\n return {\n id: generateId(),\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content,\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 finish_reason: \"tool_calls\",\n },\n ],\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\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 // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\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 {\n FixtureResponse,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\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\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 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 );\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 return \"audio\" in r && typeof (r as AudioResponse).audio === \"string\";\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 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: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\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 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 }, 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: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"stop\" }],\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 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 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 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: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\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 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 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: [{ index: 0, delta: { reasoning_content: slice }, finish_reason: null }],\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 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 }, 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 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 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: [{ index: 0, delta: {}, finish_reason: overrides?.finishReason ?? \"tool_calls\" }],\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 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\nexport function readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\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 // Duplicated from journal.ts DEFAULT_TEST_ID — importing it here would create\n // a circular dependency (journal.ts imports from helpers.ts).\n return \"__default__\";\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":";;;AAmBA,MAAM,mBAAmB,IAAI,IAAI;CAAC;CAAiB;CAAa;CAAU,CAAC;AAE3E,SAAgB,eAAe,SAA2D;CACxF,MAAM,OAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,MAAI,UAAU,OAAW;AACzB,MAAI,iBAAiB,IAAI,IAAI,aAAa,CAAC,CACzC,MAAK,OAAO;MAEZ,MAAK,OAAO,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;;AAG1D,QAAO;;AAGT,SAAgB,WAAW,SAAS,YAAoB;AACtD,QAAO,GAAG,OAAO,GAAG,YAAY,GAAG,CAAC,SAAS,YAAY;;AAG3D,SAAgB,qBAA6B;AAC3C,QAAO,QAAQ,YAAY,GAAG,CAAC,SAAS,YAAY;;AAGtD,SAAgB,oBAA4B;AAC1C,QAAO,OAAO,YAAY,GAAG,CAAC,SAAS,YAAY;;AAGrD,SAAgB,oBAA4B;AAC1C,QAAO,SAAS,YAAY,GAAG,CAAC,SAAS,YAAY;;AAGvD,SAAgB,eAAe,GAAuC;AACpE,QAAO,aAAa,KAAK,OAAQ,EAAmB,YAAY,YAAY,EAAE,eAAe;;AAG/F,SAAgB,mBAAmB,GAA2C;AAC5E,QACE,eAAe,KACf,MAAM,QAAS,EAAuB,UAAU,IAChD,EAAE,aAAa,KAAK,OAAQ,EAAyC,YAAY;;AAIrF,SAAgB,+BACd,GACmC;AACnC,QACE,aAAa,KACb,OAAQ,EAAmC,YAAY,YACvD,eAAe,KACf,MAAM,QAAS,EAAmC,UAAU;;AAIhE,SAAgB,gBAAgB,GAAwC;AACtE,QACE,WAAW,KACV,EAAoB,UAAU,QAC/B,OAAQ,EAAoB,UAAU;;AAI1C,SAAgB,oBAAoB,GAA4C;AAC9E,QAAO,eAAe,KAAK,MAAM,QAAS,EAAwB,UAAU;;AAG9E,SAAgB,gBAAgB,GAAwC;AACtE,QACG,WAAW,KAAK,EAAE,SAAS,QAC3B,YAAY,KAAK,MAAM,QAAS,EAAoB,OAAO;;AAIhE,SAAgB,gBAAgB,GAAwC;AACtE,QAAO,WAAW,KAAK,OAAQ,EAAoB,UAAU;;AAG/D,SAAgB,wBAAwB,GAAgD;AACtF,QACE,mBAAmB,KAClB,EAA4B,iBAAiB,QAC9C,OAAQ,EAA4B,kBAAkB;;AAI1D,SAAgB,gBAAgB,GAAwC;AACtE,QACE,WAAW,KACV,EAAoB,SAAS,QAC9B,OAAQ,EAAoB,UAAU;;AAI1C,SAAgB,iBACd,UACmB;CACnB,MAAM,IAAI;AACV,QAAO;EACL,GAAI,EAAE,OAAO,UAAa,EAAE,IAAI,EAAE,IAAI;EACtC,GAAI,EAAE,YAAY,UAAa,EAAE,SAAS,EAAE,SAAS;EACrD,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,OAAO;EAC/C,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,OAAO;EAC/C,GAAI,EAAE,sBAAsB,UAAa,EAAE,mBAAmB,EAAE,mBAAmB;EACnF,GAAI,EAAE,iBAAiB,UAAa,EAAE,cAAc,EAAE,cAAc;EACpE,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,MAAM;EAC7C;;AAGH,SAAgB,gBACd,SACA,OACA,WACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,KAAI,UACF,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,WAAW;EACpD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,UAAU;AAC/C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,mBAAmB,OAAO;IAAE,eAAe;IAAM,CAAC;GACjF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAI;GAC5D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,SAAS,OAAO;IAAE,eAAe;IAAM,CAAC;GACvE,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAIJ,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAQ,CAAC;EACpF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAGT,SAAgB,oBACd,WACA,OACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAM;GAC9D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;EACrD,MAAM,KAAK,UAAU;EACrB,MAAM,OAAO,GAAG,MAAM,oBAAoB;AAG1C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CACP;IACE,OAAO;IACP,OAAO,EACL,YAAY,CACV;KACE,OAAO;KACP,IAAI;KACJ,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW;MAAI;KAC3C,CACF,EACF;IACD,eAAe;IAChB,CACF;GACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;EAGF,MAAM,OAAO,GAAG;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;GAC/C,MAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,UAAU;AAC1C,UAAO,KAAK;IACV;IACA,QAAQ;IACR;IACA,OAAO;IACP,SAAS,CACP;KACE,OAAO;KACP,OAAO,EACL,YAAY,CAAC;MAAE,OAAO;MAAO,UAAU,EAAE,WAAW,OAAO;MAAE,CAAC,EAC/D;KACD,eAAe;KAChB,CACF;IACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;IACrE,CAAC;;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAc,CAAC;EAC1F,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAKT,SAAgB,oBACd,SACA,OACA,WACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB;IACA,SAAS;IACT,GAAI,YAAY,EAAE,mBAAmB,WAAW,GAAG,EAAE;IACtD;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAGH,SAAgB,wBACd,WACA,OACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB,SAAS;IACT,SAAS;IACT,YAAY,UAAU,KAAK,QAAQ;KACjC,IAAI,GAAG,MAAM,oBAAoB;KACjC,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW,GAAG;MAAW;KACrD,EAAE;IACJ;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAGH,SAAgB,gCACd,SACA,WACA,OACA,WACA,WACA,WACY;CACZ,MAAM,KAAK,WAAW,MAAM,YAAY;CACxC,MAAM,UAAU,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;CACnE,MAAM,iBAAiB,WAAW,SAAS;CAC3C,MAAM,SAAqB,EAAE;CAC7B,MAAM,cAAc,WAAW;AAG/B,KAAI,UACF,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,WAAW;EACpD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,UAAU;AAC/C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,mBAAmB,OAAO;IAAE,eAAe;IAAM,CAAC;GACjF,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CACP;GACE,OAAO;GACP,OAAO;IAAE,MAAM,WAAW,QAAQ;IAAa,SAAS;IAAI;GAC5D,eAAe;GAChB,CACF;EACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAGF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CAAC;IAAE,OAAO;IAAG,OAAO,EAAE,SAAS,OAAO;IAAE,eAAe;IAAM,CAAC;GACvE,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;;AAIJ,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;EACrD,MAAM,KAAK,UAAU;EACrB,MAAM,OAAO,GAAG,MAAM,oBAAoB;AAG1C,SAAO,KAAK;GACV;GACA,QAAQ;GACR;GACA,OAAO;GACP,SAAS,CACP;IACE,OAAO;IACP,OAAO,EACL,YAAY,CACV;KACE,OAAO;KACP,IAAI;KACJ,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW;MAAI;KAC3C,CACF,EACF;IACD,eAAe;IAChB,CACF;GACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;GACrE,CAAC;EAGF,MAAM,OAAO,GAAG;AAChB,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;GAC/C,MAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,UAAU;AAC1C,UAAO,KAAK;IACV;IACA,QAAQ;IACR;IACA,OAAO;IACP,SAAS,CACP;KACE,OAAO;KACP,OAAO,EACL,YAAY,CAAC;MAAE,OAAO;MAAO,UAAU,EAAE,WAAW,OAAO;MAAE,CAAC,EAC/D;KACD,eAAe;KAChB,CACF;IACD,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;IACrE,CAAC;;;AAKN,QAAO,KAAK;EACV;EACA,QAAQ;EACR;EACA,OAAO;EACP,SAAS,CAAC;GAAE,OAAO;GAAG,OAAO,EAAE;GAAE,eAAe,WAAW,gBAAgB;GAAc,CAAC;EAC1F,GAAI,gBAAgB,UAAa,EAAE,oBAAoB,aAAa;EACrE,CAAC;AAEF,QAAO;;AAGT,SAAgB,oCACd,SACA,WACA,OACA,WACA,WACgB;CAChB,MAAM,eAAe;EAAE,eAAe;EAAG,mBAAmB;EAAG,cAAc;EAAG;AAChF,QAAO;EACL,IAAI,WAAW,MAAM,YAAY;EACjC,QAAQ;EACR,SAAS,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAC5D,OAAO,WAAW,SAAS;EAC3B,SAAS,CACP;GACE,OAAO;GACP,SAAS;IACP,MAAM,WAAW,QAAQ;IACzB;IACA,SAAS;IACT,GAAI,YAAY,EAAE,mBAAmB,WAAW,GAAG,EAAE;IACrD,YAAY,UAAU,KAAK,QAAQ;KACjC,IAAI,GAAG,MAAM,oBAAoB;KACjC,MAAM;KACN,UAAU;MAAE,MAAM,GAAG;MAAM,WAAW,GAAG;MAAW;KACrD,EAAE;IACJ;GACD,eAAe,WAAW,gBAAgB;GAC3C,CACF;EACD,OAAO,WAAW,QACd;GACE,eAAe,UAAU,MAAM,iBAAiB,aAAa;GAC7D,mBAAmB,UAAU,MAAM,qBAAqB,aAAa;GACrE,cACE,UAAU,MAAM,iBACf,UAAU,MAAM,iBAAiB,MAAM,UAAU,MAAM,qBAAqB;GAChF,GACD;EACJ,GAAI,WAAW,sBAAsB,UAAa,EAChD,oBAAoB,UAAU,mBAC/B;EACF;;AAKH,SAAgB,SAAS,KAA4C;AACnE,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,SAAmB,EAAE;AAC3B,MAAI,GAAG,SAAS,UAAkB,OAAO,KAAK,MAAM,CAAC;AACrD,MAAI,GAAG,aAAa,QAAQ,OAAO,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC9D,MAAI,GAAG,SAAS,OAAO;GACvB;;AAKJ,SAAgB,eAAe,MAAc,SAAmC;AAC9E,KAAI,OAAO,YAAY,SACrB,QAAO,KAAK,aAAa,CAAC,SAAS,QAAQ,aAAa,CAAC;AAE3D,QAAO,QAAQ,KAAK,KAAK;;AAG3B,SAAgB,UAAU,KAAmC;CAC3D,MAAM,cAAc,IAAI,QAAQ;AAChC,KAAI,MAAM,QAAQ,YAAY,EAC5B;MAAI,YAAY,SAAS,KAAK,YAAY,GAAI,QAAO,YAAY;YACxD,OAAO,gBAAgB,YAAY,YAC5C,QAAO;CAGT,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,OAAO,IAAI,QAAQ,IAAI;AAC7B,KAAI,SAAS,IAAI;EAEf,MAAM,aADS,IAAI,gBAAgB,IAAI,MAAM,OAAO,EAAE,CAAC,CAC7B,IAAI,SAAS;AACvC,MAAI,WAAY,QAAO;;AAKzB,QAAO;;AAKT,MAAM,+BAA+B;;;;;;AAOrC,SAAgB,+BACd,OACA,aAAqB,8BACX;CACV,IAAI,cAAc,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,QAAQ;CAC7D,MAAM,YAAsB,IAAI,MAAM,WAAW;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,MAAI,IAAI,KAAK,IAAI,OAAO,EACtB,eAAc,WAAW,SAAS,CAAC,OAAO,YAAY,CAAC,QAAQ;AAGjE,YAAU,KAAK,YAAY,IAAI,MAAM,QAAQ;;AAE/C,QAAO;;;;;AAaT,SAAgB,uBACd,YACA,OACsB;AACtB,QAAO;EACL,QAAQ;EACR,MAAM,WAAW,KAAK,WAAW,WAAW;GAC1C,QAAQ;GACR;GACA;GACD,EAAE;EACH;EACA,OAAO;GAAE,eAAe;GAAG,cAAc;GAAG;EAC7C"}