@copilotkit/aimock 1.46.0 → 1.47.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -1
- package/README.md +4 -1
- package/dist/aws-event-stream.cjs +20 -23
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +0 -1
- package/dist/aws-event-stream.d.ts +0 -1
- package/dist/aws-event-stream.js +20 -24
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/bedrock-converse.cjs +79 -82
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +80 -83
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +112 -121
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +113 -122
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +58 -106
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +60 -108
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/fixture-loader.cjs +103 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +104 -4
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +63 -121
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +65 -123
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +242 -155
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +244 -157
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +400 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +51 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +51 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +391 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/messages.cjs +159 -141
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +161 -143
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +62 -3
- package/dist/misbehavior.cjs.map +1 -1
- package/dist/misbehavior.d.cts.map +1 -1
- package/dist/misbehavior.d.ts.map +1 -1
- package/dist/misbehavior.js +63 -4
- package/dist/misbehavior.js.map +1 -1
- package/dist/ndjson-writer.cjs +21 -23
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +0 -1
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +0 -1
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +21 -24
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +153 -147
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +155 -149
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-chat.cjs +1 -1
- package/dist/openrouter-chat.js +1 -1
- package/dist/raw-validation.cjs +177 -0
- package/dist/raw-validation.cjs.map +1 -0
- package/dist/raw-validation.js +176 -0
- package/dist/raw-validation.js.map +1 -0
- package/dist/response/deliver.cjs +144 -0
- package/dist/response/deliver.cjs.map +1 -0
- package/dist/response/deliver.js +143 -0
- package/dist/response/deliver.js.map +1 -0
- package/dist/response/frame-loop.cjs +102 -0
- package/dist/response/frame-loop.cjs.map +1 -0
- package/dist/response/frame-loop.js +100 -0
- package/dist/response/frame-loop.js.map +1 -0
- package/dist/response/lifecycle.cjs +223 -0
- package/dist/response/lifecycle.cjs.map +1 -0
- package/dist/response/lifecycle.js +211 -0
- package/dist/response/lifecycle.js.map +1 -0
- package/dist/response/proxy.cjs +71 -0
- package/dist/response/proxy.cjs.map +1 -0
- package/dist/response/proxy.js +70 -0
- package/dist/response/proxy.js.map +1 -0
- package/dist/response/raw.cjs +228 -0
- package/dist/response/raw.cjs.map +1 -0
- package/dist/response/raw.js +227 -0
- package/dist/response/raw.js.map +1 -0
- package/dist/response/timing.cjs +157 -0
- package/dist/response/timing.cjs.map +1 -0
- package/dist/response/timing.js +150 -0
- package/dist/response/timing.js.map +1 -0
- package/dist/responses.cjs +269 -148
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +270 -149
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -4
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +7 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -5
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +89 -120
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +91 -122
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +49 -30
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +0 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +0 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +49 -31
- package/dist/sse-writer.js.map +1 -1
- package/dist/transcription.cjs +19 -33
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +20 -34
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +173 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +173 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ws-framing.cjs +12 -6
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +2 -0
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +2 -0
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +12 -6
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +120 -93
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.js +120 -93
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +80 -70
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +80 -70
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +72 -53
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +73 -54
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +34 -2
package/dist/gemini.cjs
CHANGED
|
@@ -6,7 +6,11 @@ const require_chaos = require('./chaos.cjs');
|
|
|
6
6
|
const require_misbehavior = require('./misbehavior.cjs');
|
|
7
7
|
const require_router = require('./router.cjs');
|
|
8
8
|
const require_recorder = require('./recorder.cjs');
|
|
9
|
-
const
|
|
9
|
+
const require_logger = require('./logger.cjs');
|
|
10
|
+
const require_timing = require('./response/timing.cjs');
|
|
11
|
+
const require_deliver = require('./response/deliver.cjs');
|
|
12
|
+
const require_proxy = require('./response/proxy.cjs');
|
|
13
|
+
const require_raw = require('./response/raw.cjs');
|
|
10
14
|
const require_finish = require('./response/finish.cjs');
|
|
11
15
|
const require_gemini = require('./response/usage/codecs/gemini.cjs');
|
|
12
16
|
|
|
@@ -149,6 +153,110 @@ function geminiFinishReason(finishReason, defaultReason) {
|
|
|
149
153
|
function geminiUsageMetadata(overrides) {
|
|
150
154
|
return require_gemini.render(overrides);
|
|
151
155
|
}
|
|
156
|
+
function geminiWholeTextPart(text, partIndex) {
|
|
157
|
+
return [{
|
|
158
|
+
partIndex,
|
|
159
|
+
start: 0,
|
|
160
|
+
end: text.length
|
|
161
|
+
}];
|
|
162
|
+
}
|
|
163
|
+
function geminiGroundingSupports(text, citations, textParts) {
|
|
164
|
+
const segment = (part, from, to) => {
|
|
165
|
+
const partText = text.slice(part.start, part.end);
|
|
166
|
+
const start = Math.min(Math.max(from - part.start, 0), partText.length);
|
|
167
|
+
const end = Math.min(Math.max(to - part.start, start), partText.length);
|
|
168
|
+
const byteOffset = (charIndex) => Buffer.byteLength(partText.slice(0, charIndex), "utf8");
|
|
169
|
+
return {
|
|
170
|
+
partIndex: part.partIndex,
|
|
171
|
+
startIndex: byteOffset(start),
|
|
172
|
+
endIndex: byteOffset(end),
|
|
173
|
+
text: partText.slice(start, end)
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
return citations.flatMap((c, i) => {
|
|
177
|
+
const spanned = textParts.filter((p) => Math.max(c.startIndex, p.start) < Math.min(c.endIndex, p.end));
|
|
178
|
+
return (spanned.length > 0 ? spanned : [textParts.find((p) => c.startIndex < p.end) ?? textParts[textParts.length - 1]]).map((p) => ({
|
|
179
|
+
segment: segment(p, c.startIndex, c.endIndex),
|
|
180
|
+
groundingChunkIndices: [i]
|
|
181
|
+
}));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function geminiGroundingMetadata(text, overrides, textParts) {
|
|
185
|
+
const citations = require_helpers.resolveCitations(text, overrides?.citations);
|
|
186
|
+
if (citations.length === 0 || textParts.length === 0) return void 0;
|
|
187
|
+
return {
|
|
188
|
+
groundingChunks: geminiGroundingChunks(citations),
|
|
189
|
+
groundingSupports: geminiGroundingSupports(text, citations, textParts)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function geminiGroundingChunks(citations) {
|
|
193
|
+
return citations.map((c) => ({ web: {
|
|
194
|
+
uri: c.url,
|
|
195
|
+
title: c.title
|
|
196
|
+
} }));
|
|
197
|
+
}
|
|
198
|
+
function geminiGroundingField(text, overrides, textParts) {
|
|
199
|
+
const groundingMetadata = geminiGroundingMetadata(text, overrides, textParts);
|
|
200
|
+
return groundingMetadata ? { groundingMetadata } : {};
|
|
201
|
+
}
|
|
202
|
+
function geminiStreamGroundingFields(text, overrides, spans) {
|
|
203
|
+
const fields = spans.map(() => ({}));
|
|
204
|
+
const citations = require_helpers.resolveCitations(text, overrides?.citations);
|
|
205
|
+
if (citations.length === 0 || spans.length === 0) return fields;
|
|
206
|
+
const textParts = spans.map((s, k) => ({
|
|
207
|
+
partIndex: k,
|
|
208
|
+
start: s.start,
|
|
209
|
+
end: s.end
|
|
210
|
+
}));
|
|
211
|
+
const groundingChunks = geminiGroundingChunks(citations);
|
|
212
|
+
for (const support of geminiGroundingSupports(text, citations, textParts)) {
|
|
213
|
+
const k = support.segment.partIndex;
|
|
214
|
+
(fields[k].groundingMetadata ??= {
|
|
215
|
+
groundingChunks,
|
|
216
|
+
groundingSupports: []
|
|
217
|
+
}).groundingSupports.push({
|
|
218
|
+
...support,
|
|
219
|
+
segment: {
|
|
220
|
+
...support.segment,
|
|
221
|
+
partIndex: 0
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return fields;
|
|
226
|
+
}
|
|
227
|
+
function geminiTextChunkSpans(text, chunkSize, offset = 0) {
|
|
228
|
+
if (text.length === 0) return [{
|
|
229
|
+
start: offset,
|
|
230
|
+
end: offset
|
|
231
|
+
}];
|
|
232
|
+
const spans = [];
|
|
233
|
+
let start = offset;
|
|
234
|
+
for (const slice of require_helpers.surrogateSafeChunks(text, chunkSize)) {
|
|
235
|
+
spans.push({
|
|
236
|
+
start,
|
|
237
|
+
end: start + slice.length
|
|
238
|
+
});
|
|
239
|
+
start += slice.length;
|
|
240
|
+
}
|
|
241
|
+
return spans;
|
|
242
|
+
}
|
|
243
|
+
function geminiFileParts(overrides) {
|
|
244
|
+
return require_helpers.fixtureFiles(overrides?.files).map((f) => ({ inlineData: {
|
|
245
|
+
mimeType: f.mimeType,
|
|
246
|
+
data: f.data
|
|
247
|
+
} }));
|
|
248
|
+
}
|
|
249
|
+
function geminiFileChunks(overrides) {
|
|
250
|
+
const parts = geminiFileParts(overrides);
|
|
251
|
+
if (parts.length === 0) return [];
|
|
252
|
+
return [{ candidates: [{
|
|
253
|
+
content: {
|
|
254
|
+
role: "model",
|
|
255
|
+
parts
|
|
256
|
+
},
|
|
257
|
+
index: 0
|
|
258
|
+
}] }];
|
|
259
|
+
}
|
|
152
260
|
function buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides) {
|
|
153
261
|
const chunks = [];
|
|
154
262
|
const effectiveFinish = geminiFinishReason(overrides?.finishReason, "STOP");
|
|
@@ -166,9 +274,11 @@ function buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides) {
|
|
|
166
274
|
index: 0
|
|
167
275
|
}] });
|
|
168
276
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
277
|
+
chunks.push(...geminiFileChunks(overrides));
|
|
278
|
+
const grounding = geminiStreamGroundingFields(content, overrides, geminiTextChunkSpans(content, chunkSize));
|
|
279
|
+
const contentSlices = require_helpers.surrogateSafeChunks(content, chunkSize);
|
|
280
|
+
contentSlices.forEach((slice, k) => {
|
|
281
|
+
const isLast = k === contentSlices.length - 1;
|
|
172
282
|
const chunk = {
|
|
173
283
|
candidates: [{
|
|
174
284
|
content: {
|
|
@@ -176,12 +286,13 @@ function buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides) {
|
|
|
176
286
|
parts: [{ text: slice }]
|
|
177
287
|
},
|
|
178
288
|
index: 0,
|
|
179
|
-
...isLast ? { finishReason: effectiveFinish } : {}
|
|
289
|
+
...isLast ? { finishReason: effectiveFinish } : {},
|
|
290
|
+
...grounding[k]
|
|
180
291
|
}],
|
|
181
292
|
...isLast ? { usageMetadata: usage } : {}
|
|
182
293
|
};
|
|
183
294
|
chunks.push(chunk);
|
|
184
|
-
}
|
|
295
|
+
});
|
|
185
296
|
if (content.length === 0) chunks.push({
|
|
186
297
|
candidates: [{
|
|
187
298
|
content: {
|
|
@@ -189,6 +300,7 @@ function buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides) {
|
|
|
189
300
|
parts: [{ text: "" }]
|
|
190
301
|
},
|
|
191
302
|
finishReason: effectiveFinish,
|
|
303
|
+
...grounding[0],
|
|
192
304
|
index: 0
|
|
193
305
|
}],
|
|
194
306
|
usageMetadata: usage
|
|
@@ -219,6 +331,8 @@ function buildGeminiToolCallStreamChunks(toolCalls, chunkSize, logger, reasoning
|
|
|
219
331
|
index: 0
|
|
220
332
|
}] });
|
|
221
333
|
}
|
|
334
|
+
chunks.push(...geminiFileChunks(overrides));
|
|
335
|
+
require_helpers.resolveCitations("", overrides?.citations);
|
|
222
336
|
const parts = toolCalls.map((tc) => parseToolCallPart(tc, logger));
|
|
223
337
|
chunks.push({
|
|
224
338
|
candidates: [{
|
|
@@ -239,6 +353,8 @@ function buildGeminiTextResponse(content, reasoning, overrides) {
|
|
|
239
353
|
text: reasoning,
|
|
240
354
|
thought: true
|
|
241
355
|
});
|
|
356
|
+
parts.push(...geminiFileParts(overrides));
|
|
357
|
+
const textParts = geminiWholeTextPart(content, parts.length);
|
|
242
358
|
parts.push({ text: content });
|
|
243
359
|
return {
|
|
244
360
|
candidates: [{
|
|
@@ -247,17 +363,20 @@ function buildGeminiTextResponse(content, reasoning, overrides) {
|
|
|
247
363
|
parts
|
|
248
364
|
},
|
|
249
365
|
finishReason: geminiFinishReason(overrides?.finishReason, "STOP"),
|
|
366
|
+
...geminiGroundingField(content, overrides, textParts),
|
|
250
367
|
index: 0
|
|
251
368
|
}],
|
|
252
369
|
usageMetadata: geminiUsageMetadata(overrides)
|
|
253
370
|
};
|
|
254
371
|
}
|
|
255
372
|
function buildGeminiToolCallResponse(toolCalls, logger, reasoning, overrides) {
|
|
373
|
+
require_helpers.resolveCitations("", overrides?.citations);
|
|
256
374
|
const parts = [];
|
|
257
375
|
if (reasoning) parts.push({
|
|
258
376
|
text: reasoning,
|
|
259
377
|
thought: true
|
|
260
378
|
});
|
|
379
|
+
parts.push(...geminiFileParts(overrides));
|
|
261
380
|
parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));
|
|
262
381
|
return {
|
|
263
382
|
candidates: [{
|
|
@@ -286,9 +405,19 @@ function buildGeminiContentWithToolCallsStreamChunks(content, toolCalls, chunkSi
|
|
|
286
405
|
index: 0
|
|
287
406
|
}] });
|
|
288
407
|
}
|
|
408
|
+
chunks.push(...geminiFileChunks(overrides));
|
|
289
409
|
if (blocks && blocks.length > 0) {
|
|
290
410
|
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
291
411
|
const resolved = outcome.ordered;
|
|
412
|
+
const spans = [];
|
|
413
|
+
let offset = 0;
|
|
414
|
+
for (const block of resolved) {
|
|
415
|
+
if (block.type !== "text") continue;
|
|
416
|
+
spans.push(...geminiTextChunkSpans(block.text, chunkSize, offset));
|
|
417
|
+
offset += block.text.length;
|
|
418
|
+
}
|
|
419
|
+
const grounding = geminiStreamGroundingFields(outcome.content, overrides, spans);
|
|
420
|
+
let textChunk = 0;
|
|
292
421
|
resolved.forEach((block, i) => {
|
|
293
422
|
const isLast = i === resolved.length - 1;
|
|
294
423
|
const finishReason = isLast ? geminiFinishReason(overrides?.finishReason, outcome.hasToolCalls ? "FUNCTION_CALL" : "STOP") : void 0;
|
|
@@ -318,47 +447,53 @@ function buildGeminiContentWithToolCallsStreamChunks(content, toolCalls, chunkSi
|
|
|
318
447
|
parts: [{ text: "" }]
|
|
319
448
|
},
|
|
320
449
|
...finishReason ? { finishReason } : {},
|
|
450
|
+
...grounding[textChunk++],
|
|
321
451
|
index: 0
|
|
322
452
|
}],
|
|
323
453
|
...isLast ? { usageMetadata: geminiUsageMetadata(overrides) } : {}
|
|
324
454
|
});
|
|
325
|
-
else
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
455
|
+
else {
|
|
456
|
+
const slices = require_helpers.surrogateSafeChunks(text, chunkSize);
|
|
457
|
+
slices.forEach((slice, j) => {
|
|
458
|
+
const lastSlice = j === slices.length - 1;
|
|
459
|
+
const sliceFinish = isLast && lastSlice ? finishReason : void 0;
|
|
460
|
+
chunks.push({
|
|
461
|
+
candidates: [{
|
|
462
|
+
content: {
|
|
463
|
+
role: "model",
|
|
464
|
+
parts: [{ text: slice }]
|
|
465
|
+
},
|
|
466
|
+
...sliceFinish ? { finishReason: sliceFinish } : {},
|
|
467
|
+
...grounding[textChunk++],
|
|
468
|
+
index: 0
|
|
469
|
+
}],
|
|
470
|
+
...isLast && lastSlice ? { usageMetadata: geminiUsageMetadata(overrides) } : {}
|
|
471
|
+
});
|
|
339
472
|
});
|
|
340
473
|
}
|
|
341
474
|
}
|
|
342
475
|
});
|
|
343
476
|
return chunks;
|
|
344
477
|
}
|
|
478
|
+
const grounding = geminiStreamGroundingFields(content, overrides, geminiTextChunkSpans(content, chunkSize));
|
|
345
479
|
if (content.length === 0) chunks.push({ candidates: [{
|
|
346
480
|
content: {
|
|
347
481
|
role: "model",
|
|
348
482
|
parts: [{ text: "" }]
|
|
349
483
|
},
|
|
484
|
+
...grounding[0],
|
|
350
485
|
index: 0
|
|
351
486
|
}] });
|
|
352
|
-
else
|
|
353
|
-
const slice = content.slice(i, i + chunkSize);
|
|
487
|
+
else require_helpers.surrogateSafeChunks(content, chunkSize).forEach((slice, k) => {
|
|
354
488
|
chunks.push({ candidates: [{
|
|
355
489
|
content: {
|
|
356
490
|
role: "model",
|
|
357
491
|
parts: [{ text: slice }]
|
|
358
492
|
},
|
|
493
|
+
...grounding[k],
|
|
359
494
|
index: 0
|
|
360
495
|
}] });
|
|
361
|
-
}
|
|
496
|
+
});
|
|
362
497
|
const parts = toolCalls.map((tc) => parseToolCallPart(tc, logger));
|
|
363
498
|
chunks.push({
|
|
364
499
|
candidates: [{
|
|
@@ -379,18 +514,31 @@ function buildGeminiContentWithToolCallsResponse(content, toolCalls, logger, rea
|
|
|
379
514
|
text: reasoning,
|
|
380
515
|
thought: true
|
|
381
516
|
});
|
|
517
|
+
parts.push(...geminiFileParts(overrides));
|
|
382
518
|
let blockHasTools;
|
|
519
|
+
let groundingText = content;
|
|
520
|
+
const textParts = [];
|
|
383
521
|
if (blocks && blocks.length > 0) {
|
|
384
522
|
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
385
523
|
const resolved = outcome.ordered;
|
|
386
524
|
blockHasTools = outcome.hasToolCalls;
|
|
525
|
+
groundingText = outcome.content;
|
|
387
526
|
for (const block of resolved) if (block.type === "toolCall") parts.push(parseToolCallPart({
|
|
388
527
|
name: block.name,
|
|
389
528
|
arguments: block.arguments,
|
|
390
529
|
id: block.id
|
|
391
530
|
}, logger));
|
|
392
|
-
else
|
|
531
|
+
else {
|
|
532
|
+
const start = textParts.length > 0 ? textParts[textParts.length - 1].end : 0;
|
|
533
|
+
textParts.push({
|
|
534
|
+
partIndex: parts.length,
|
|
535
|
+
start,
|
|
536
|
+
end: start + block.text.length
|
|
537
|
+
});
|
|
538
|
+
parts.push({ text: block.text });
|
|
539
|
+
}
|
|
393
540
|
} else {
|
|
541
|
+
textParts.push(...geminiWholeTextPart(content, parts.length));
|
|
394
542
|
parts.push({ text: content });
|
|
395
543
|
parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));
|
|
396
544
|
}
|
|
@@ -401,6 +549,7 @@ function buildGeminiContentWithToolCallsResponse(content, toolCalls, logger, rea
|
|
|
401
549
|
parts
|
|
402
550
|
},
|
|
403
551
|
finishReason: geminiFinishReason(overrides?.finishReason, blockHasTools === false ? "STOP" : "FUNCTION_CALL"),
|
|
552
|
+
...geminiGroundingField(groundingText, overrides, textParts),
|
|
404
553
|
index: 0
|
|
405
554
|
}],
|
|
406
555
|
usageMetadata: geminiUsageMetadata(overrides)
|
|
@@ -452,30 +601,19 @@ function buildGeminiAudioStreamChunks(audio, toolCalls, logger, effReasoning) {
|
|
|
452
601
|
usageMetadata: require_gemini.render()
|
|
453
602
|
}];
|
|
454
603
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
469
|
-
if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
|
|
470
|
-
if (signal?.aborted) return false;
|
|
471
|
-
if (res.writableEnded) return true;
|
|
472
|
-
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
473
|
-
onChunkSent?.();
|
|
474
|
-
if (signal?.aborted) return false;
|
|
475
|
-
chunkIndex++;
|
|
476
|
-
}
|
|
477
|
-
if (!res.writableEnded) res.end();
|
|
478
|
-
return true;
|
|
604
|
+
const GEMINI_SSE = require_sse_writer.sseCodec({ done: false });
|
|
605
|
+
const SILENT_LOGGER = new require_logger.Logger("silent");
|
|
606
|
+
/**
|
|
607
|
+
* The reasoning a misbehavior reply's non-streaming wait is sized with: the
|
|
608
|
+
* plan response's reasoning through the same model and strict gate the
|
|
609
|
+
* builders apply. The gate runs with a silent logger, so the builder's own
|
|
610
|
+
* warning stays the only one. A reasoning-only plan carries its reasoning on
|
|
611
|
+
* the plan, and `misbehaviorPayloadLength` reads it there.
|
|
612
|
+
*/
|
|
613
|
+
function misbehaviorPayloadReasoning(plan, model, strict) {
|
|
614
|
+
const response = plan.response;
|
|
615
|
+
if (plan.reasoning !== void 0 || !(require_helpers.isCombinedFixtureResponse(response) || require_helpers.isTextResponse(response) || require_helpers.isToolCallResponse(response))) return;
|
|
616
|
+
return require_helpers.resolveReasoningForModel(response.reasoning, model, strict, SILENT_LOGGER);
|
|
479
617
|
}
|
|
480
618
|
/** Prepare native fault output once; the journal describes these exact parts. */
|
|
481
619
|
function prepareGeminiMisbehavior(plan, request, streaming, chunkSize, logger, strict) {
|
|
@@ -508,7 +646,11 @@ function prepareGeminiMisbehavior(plan, request, streaming, chunkSize, logger, s
|
|
|
508
646
|
candidate.finishReason = "UNEXPECTED_TOOL_CALL";
|
|
509
647
|
chunks = [{ candidates: [candidate] }];
|
|
510
648
|
} else if (fault === "stop-length-mid-tool") {
|
|
511
|
-
|
|
649
|
+
const parts = geminiFileParts(overrides);
|
|
650
|
+
const textPartIndex = parts.length;
|
|
651
|
+
if (content) parts.push({ text: content });
|
|
652
|
+
candidate.content.parts = parts;
|
|
653
|
+
if (content) Object.assign(candidate, geminiGroundingField(content, overrides, geminiWholeTextPart(content, textPartIndex)));
|
|
512
654
|
candidate.finishReason = "MAX_TOKENS";
|
|
513
655
|
chunks = [{ candidates: [candidate] }];
|
|
514
656
|
} else if (fault === "empty-response") {
|
|
@@ -626,7 +768,10 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
626
768
|
completionReq._endpointType = "chat";
|
|
627
769
|
completionReq._context = require_helpers.getContext(req);
|
|
628
770
|
const testId = require_helpers.getTestId(req);
|
|
629
|
-
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform,
|
|
771
|
+
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, {
|
|
772
|
+
...require_router.recordMatchOptions(!!defaults.record, defaults.logger),
|
|
773
|
+
wire: "gemini"
|
|
774
|
+
});
|
|
630
775
|
const path = req.url ?? `/v1beta/models/${model}:generateContent`;
|
|
631
776
|
if (fixture) logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
632
777
|
else logger.debug(`No fixture matched for request`);
|
|
@@ -661,7 +806,7 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
661
806
|
return;
|
|
662
807
|
}
|
|
663
808
|
if (decisions.upstream && defaults.record) {
|
|
664
|
-
const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, providerKey, path, fixtures, defaults, raw);
|
|
809
|
+
const { outcome, clientHungUp } = await require_proxy.withHangUpDetection(req, res, () => require_recorder.proxyAndRecord(req, res, completionReq, providerKey, path, fixtures, defaults, raw));
|
|
665
810
|
if (outcome === "handled_by_hook") return;
|
|
666
811
|
if (outcome !== "not_configured") {
|
|
667
812
|
journal.add({
|
|
@@ -672,7 +817,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
672
817
|
response: {
|
|
673
818
|
status: res.statusCode ?? 200,
|
|
674
819
|
fixture: null,
|
|
675
|
-
source: "proxy"
|
|
820
|
+
source: "proxy",
|
|
821
|
+
...require_proxy.proxyInterruption(res, clientHungUp)
|
|
676
822
|
}
|
|
677
823
|
});
|
|
678
824
|
return;
|
|
@@ -736,8 +882,21 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
736
882
|
const latency = fixture.latency ?? defaults.latency;
|
|
737
883
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
738
884
|
const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
|
|
885
|
+
const streamTiming = {
|
|
886
|
+
latency,
|
|
887
|
+
streamingProfile: fixture.streamingProfile,
|
|
888
|
+
recordedTimings: fixture.recordedTimings,
|
|
889
|
+
replaySpeed
|
|
890
|
+
};
|
|
891
|
+
const jsonOpts = (payloadLength, duplicateId = false) => ({
|
|
892
|
+
surface: "other",
|
|
893
|
+
payloadLength,
|
|
894
|
+
chunkSize,
|
|
895
|
+
recordedTimings: fixture.recordedTimings,
|
|
896
|
+
duplicateId,
|
|
897
|
+
replaySpeed
|
|
898
|
+
});
|
|
739
899
|
if (misbehavior.kind === "applied") {
|
|
740
|
-
const prepared = prepareGeminiMisbehavior(misbehavior, completionReq, streaming, chunkSize, logger, require_policy.resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression);
|
|
741
900
|
const entry = journal.add({
|
|
742
901
|
method: req.method ?? "POST",
|
|
743
902
|
path,
|
|
@@ -748,6 +907,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
748
907
|
fixture
|
|
749
908
|
}
|
|
750
909
|
});
|
|
910
|
+
const strictReasoning = require_policy.resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
|
|
911
|
+
const prepared = prepareGeminiMisbehavior(misbehavior, completionReq, streaming, chunkSize, logger, strictReasoning);
|
|
751
912
|
require_misbehavior.recordMisbehaviorOutcome({
|
|
752
913
|
entry,
|
|
753
914
|
summary: prepared.summary,
|
|
@@ -755,24 +916,26 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
755
916
|
testId
|
|
756
917
|
});
|
|
757
918
|
if (!streaming) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
} else
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
919
|
+
const preparedBody = prepared.chunks[0];
|
|
920
|
+
await require_deliver.deliverJson(res, entry, fixture, () => preparedBody, jsonOpts(require_timing.misbehaviorPayloadLength(misbehavior, misbehaviorPayloadReasoning(misbehavior, completionReq.model, strictReasoning)), !!misbehavior.duplicateId));
|
|
921
|
+
} else await require_deliver.deliverStream(res, entry, fixture, GEMINI_SSE, prepared.chunks, streamTiming);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (require_helpers.isRawResponse(response)) {
|
|
925
|
+
await require_raw.serveRawResponse({
|
|
926
|
+
wire: "gemini",
|
|
927
|
+
stream: streaming,
|
|
928
|
+
req,
|
|
929
|
+
res,
|
|
930
|
+
fixture,
|
|
931
|
+
response,
|
|
932
|
+
body: completionReq,
|
|
933
|
+
journal,
|
|
934
|
+
defaults,
|
|
935
|
+
testId,
|
|
936
|
+
misbehaviorSummary: misbehavior.summary,
|
|
937
|
+
errorWire: "gemini"
|
|
938
|
+
});
|
|
776
939
|
return;
|
|
777
940
|
}
|
|
778
941
|
if (require_helpers.isErrorResponse(response)) {
|
|
@@ -819,27 +982,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
819
982
|
testId
|
|
820
983
|
});
|
|
821
984
|
const audioToolCalls = Array.isArray(response.toolCalls) ? require_helpers.requireServedFunctionToolCalls(response, wire) : [];
|
|
822
|
-
if (!streaming)
|
|
823
|
-
|
|
824
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
825
|
-
res.end(JSON.stringify(body));
|
|
826
|
-
} else {
|
|
827
|
-
const chunks = buildGeminiAudioStreamChunks(response, audioToolCalls, logger, effReasoning);
|
|
828
|
-
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
829
|
-
if (!await writeGeminiSSEStream(res, chunks, {
|
|
830
|
-
latency,
|
|
831
|
-
streamingProfile: fixture.streamingProfile,
|
|
832
|
-
recordedTimings: fixture.recordedTimings,
|
|
833
|
-
replaySpeed,
|
|
834
|
-
signal: interruption?.signal,
|
|
835
|
-
onChunkSent: interruption?.tick
|
|
836
|
-
})) {
|
|
837
|
-
if (!res.writableEnded) res.destroy();
|
|
838
|
-
journalEntry.response.interrupted = true;
|
|
839
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
840
|
-
}
|
|
841
|
-
interruption?.cleanup();
|
|
842
|
-
}
|
|
985
|
+
if (!streaming) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildGeminiAudioResponse(response, audioToolCalls, logger, effReasoning), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
986
|
+
else await require_deliver.deliverStream(res, journalEntry, fixture, GEMINI_SSE, buildGeminiAudioStreamChunks(response, audioToolCalls, logger, effReasoning), streamTiming);
|
|
843
987
|
return;
|
|
844
988
|
}
|
|
845
989
|
if (require_helpers.isCombinedFixtureResponse(response)) {
|
|
@@ -864,27 +1008,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
864
1008
|
testId
|
|
865
1009
|
});
|
|
866
1010
|
const functionToolCalls = require_helpers.requireEmittedFunctionToolCalls(response, wire);
|
|
867
|
-
if (!streaming)
|
|
868
|
-
|
|
869
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
870
|
-
res.end(JSON.stringify(body));
|
|
871
|
-
} else {
|
|
872
|
-
const chunks = buildGeminiContentWithToolCallsStreamChunks(response.content ?? "", functionToolCalls, chunkSize, logger, effReasoning, overrides, response.blocks);
|
|
873
|
-
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
874
|
-
if (!await writeGeminiSSEStream(res, chunks, {
|
|
875
|
-
latency,
|
|
876
|
-
streamingProfile: fixture.streamingProfile,
|
|
877
|
-
recordedTimings: fixture.recordedTimings,
|
|
878
|
-
replaySpeed,
|
|
879
|
-
signal: interruption?.signal,
|
|
880
|
-
onChunkSent: interruption?.tick
|
|
881
|
-
})) {
|
|
882
|
-
if (!res.writableEnded) res.destroy();
|
|
883
|
-
journalEntry.response.interrupted = true;
|
|
884
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
885
|
-
}
|
|
886
|
-
interruption?.cleanup();
|
|
887
|
-
}
|
|
1011
|
+
if (!streaming) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildGeminiContentWithToolCallsResponse(response.content ?? "", functionToolCalls, logger, effReasoning, overrides, response.blocks), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1012
|
+
else await require_deliver.deliverStream(res, journalEntry, fixture, GEMINI_SSE, buildGeminiContentWithToolCallsStreamChunks(response.content ?? "", functionToolCalls, chunkSize, logger, effReasoning, overrides, response.blocks), streamTiming);
|
|
888
1013
|
return;
|
|
889
1014
|
}
|
|
890
1015
|
if (require_helpers.isTextResponse(response)) {
|
|
@@ -908,27 +1033,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
908
1033
|
defaults,
|
|
909
1034
|
testId
|
|
910
1035
|
});
|
|
911
|
-
if (!streaming)
|
|
912
|
-
|
|
913
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
914
|
-
res.end(JSON.stringify(body));
|
|
915
|
-
} else {
|
|
916
|
-
const chunks = buildGeminiTextStreamChunks(response.content, chunkSize, effReasoning, overrides);
|
|
917
|
-
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
918
|
-
if (!await writeGeminiSSEStream(res, chunks, {
|
|
919
|
-
latency,
|
|
920
|
-
streamingProfile: fixture.streamingProfile,
|
|
921
|
-
recordedTimings: fixture.recordedTimings,
|
|
922
|
-
replaySpeed,
|
|
923
|
-
signal: interruption?.signal,
|
|
924
|
-
onChunkSent: interruption?.tick
|
|
925
|
-
})) {
|
|
926
|
-
if (!res.writableEnded) res.destroy();
|
|
927
|
-
journalEntry.response.interrupted = true;
|
|
928
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
929
|
-
}
|
|
930
|
-
interruption?.cleanup();
|
|
931
|
-
}
|
|
1036
|
+
if (!streaming) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildGeminiTextResponse(response.content, effReasoning, overrides), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1037
|
+
else await require_deliver.deliverStream(res, journalEntry, fixture, GEMINI_SSE, buildGeminiTextStreamChunks(response.content, chunkSize, effReasoning, overrides), streamTiming);
|
|
932
1038
|
return;
|
|
933
1039
|
}
|
|
934
1040
|
if (require_helpers.isToolCallResponse(response)) {
|
|
@@ -953,27 +1059,8 @@ async function handleGemini(req, res, raw, model, streaming, fixtures, journal,
|
|
|
953
1059
|
testId
|
|
954
1060
|
});
|
|
955
1061
|
const functionToolCalls = require_helpers.requireEmittedFunctionToolCalls(response, wire);
|
|
956
|
-
if (!streaming)
|
|
957
|
-
|
|
958
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
959
|
-
res.end(JSON.stringify(body));
|
|
960
|
-
} else {
|
|
961
|
-
const chunks = buildGeminiToolCallStreamChunks(functionToolCalls, chunkSize, logger, effReasoning, overrides);
|
|
962
|
-
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
963
|
-
if (!await writeGeminiSSEStream(res, chunks, {
|
|
964
|
-
latency,
|
|
965
|
-
streamingProfile: fixture.streamingProfile,
|
|
966
|
-
recordedTimings: fixture.recordedTimings,
|
|
967
|
-
replaySpeed,
|
|
968
|
-
signal: interruption?.signal,
|
|
969
|
-
onChunkSent: interruption?.tick
|
|
970
|
-
})) {
|
|
971
|
-
if (!res.writableEnded) res.destroy();
|
|
972
|
-
journalEntry.response.interrupted = true;
|
|
973
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
974
|
-
}
|
|
975
|
-
interruption?.cleanup();
|
|
976
|
-
}
|
|
1062
|
+
if (!streaming) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildGeminiToolCallResponse(functionToolCalls, logger, effReasoning, overrides), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1063
|
+
else await require_deliver.deliverStream(res, journalEntry, fixture, GEMINI_SSE, buildGeminiToolCallStreamChunks(functionToolCalls, chunkSize, logger, effReasoning, overrides), streamTiming);
|
|
977
1064
|
return;
|
|
978
1065
|
}
|
|
979
1066
|
require_misbehavior.recordMisbehaviorOutcome({
|