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