@copilotkit/aimock 1.27.2 → 1.28.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 +19 -0
- package/README.md +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/gemini.cjs +18 -8
- 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 +18 -8
- package/dist/gemini.js.map +1 -1
- package/dist/harmony.cjs +419 -0
- package/dist/harmony.cjs.map +1 -0
- package/dist/harmony.js +417 -0
- package/dist/harmony.js.map +1 -0
- package/dist/images.cjs +3 -3
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts +1 -1
- package/dist/images.d.ts +1 -1
- package/dist/images.js +3 -3
- package/dist/images.js.map +1 -1
- package/dist/recorder.cjs +57 -13
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +6 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +6 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +57 -13
- package/dist/recorder.js.map +1 -1
- package/dist/server.cjs +4 -3
- 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 +4 -3
- package/dist/server.js.map +1 -1
- package/dist/stream-collapse.cjs +219 -57
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +16 -0
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +16 -0
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +219 -57
- package/dist/stream-collapse.js.map +1 -1
- package/dist/types.d.cts +9 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-types.d.cts.map +1 -1
- package/package.json +1 -1
package/dist/stream-collapse.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
const require_harmony = require('./harmony.cjs');
|
|
2
3
|
let node_zlib = require("node:zlib");
|
|
3
4
|
|
|
4
5
|
//#region src/stream-collapse.ts
|
|
@@ -11,6 +12,60 @@ let node_zlib = require("node:zlib");
|
|
|
11
12
|
* text followed by tool calls.
|
|
12
13
|
*/
|
|
13
14
|
/**
|
|
15
|
+
* Slice the first `max` UTF-16 code units of `s` for a diagnostic sample,
|
|
16
|
+
* trimming a trailing lone high-surrogate so the resulting sample never ends on
|
|
17
|
+
* a lone high surrogate (i.e. never mid-surrogate-pair).
|
|
18
|
+
*/
|
|
19
|
+
function surrogateSafeSlice(s, max) {
|
|
20
|
+
let out = s.slice(0, max);
|
|
21
|
+
if (out.length > 0) {
|
|
22
|
+
const last = out.charCodeAt(out.length - 1);
|
|
23
|
+
if (last >= 55296 && last <= 56319) out = out.slice(0, -1);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Split a raw SSE body into per-event blocks.
|
|
29
|
+
*
|
|
30
|
+
* Events are delimited by a blank line. Real HTTP/SSE transports use CRLF
|
|
31
|
+
* (`\r\n`) line endings, so the inter-event delimiter is `\r\n\r\n` (which
|
|
32
|
+
* contains no `\n\n` substring) and each line ends with a trailing `\r`.
|
|
33
|
+
* Splitting on `/\r?\n\r?\n/` handles LF, CRLF, and mixed streams; per-line
|
|
34
|
+
* `\r` trimming happens in {@link splitSSELines}. Blank blocks are dropped.
|
|
35
|
+
*/
|
|
36
|
+
function splitSSEEvents(body) {
|
|
37
|
+
return body.split(/\r?\n\r?\n/).filter((block) => block.trim().length > 0);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Split a single SSE event block into its lines, trimming a trailing `\r` so
|
|
41
|
+
* CRLF streams parse identically to LF streams.
|
|
42
|
+
*/
|
|
43
|
+
function splitSSELines(block) {
|
|
44
|
+
return block.split("\n").map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract the SSE `data` field from a single event block's lines.
|
|
48
|
+
*
|
|
49
|
+
* Per the SSE spec a single event may carry MULTIPLE `data:` lines; the field
|
|
50
|
+
* value is every data line's content joined with "\n". Collecting only the
|
|
51
|
+
* first `data:` line (e.g. via `.find`) corrupts payloads that a server split
|
|
52
|
+
* across lines. Callers MUST pass lines produced by {@link splitSSELines} so
|
|
53
|
+
* any trailing `\r` is already stripped. Returns the joined payload (with the
|
|
54
|
+
* leading "data:" prefix and one optional leading space stripped per line), or
|
|
55
|
+
* `undefined` when the block contains no `data:` line.
|
|
56
|
+
*/
|
|
57
|
+
function extractSSEData(lines) {
|
|
58
|
+
const dataParts = [];
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (!line.startsWith("data:")) continue;
|
|
61
|
+
let part = line.slice(5);
|
|
62
|
+
if (part.startsWith(" ")) part = part.slice(1);
|
|
63
|
+
dataParts.push(part);
|
|
64
|
+
}
|
|
65
|
+
if (dataParts.length === 0) return void 0;
|
|
66
|
+
return dataParts.join("\n");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
14
69
|
* Collapse OpenAI Chat Completions SSE stream into a single response.
|
|
15
70
|
*
|
|
16
71
|
* Format:
|
|
@@ -18,24 +73,28 @@ let node_zlib = require("node:zlib");
|
|
|
18
73
|
* data: [DONE]\n\n
|
|
19
74
|
*/
|
|
20
75
|
function collapseOpenAISSE(body) {
|
|
21
|
-
const lines = body
|
|
76
|
+
const lines = splitSSEEvents(body);
|
|
22
77
|
let content = "";
|
|
23
78
|
let reasoning = "";
|
|
24
79
|
const webSearchQueries = [];
|
|
25
80
|
let droppedChunks = 0;
|
|
26
81
|
let firstDroppedSample;
|
|
82
|
+
let harmonyUnparsed = false;
|
|
83
|
+
let harmonyNote;
|
|
27
84
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
85
|
+
let nextSyntheticIndex = 1e6;
|
|
86
|
+
const idKeyMap = /* @__PURE__ */ new Map();
|
|
28
87
|
for (const line of lines) {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
const payload =
|
|
88
|
+
const data = extractSSEData(splitSSELines(line));
|
|
89
|
+
if (data === void 0) continue;
|
|
90
|
+
const payload = data.trim();
|
|
32
91
|
if (payload === "[DONE]") continue;
|
|
33
92
|
let parsed;
|
|
34
93
|
try {
|
|
35
94
|
parsed = JSON.parse(payload);
|
|
36
95
|
} catch (err) {
|
|
37
96
|
droppedChunks++;
|
|
38
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload
|
|
97
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
|
|
39
98
|
continue;
|
|
40
99
|
}
|
|
41
100
|
if (parsed.type === "response.reasoning_summary_text.delta" && typeof parsed.delta === "string") {
|
|
@@ -65,10 +124,20 @@ function collapseOpenAISSE(body) {
|
|
|
65
124
|
if (typeof delta.content === "string") content += delta.content;
|
|
66
125
|
const toolCalls = delta.tool_calls;
|
|
67
126
|
if (toolCalls) for (const tc of toolCalls) {
|
|
68
|
-
const index = tc.index;
|
|
69
127
|
const fn = tc.function;
|
|
128
|
+
const rawId = typeof tc.id === "string" ? tc.id : void 0;
|
|
129
|
+
let index;
|
|
130
|
+
if (typeof tc.index === "number") index = tc.index;
|
|
131
|
+
else if (rawId !== void 0) {
|
|
132
|
+
const existing = idKeyMap.get(rawId);
|
|
133
|
+
if (existing !== void 0) index = existing;
|
|
134
|
+
else {
|
|
135
|
+
index = nextSyntheticIndex++;
|
|
136
|
+
idKeyMap.set(rawId, index);
|
|
137
|
+
}
|
|
138
|
+
} else index = nextSyntheticIndex++;
|
|
70
139
|
if (!toolCallMap.has(index)) toolCallMap.set(index, {
|
|
71
|
-
id:
|
|
140
|
+
id: rawId ?? "",
|
|
72
141
|
name: fn?.name ?? "",
|
|
73
142
|
arguments: ""
|
|
74
143
|
});
|
|
@@ -78,17 +147,33 @@ function collapseOpenAISSE(body) {
|
|
|
78
147
|
if (fn?.arguments && typeof fn.arguments === "string") entry.arguments += fn.arguments;
|
|
79
148
|
}
|
|
80
149
|
}
|
|
81
|
-
|
|
150
|
+
const harmonyToolCalls = [];
|
|
151
|
+
if (toolCallMap.size === 0 && require_harmony.isHarmonyContent(content)) {
|
|
152
|
+
const parsed = require_harmony.parseHarmonyContent(content);
|
|
153
|
+
if (parsed.failed) {
|
|
154
|
+
harmonyUnparsed = true;
|
|
155
|
+
harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;
|
|
156
|
+
} else {
|
|
157
|
+
content = parsed.content;
|
|
158
|
+
if (parsed.reasoning) reasoning += parsed.reasoning;
|
|
159
|
+
harmonyToolCalls.push(...parsed.toolCalls);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (toolCallMap.size > 0 || harmonyToolCalls.length > 0) {
|
|
82
163
|
const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
|
|
83
164
|
return {
|
|
84
165
|
...content ? { content } : {},
|
|
85
|
-
toolCalls: sorted.map(([, tc]) => ({
|
|
166
|
+
toolCalls: [...sorted.map(([, tc]) => ({
|
|
86
167
|
name: tc.name,
|
|
87
168
|
arguments: tc.arguments,
|
|
88
169
|
...tc.id ? { id: tc.id } : {}
|
|
89
|
-
})),
|
|
170
|
+
})), ...harmonyToolCalls],
|
|
171
|
+
...reasoning ? { reasoning } : {},
|
|
172
|
+
...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
|
|
90
173
|
...droppedChunks > 0 ? { droppedChunks } : {},
|
|
91
|
-
...firstDroppedSample ? { firstDroppedSample } : {}
|
|
174
|
+
...firstDroppedSample ? { firstDroppedSample } : {},
|
|
175
|
+
...harmonyUnparsed ? { harmonyUnparsed: true } : {},
|
|
176
|
+
...harmonyNote ? { harmonyNote } : {}
|
|
92
177
|
};
|
|
93
178
|
}
|
|
94
179
|
return {
|
|
@@ -96,7 +181,9 @@ function collapseOpenAISSE(body) {
|
|
|
96
181
|
...reasoning ? { reasoning } : {},
|
|
97
182
|
...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
|
|
98
183
|
...droppedChunks > 0 ? { droppedChunks } : {},
|
|
99
|
-
...firstDroppedSample ? { firstDroppedSample } : {}
|
|
184
|
+
...firstDroppedSample ? { firstDroppedSample } : {},
|
|
185
|
+
...harmonyUnparsed ? { harmonyUnparsed: true } : {},
|
|
186
|
+
...harmonyNote ? { harmonyNote } : {}
|
|
100
187
|
};
|
|
101
188
|
}
|
|
102
189
|
/**
|
|
@@ -107,45 +194,56 @@ function collapseOpenAISSE(body) {
|
|
|
107
194
|
* event: content_block_delta\ndata: {"delta":{"type":"text_delta","text":"Hello"}}\n\n
|
|
108
195
|
*/
|
|
109
196
|
function collapseAnthropicSSE(body) {
|
|
110
|
-
const blocks = body
|
|
197
|
+
const blocks = splitSSEEvents(body);
|
|
111
198
|
let content = "";
|
|
112
199
|
let reasoning = "";
|
|
113
200
|
let droppedChunks = 0;
|
|
114
201
|
let firstDroppedSample;
|
|
115
202
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
203
|
+
let nextSyntheticIndex = 1e6;
|
|
204
|
+
let lastSyntheticIndex;
|
|
116
205
|
for (const block of blocks) {
|
|
117
|
-
const lines = block
|
|
206
|
+
const lines = splitSSELines(block);
|
|
118
207
|
const eventLine = lines.find((l) => l.startsWith("event:"));
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
208
|
+
const data = extractSSEData(lines);
|
|
209
|
+
if (data === void 0) continue;
|
|
121
210
|
const eventType = eventLine ? eventLine.slice(6).trim() : "";
|
|
122
|
-
const payload =
|
|
211
|
+
const payload = data.trim();
|
|
123
212
|
let parsed;
|
|
124
213
|
try {
|
|
125
214
|
parsed = JSON.parse(payload);
|
|
126
215
|
} catch (err) {
|
|
127
216
|
droppedChunks++;
|
|
128
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload
|
|
217
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
|
|
129
218
|
continue;
|
|
130
219
|
}
|
|
131
220
|
if (eventType === "content_block_start") {
|
|
132
|
-
const index = parsed.index;
|
|
133
221
|
const contentBlock = parsed.content_block;
|
|
134
|
-
if (contentBlock?.type === "tool_use")
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
222
|
+
if (contentBlock?.type === "tool_use") {
|
|
223
|
+
let index;
|
|
224
|
+
if (typeof parsed.index === "number") index = parsed.index;
|
|
225
|
+
else index = nextSyntheticIndex++;
|
|
226
|
+
lastSyntheticIndex = index;
|
|
227
|
+
toolCallMap.set(index, {
|
|
228
|
+
id: contentBlock.id ?? "",
|
|
229
|
+
name: contentBlock.name ?? "",
|
|
230
|
+
arguments: ""
|
|
231
|
+
});
|
|
232
|
+
}
|
|
139
233
|
}
|
|
140
234
|
if (eventType === "content_block_delta") {
|
|
141
|
-
const index = parsed.index;
|
|
142
235
|
const delta = parsed.delta;
|
|
143
236
|
if (!delta) continue;
|
|
144
237
|
if (delta.type === "text_delta" && typeof delta.text === "string") content += delta.text;
|
|
145
238
|
if (delta.type === "thinking_delta" && typeof delta.thinking === "string") reasoning += delta.thinking;
|
|
146
239
|
if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
147
|
-
const
|
|
240
|
+
const index = typeof parsed.index === "number" ? parsed.index : lastSyntheticIndex;
|
|
241
|
+
const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
|
|
148
242
|
if (entry) entry.arguments += delta.partial_json;
|
|
243
|
+
else {
|
|
244
|
+
droppedChunks++;
|
|
245
|
+
if (droppedChunks === 1) firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(payload, 200)}`;
|
|
246
|
+
}
|
|
149
247
|
}
|
|
150
248
|
}
|
|
151
249
|
}
|
|
@@ -177,7 +275,7 @@ function collapseAnthropicSSE(body) {
|
|
|
177
275
|
* data: {"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}\n\n
|
|
178
276
|
*/
|
|
179
277
|
function collapseGeminiSSE(body) {
|
|
180
|
-
const lines = body
|
|
278
|
+
const lines = splitSSEEvents(body);
|
|
181
279
|
let content = "";
|
|
182
280
|
let reasoning = "";
|
|
183
281
|
let droppedChunks = 0;
|
|
@@ -186,15 +284,15 @@ function collapseGeminiSSE(body) {
|
|
|
186
284
|
let audioMimeType;
|
|
187
285
|
const toolCalls = [];
|
|
188
286
|
for (const line of lines) {
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
const payload =
|
|
287
|
+
const data = extractSSEData(splitSSELines(line));
|
|
288
|
+
if (data === void 0) continue;
|
|
289
|
+
const payload = data.trim();
|
|
192
290
|
let parsed;
|
|
193
291
|
try {
|
|
194
292
|
parsed = JSON.parse(payload);
|
|
195
293
|
} catch (err) {
|
|
196
294
|
droppedChunks++;
|
|
197
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload
|
|
295
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
|
|
198
296
|
continue;
|
|
199
297
|
}
|
|
200
298
|
const candidates = parsed.candidates;
|
|
@@ -207,7 +305,7 @@ function collapseGeminiSSE(body) {
|
|
|
207
305
|
const fc = part.functionCall;
|
|
208
306
|
toolCalls.push({
|
|
209
307
|
name: String(fc.name ?? ""),
|
|
210
|
-
arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args)
|
|
308
|
+
arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args ?? {})
|
|
211
309
|
});
|
|
212
310
|
} else if (part.inlineData && typeof part.inlineData.mimeType === "string" && part.inlineData.mimeType.startsWith("audio/")) {
|
|
213
311
|
const inlineData = part.inlineData;
|
|
@@ -219,6 +317,9 @@ function collapseGeminiSSE(body) {
|
|
|
219
317
|
if (audioB64) return {
|
|
220
318
|
audioB64,
|
|
221
319
|
audioMimeType,
|
|
320
|
+
...content ? { content } : {},
|
|
321
|
+
...reasoning ? { reasoning } : {},
|
|
322
|
+
...toolCalls.length > 0 ? { toolCalls } : {},
|
|
222
323
|
...droppedChunks > 0 ? { droppedChunks } : {},
|
|
223
324
|
...firstDroppedSample ? { firstDroppedSample } : {}
|
|
224
325
|
};
|
|
@@ -244,12 +345,20 @@ function collapseGeminiSSE(body) {
|
|
|
244
345
|
*
|
|
245
346
|
* /api/generate format:
|
|
246
347
|
* {"model":"llama3","response":"Hello","done":false}\n
|
|
348
|
+
*
|
|
349
|
+
* Open-weight gpt-oss served via Ollama streams harmony channel tokens inside
|
|
350
|
+
* `message.content` (just like the OpenAI SSE path), so after accumulation the
|
|
351
|
+
* content is run through the same fail-safe {@link parseHarmonyContent} gate to
|
|
352
|
+
* capture structured tool calls / reasoning instead of leaking raw tokens.
|
|
247
353
|
*/
|
|
248
354
|
function collapseOllamaNDJSON(body) {
|
|
249
355
|
const lines = body.split("\n").filter((l) => l.trim().length > 0);
|
|
250
356
|
let content = "";
|
|
357
|
+
let reasoning = "";
|
|
251
358
|
let droppedChunks = 0;
|
|
252
359
|
let firstDroppedSample;
|
|
360
|
+
let harmonyUnparsed = false;
|
|
361
|
+
let harmonyNote;
|
|
253
362
|
const toolCalls = [];
|
|
254
363
|
for (const line of lines) {
|
|
255
364
|
let parsed;
|
|
@@ -257,7 +366,7 @@ function collapseOllamaNDJSON(body) {
|
|
|
257
366
|
parsed = JSON.parse(line.trim());
|
|
258
367
|
} catch (err) {
|
|
259
368
|
droppedChunks++;
|
|
260
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${line.trim()
|
|
369
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(line.trim(), 200)}`;
|
|
261
370
|
continue;
|
|
262
371
|
}
|
|
263
372
|
const message = parsed.message;
|
|
@@ -267,21 +376,38 @@ function collapseOllamaNDJSON(body) {
|
|
|
267
376
|
const fn = tc.function;
|
|
268
377
|
if (fn) toolCalls.push({
|
|
269
378
|
name: String(fn.name ?? ""),
|
|
270
|
-
arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments)
|
|
379
|
+
arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {})
|
|
271
380
|
});
|
|
272
381
|
}
|
|
273
382
|
} else if (typeof parsed.response === "string") content += parsed.response;
|
|
274
383
|
}
|
|
384
|
+
if (toolCalls.length === 0 && require_harmony.isHarmonyContent(content)) {
|
|
385
|
+
const parsedHarmony = require_harmony.parseHarmonyContent(content);
|
|
386
|
+
if (parsedHarmony.failed) {
|
|
387
|
+
harmonyUnparsed = true;
|
|
388
|
+
harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;
|
|
389
|
+
} else {
|
|
390
|
+
content = parsedHarmony.content;
|
|
391
|
+
if (parsedHarmony.reasoning) reasoning += parsedHarmony.reasoning;
|
|
392
|
+
toolCalls.push(...parsedHarmony.toolCalls);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
275
395
|
if (toolCalls.length > 0) return {
|
|
276
396
|
...content ? { content } : {},
|
|
277
397
|
toolCalls,
|
|
398
|
+
...reasoning ? { reasoning } : {},
|
|
278
399
|
...droppedChunks > 0 ? { droppedChunks } : {},
|
|
279
|
-
...firstDroppedSample ? { firstDroppedSample } : {}
|
|
400
|
+
...firstDroppedSample ? { firstDroppedSample } : {},
|
|
401
|
+
...harmonyUnparsed ? { harmonyUnparsed: true } : {},
|
|
402
|
+
...harmonyNote ? { harmonyNote } : {}
|
|
280
403
|
};
|
|
281
404
|
return {
|
|
282
405
|
content,
|
|
406
|
+
...reasoning ? { reasoning } : {},
|
|
283
407
|
...droppedChunks > 0 ? { droppedChunks } : {},
|
|
284
|
-
...firstDroppedSample ? { firstDroppedSample } : {}
|
|
408
|
+
...firstDroppedSample ? { firstDroppedSample } : {},
|
|
409
|
+
...harmonyUnparsed ? { harmonyUnparsed: true } : {},
|
|
410
|
+
...harmonyNote ? { harmonyNote } : {}
|
|
285
411
|
};
|
|
286
412
|
}
|
|
287
413
|
/**
|
|
@@ -291,24 +417,26 @@ function collapseOllamaNDJSON(body) {
|
|
|
291
417
|
* event: content-delta\ndata: {"type":"content-delta","delta":{"message":{"content":{"text":"Hello"}}}}\n\n
|
|
292
418
|
*/
|
|
293
419
|
function collapseCohereSSE(body) {
|
|
294
|
-
const blocks = body
|
|
420
|
+
const blocks = splitSSEEvents(body);
|
|
295
421
|
let content = "";
|
|
296
422
|
let droppedChunks = 0;
|
|
297
423
|
let firstDroppedSample;
|
|
298
424
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
425
|
+
let nextSyntheticIndex = 1e6;
|
|
426
|
+
let lastStartKey;
|
|
299
427
|
for (const block of blocks) {
|
|
300
|
-
const lines = block
|
|
428
|
+
const lines = splitSSELines(block);
|
|
301
429
|
const eventLine = lines.find((l) => l.startsWith("event:"));
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
430
|
+
const data = extractSSEData(lines);
|
|
431
|
+
if (data === void 0) continue;
|
|
304
432
|
const eventType = eventLine ? eventLine.slice(6).trim() : "";
|
|
305
|
-
const payload =
|
|
433
|
+
const payload = data.trim();
|
|
306
434
|
let parsed;
|
|
307
435
|
try {
|
|
308
436
|
parsed = JSON.parse(payload);
|
|
309
437
|
} catch (err) {
|
|
310
438
|
droppedChunks++;
|
|
311
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload
|
|
439
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
|
|
312
440
|
continue;
|
|
313
441
|
}
|
|
314
442
|
if (eventType === "content-delta") {
|
|
@@ -316,7 +444,10 @@ function collapseCohereSSE(body) {
|
|
|
316
444
|
if (contentObj && typeof contentObj.text === "string") content += contentObj.text;
|
|
317
445
|
}
|
|
318
446
|
if (eventType === "tool-call-start") {
|
|
319
|
-
|
|
447
|
+
let index;
|
|
448
|
+
if (typeof parsed.index === "number") index = parsed.index;
|
|
449
|
+
else index = nextSyntheticIndex++;
|
|
450
|
+
lastStartKey = index;
|
|
320
451
|
const toolCalls = (parsed.delta?.message)?.tool_calls;
|
|
321
452
|
if (toolCalls) {
|
|
322
453
|
const fn = toolCalls.function;
|
|
@@ -328,13 +459,17 @@ function collapseCohereSSE(body) {
|
|
|
328
459
|
}
|
|
329
460
|
}
|
|
330
461
|
if (eventType === "tool-call-delta") {
|
|
331
|
-
const index = parsed.index;
|
|
462
|
+
const index = typeof parsed.index === "number" ? parsed.index : lastStartKey;
|
|
332
463
|
const toolCalls = (parsed.delta?.message)?.tool_calls;
|
|
333
464
|
if (toolCalls) {
|
|
334
465
|
const fn = toolCalls.function;
|
|
335
466
|
if (fn && typeof fn.arguments === "string") {
|
|
336
|
-
const entry = toolCallMap.get(index);
|
|
467
|
+
const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
|
|
337
468
|
if (entry) entry.arguments += fn.arguments;
|
|
469
|
+
else {
|
|
470
|
+
droppedChunks++;
|
|
471
|
+
if (droppedChunks === 1) firstDroppedSample = `tool-call-delta with no correlating start: ${surrogateSafeSlice(payload, 200)}`;
|
|
472
|
+
}
|
|
338
473
|
}
|
|
339
474
|
}
|
|
340
475
|
}
|
|
@@ -388,22 +523,43 @@ function decodeEventStreamFrames(buf) {
|
|
|
388
523
|
};
|
|
389
524
|
const headersStart = offset + 12;
|
|
390
525
|
const headersEnd = headersStart + headersLength;
|
|
526
|
+
const payloadEnd = offset + totalLength - 4;
|
|
527
|
+
if (headersEnd > payloadEnd || headersEnd > buf.length) return {
|
|
528
|
+
frames,
|
|
529
|
+
truncated: true
|
|
530
|
+
};
|
|
391
531
|
const headers = {};
|
|
392
532
|
let hOffset = headersStart;
|
|
533
|
+
let headerOverrun = false;
|
|
393
534
|
while (hOffset < headersEnd) {
|
|
535
|
+
if (hOffset + 1 > headersEnd) {
|
|
536
|
+
headerOverrun = true;
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
394
539
|
const nameLen = buf.readUInt8(hOffset);
|
|
395
540
|
hOffset += 1;
|
|
541
|
+
if (hOffset + nameLen + 1 + 2 > headersEnd) {
|
|
542
|
+
headerOverrun = true;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
396
545
|
const name = buf.subarray(hOffset, hOffset + nameLen).toString("utf8");
|
|
397
546
|
hOffset += nameLen;
|
|
398
547
|
hOffset += 1;
|
|
399
548
|
const valueLen = buf.readUInt16BE(hOffset);
|
|
400
549
|
hOffset += 2;
|
|
550
|
+
if (hOffset + valueLen > headersEnd) {
|
|
551
|
+
headerOverrun = true;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
401
554
|
const value = buf.subarray(hOffset, hOffset + valueLen).toString("utf8");
|
|
402
555
|
hOffset += valueLen;
|
|
403
556
|
headers[name] = value;
|
|
404
557
|
}
|
|
558
|
+
if (headerOverrun) return {
|
|
559
|
+
frames,
|
|
560
|
+
truncated: true
|
|
561
|
+
};
|
|
405
562
|
const payloadStart = headersEnd;
|
|
406
|
-
const payloadEnd = offset + totalLength - 4;
|
|
407
563
|
const payload = buf.subarray(payloadStart, payloadEnd);
|
|
408
564
|
const messageCrc = buf.readUInt32BE(offset + totalLength - 4);
|
|
409
565
|
const computedMessageCrc = (0, node_zlib.crc32)(buf.subarray(offset, offset + totalLength - 4));
|
|
@@ -441,7 +597,7 @@ function collapseBedrockEventStream(body) {
|
|
|
441
597
|
parsed = JSON.parse(frameStr);
|
|
442
598
|
} catch (err) {
|
|
443
599
|
droppedChunks++;
|
|
444
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${frameStr
|
|
600
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(frameStr, 200)}`;
|
|
445
601
|
continue;
|
|
446
602
|
}
|
|
447
603
|
if (parsed.type === "content_block_delta") {
|
|
@@ -449,9 +605,11 @@ function collapseBedrockEventStream(body) {
|
|
|
449
605
|
if (delta?.type === "text_delta" && typeof delta.text === "string") content += delta.text;
|
|
450
606
|
if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
451
607
|
const index = parsed.index;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
608
|
+
const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
|
|
609
|
+
if (entry) entry.arguments += delta.partial_json;
|
|
610
|
+
else {
|
|
611
|
+
droppedChunks++;
|
|
612
|
+
if (droppedChunks === 1) firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(frameStr, 200)}`;
|
|
455
613
|
}
|
|
456
614
|
}
|
|
457
615
|
continue;
|
|
@@ -487,9 +645,13 @@ function collapseBedrockEventStream(body) {
|
|
|
487
645
|
if (typeof delta.text === "string") content += delta.text;
|
|
488
646
|
if (typeof delta.toolUse === "object" && delta.toolUse !== null) {
|
|
489
647
|
const toolUseDelta = delta.toolUse;
|
|
490
|
-
if (typeof toolUseDelta.input === "string"
|
|
491
|
-
const entry = toolCallMap.get(index);
|
|
648
|
+
if (typeof toolUseDelta.input === "string") {
|
|
649
|
+
const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
|
|
492
650
|
if (entry) entry.arguments += toolUseDelta.input;
|
|
651
|
+
else {
|
|
652
|
+
droppedChunks++;
|
|
653
|
+
if (droppedChunks === 1) firstDroppedSample = `toolUse.input delta with no correlating tool_use start: ${surrogateSafeSlice(frameStr, 200)}`;
|
|
654
|
+
}
|
|
493
655
|
}
|
|
494
656
|
}
|
|
495
657
|
}
|
|
@@ -519,22 +681,22 @@ function collapseBedrockEventStream(body) {
|
|
|
519
681
|
* data: {"event_type":"interaction.complete","interaction":{"id":"...","usage":{...}}}\n\n
|
|
520
682
|
*/
|
|
521
683
|
function collapseGeminiInteractionsSSE(body) {
|
|
522
|
-
const lines = body
|
|
684
|
+
const lines = splitSSEEvents(body);
|
|
523
685
|
let content = "";
|
|
524
686
|
let reasoning = "";
|
|
525
687
|
let droppedChunks = 0;
|
|
526
688
|
let firstDroppedSample;
|
|
527
689
|
const toolCalls = [];
|
|
528
690
|
for (const line of lines) {
|
|
529
|
-
const
|
|
530
|
-
if (
|
|
531
|
-
const payload =
|
|
691
|
+
const data = extractSSEData(splitSSELines(line));
|
|
692
|
+
if (data === void 0) continue;
|
|
693
|
+
const payload = data.trim();
|
|
532
694
|
let parsed;
|
|
533
695
|
try {
|
|
534
696
|
parsed = JSON.parse(payload);
|
|
535
697
|
} catch (err) {
|
|
536
698
|
droppedChunks++;
|
|
537
|
-
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload
|
|
699
|
+
if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
|
|
538
700
|
continue;
|
|
539
701
|
}
|
|
540
702
|
const eventType = parsed.event_type;
|