@copilotkit/aimock 1.15.0 → 1.16.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/plugin.json +1 -1
- package/CHANGELOG.md +33 -15
- package/README.md +1 -1
- package/dist/bedrock-converse.cjs +133 -25
- 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 +135 -27
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +262 -48
- 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 +263 -50
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +9 -35
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +2 -17
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +2 -17
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +10 -35
- package/dist/chaos.js.map +1 -1
- package/dist/cohere.cjs +289 -33
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts +9 -0
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts +9 -0
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +290 -34
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/embeddings.cjs +22 -4
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +2 -2
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +22 -4
- package/dist/embeddings.js.map +1 -1
- package/dist/fixture-loader.cjs +19 -4
- 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 +19 -4
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini.cjs +48 -45
- 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 +48 -45
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +9 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +9 -0
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +21 -3
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +21 -3
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/jest.cjs +10 -3
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.js +10 -3
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +1 -1
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +1 -1
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +6 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +1 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +1 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +6 -0
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +5 -4
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +5 -4
- package/dist/messages.js.map +1 -1
- package/dist/ollama.cjs +129 -8
- 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 +130 -9
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +234 -69
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +5 -50
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +5 -50
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +234 -69
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +12 -3
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +2 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +2 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +12 -4
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +19 -6
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +19 -6
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +150 -94
- 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 +152 -96
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +21 -3
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +21 -3
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +10 -6
- 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 +10 -6
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +5 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +5 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/video.cjs +66 -10
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +16 -3
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +16 -3
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +66 -11
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +10 -3
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.js +10 -3
- package/dist/vitest.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +75 -49
package/dist/recorder.js
CHANGED
|
@@ -28,14 +28,17 @@ const STRIP_HEADERS = new Set([
|
|
|
28
28
|
* Proxy an unmatched request to the real upstream provider, record the
|
|
29
29
|
* response as a fixture on disk and in memory, then relay the response
|
|
30
30
|
* back to the original client.
|
|
31
|
+
*
|
|
32
|
+
* Returns `true` if the request was proxied (provider configured),
|
|
33
|
+
* `false` if no upstream URL is configured for the given provider key.
|
|
31
34
|
*/
|
|
32
|
-
async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures, defaults, rawBody
|
|
35
|
+
async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures, defaults, rawBody) {
|
|
33
36
|
const record = defaults.record;
|
|
34
|
-
if (!record) return
|
|
37
|
+
if (!record) return false;
|
|
35
38
|
const upstreamUrl = record.providers[providerKey];
|
|
36
39
|
if (!upstreamUrl) {
|
|
37
40
|
defaults.logger.warn(`No upstream URL configured for provider "${providerKey}" — cannot proxy`);
|
|
38
|
-
return
|
|
41
|
+
return false;
|
|
39
42
|
}
|
|
40
43
|
const fixturePath = record.fixturePath ?? "./fixtures/recorded";
|
|
41
44
|
let target;
|
|
@@ -47,7 +50,7 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
47
50
|
message: `Invalid upstream URL: ${upstreamUrl}`,
|
|
48
51
|
type: "proxy_error"
|
|
49
52
|
} }));
|
|
50
|
-
return
|
|
53
|
+
return true;
|
|
51
54
|
}
|
|
52
55
|
defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);
|
|
53
56
|
const forwardHeaders = {};
|
|
@@ -58,6 +61,7 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
58
61
|
let upstreamBody;
|
|
59
62
|
let rawBuffer;
|
|
60
63
|
let streamedToClient = false;
|
|
64
|
+
let clientDisconnected = false;
|
|
61
65
|
try {
|
|
62
66
|
const result = await makeUpstreamRequest(target, forwardHeaders, requestBody, res);
|
|
63
67
|
upstreamStatus = result.status;
|
|
@@ -65,15 +69,18 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
65
69
|
upstreamBody = result.body;
|
|
66
70
|
rawBuffer = result.rawBuffer;
|
|
67
71
|
streamedToClient = result.streamedToClient;
|
|
72
|
+
clientDisconnected = result.clientDisconnected;
|
|
68
73
|
} catch (err) {
|
|
69
74
|
const msg = err instanceof Error ? err.message : "Unknown proxy error";
|
|
70
75
|
defaults.logger.error(`Proxy request failed: ${msg}`);
|
|
71
|
-
res.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
if (!res.headersSent) {
|
|
77
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
78
|
+
res.end(JSON.stringify({ error: {
|
|
79
|
+
message: `Proxy to upstream failed: ${msg}`,
|
|
80
|
+
type: "proxy_error"
|
|
81
|
+
} }));
|
|
82
|
+
} else res.end();
|
|
83
|
+
return true;
|
|
77
84
|
}
|
|
78
85
|
const contentType = upstreamHeaders["content-type"];
|
|
79
86
|
const ctString = Array.isArray(contentType) ? contentType.join(", ") : contentType ?? "";
|
|
@@ -91,10 +98,20 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
91
98
|
if (collapsed.truncated) defaults.logger.warn("Bedrock EventStream: CRC mismatch — response may be truncated");
|
|
92
99
|
if (collapsed.droppedChunks && collapsed.droppedChunks > 0) defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse`);
|
|
93
100
|
if (collapsed.content === "" && (!collapsed.toolCalls || collapsed.toolCalls.length === 0)) defaults.logger.warn("Stream collapse produced empty content — fixture may be incomplete");
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
const reasoningSpread = collapsed.reasoning ? { reasoning: collapsed.reasoning } : {};
|
|
102
|
+
if (collapsed.toolCalls && collapsed.toolCalls.length > 0) if (collapsed.content) fixtureResponse = {
|
|
103
|
+
content: collapsed.content,
|
|
104
|
+
toolCalls: collapsed.toolCalls,
|
|
105
|
+
...reasoningSpread
|
|
106
|
+
};
|
|
107
|
+
else fixtureResponse = {
|
|
108
|
+
toolCalls: collapsed.toolCalls,
|
|
109
|
+
...reasoningSpread
|
|
110
|
+
};
|
|
111
|
+
else fixtureResponse = {
|
|
112
|
+
content: collapsed.content ?? "",
|
|
113
|
+
...reasoningSpread
|
|
114
|
+
};
|
|
98
115
|
} else {
|
|
99
116
|
let parsedResponse = null;
|
|
100
117
|
try {
|
|
@@ -105,9 +122,15 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
105
122
|
let encodingFormat;
|
|
106
123
|
try {
|
|
107
124
|
encodingFormat = rawBody ? JSON.parse(rawBody).encoding_format : void 0;
|
|
108
|
-
} catch {
|
|
125
|
+
} catch (err) {
|
|
126
|
+
defaults.logger.debug(`Could not parse encoding_format from raw body: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
127
|
+
}
|
|
109
128
|
fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, encodingFormat);
|
|
110
129
|
}
|
|
130
|
+
if (clientDisconnected) {
|
|
131
|
+
defaults.logger.warn("Client disconnected mid-stream — skipping fixture save to avoid truncated data");
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
111
134
|
const fixtureMatch = buildFixtureMatch(defaults.requestTransform ? defaults.requestTransform(request) : request);
|
|
112
135
|
const fixture = {
|
|
113
136
|
match: fixtureMatch,
|
|
@@ -132,29 +155,22 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
|
|
|
132
155
|
} catch (err) {
|
|
133
156
|
const msg = err instanceof Error ? err.message : "Unknown filesystem error";
|
|
134
157
|
defaults.logger.error(`Failed to save fixture to disk: ${msg}`);
|
|
135
|
-
res.setHeader("X-LLMock-Record-Error", msg);
|
|
158
|
+
if (!res.headersSent) res.setHeader("X-LLMock-Record-Error", msg);
|
|
159
|
+
else defaults.logger.warn(`Cannot set X-LLMock-Record-Error header — headers already sent`);
|
|
136
160
|
}
|
|
137
161
|
if (writtenToDisk) {
|
|
138
162
|
if (!isEmptyMatch) fixtures.push(fixture);
|
|
139
163
|
defaults.logger.warn(`Response recorded → ${filepath}`);
|
|
140
164
|
} else defaults.logger.warn(`Response relayed but NOT saved to disk — see error above`);
|
|
141
165
|
} else defaults.logger.info(`Proxied ${providerKey} request (proxy-only mode)`);
|
|
142
|
-
if (streamedToClient) {
|
|
143
|
-
if (options?.beforeWriteResponse && options.onHookBypassed) options.onHookBypassed("sse_streamed");
|
|
144
|
-
} else {
|
|
145
|
-
if (options?.beforeWriteResponse) {
|
|
146
|
-
if (await options.beforeWriteResponse({
|
|
147
|
-
status: upstreamStatus,
|
|
148
|
-
contentType: ctString,
|
|
149
|
-
body: rawBuffer
|
|
150
|
-
})) return "handled_by_hook";
|
|
151
|
-
}
|
|
166
|
+
if (!streamedToClient) {
|
|
152
167
|
const relayHeaders = {};
|
|
153
168
|
if (ctString) relayHeaders["Content-Type"] = ctString;
|
|
154
169
|
res.writeHead(upstreamStatus, relayHeaders);
|
|
155
|
-
|
|
170
|
+
const isAudioRelay = ctString.toLowerCase().startsWith("audio/");
|
|
171
|
+
res.end(isBinaryStream || isAudioRelay ? rawBuffer : upstreamBody);
|
|
156
172
|
}
|
|
157
|
-
return
|
|
173
|
+
return true;
|
|
158
174
|
}
|
|
159
175
|
function makeUpstreamRequest(target, headers, body, clientRes) {
|
|
160
176
|
return new Promise((resolve, reject) => {
|
|
@@ -176,28 +192,34 @@ function makeUpstreamRequest(target, headers, body, clientRes) {
|
|
|
176
192
|
const ctStr = Array.isArray(ct) ? ct.join(", ") : ct ?? "";
|
|
177
193
|
const isSSE = ctStr.toLowerCase().includes("text/event-stream");
|
|
178
194
|
let streamedToClient = false;
|
|
195
|
+
let clientDisconnected = false;
|
|
179
196
|
if (isSSE && clientRes && !clientRes.headersSent) {
|
|
180
197
|
const relayHeaders = {};
|
|
181
198
|
if (ctStr) relayHeaders["Content-Type"] = ctStr;
|
|
182
199
|
clientRes.writeHead(res.statusCode ?? 200, relayHeaders);
|
|
183
200
|
if (typeof clientRes.flushHeaders === "function") clientRes.flushHeaders();
|
|
184
201
|
streamedToClient = true;
|
|
202
|
+
clientRes.on("close", () => {
|
|
203
|
+
clientDisconnected = true;
|
|
204
|
+
req.destroy();
|
|
205
|
+
});
|
|
185
206
|
}
|
|
186
207
|
const chunks = [];
|
|
187
208
|
res.on("data", (chunk) => {
|
|
188
209
|
chunks.push(chunk);
|
|
189
|
-
if (streamedToClient) clientRes.write(chunk);
|
|
210
|
+
if (streamedToClient && clientRes && !clientDisconnected && !clientRes.destroyed && !clientRes.writableEnded) clientRes.write(chunk);
|
|
190
211
|
});
|
|
191
212
|
res.on("error", reject);
|
|
192
213
|
res.on("end", () => {
|
|
193
214
|
const rawBuffer = Buffer.concat(chunks);
|
|
194
|
-
if (streamedToClient) clientRes.end();
|
|
215
|
+
if (streamedToClient && clientRes && !clientDisconnected && !clientRes.destroyed && !clientRes.writableEnded) clientRes.end();
|
|
195
216
|
resolve({
|
|
196
217
|
status: res.statusCode ?? 500,
|
|
197
218
|
headers: res.headers,
|
|
198
219
|
body: rawBuffer.toString(),
|
|
199
220
|
rawBuffer,
|
|
200
|
-
streamedToClient
|
|
221
|
+
streamedToClient,
|
|
222
|
+
clientDisconnected
|
|
201
223
|
});
|
|
202
224
|
});
|
|
203
225
|
});
|
|
@@ -222,7 +244,7 @@ function buildFixtureResponse(parsed, status, encodingFormat) {
|
|
|
222
244
|
status
|
|
223
245
|
};
|
|
224
246
|
const obj = parsed;
|
|
225
|
-
if (obj.error) {
|
|
247
|
+
if (typeof obj.error === "object" && obj.error !== null && typeof obj.error.message === "string") {
|
|
226
248
|
const err = obj.error;
|
|
227
249
|
return {
|
|
228
250
|
error: {
|
|
@@ -236,11 +258,12 @@ function buildFixtureResponse(parsed, status, encodingFormat) {
|
|
|
236
258
|
if (Array.isArray(obj.data) && obj.data.length > 0) {
|
|
237
259
|
const first = obj.data[0];
|
|
238
260
|
if (Array.isArray(first.embedding)) return { embedding: first.embedding };
|
|
239
|
-
if (typeof first.embedding === "string" && encodingFormat === "base64")
|
|
261
|
+
if (typeof first.embedding === "string" && encodingFormat === "base64") {
|
|
240
262
|
const buf = Buffer.from(first.embedding, "base64");
|
|
241
|
-
const
|
|
263
|
+
const aligned = new Uint8Array(buf).buffer;
|
|
264
|
+
const floats = new Float32Array(aligned, 0, buf.byteLength / 4);
|
|
242
265
|
return { embedding: Array.from(floats) };
|
|
243
|
-
}
|
|
266
|
+
}
|
|
244
267
|
if (first.url || first.b64_json) {
|
|
245
268
|
const images = obj.data.map((item) => ({
|
|
246
269
|
...item.url ? { url: String(item.url) } : {},
|
|
@@ -266,7 +289,7 @@ function buildFixtureResponse(parsed, status, encodingFormat) {
|
|
|
266
289
|
...Array.isArray(obj.words) ? { words: obj.words } : {},
|
|
267
290
|
...Array.isArray(obj.segments) ? { segments: obj.segments } : {}
|
|
268
291
|
} };
|
|
269
|
-
if (typeof obj.id === "string" && typeof obj.status === "string" && (obj.status === "completed" || obj.status === "in_progress" || obj.status === "failed")) {
|
|
292
|
+
if (typeof obj.id === "string" && typeof obj.status === "string" && (obj.status === "completed" || obj.status === "in_progress" || obj.status === "failed") && !("choices" in obj) && !("content" in obj) && !("candidates" in obj) && !("message" in obj) && !("data" in obj) && !("object" in obj)) {
|
|
270
293
|
if (obj.status === "completed" && obj.url) return { video: {
|
|
271
294
|
id: String(obj.id),
|
|
272
295
|
status: "completed",
|
|
@@ -281,40 +304,109 @@ function buildFixtureResponse(parsed, status, encodingFormat) {
|
|
|
281
304
|
if (Array.isArray(obj.choices) && obj.choices.length > 0) {
|
|
282
305
|
const message = obj.choices[0].message;
|
|
283
306
|
if (message) {
|
|
284
|
-
|
|
285
|
-
|
|
307
|
+
const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
308
|
+
const hasContent = typeof message.content === "string" && message.content.length > 0;
|
|
309
|
+
const openaiReasoning = typeof message.reasoning_content === "string" && message.reasoning_content.length > 0 ? message.reasoning_content : void 0;
|
|
310
|
+
if (hasToolCalls) {
|
|
311
|
+
const toolCalls = message.tool_calls.map((tc) => {
|
|
312
|
+
const fn = tc.function;
|
|
313
|
+
return {
|
|
314
|
+
name: String(fn.name),
|
|
315
|
+
arguments: String(fn.arguments),
|
|
316
|
+
...tc.id ? { id: String(tc.id) } : {}
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
if (hasContent) return {
|
|
320
|
+
content: message.content,
|
|
321
|
+
toolCalls,
|
|
322
|
+
...openaiReasoning ? { reasoning: openaiReasoning } : {}
|
|
323
|
+
};
|
|
286
324
|
return {
|
|
287
|
-
|
|
288
|
-
|
|
325
|
+
toolCalls,
|
|
326
|
+
...openaiReasoning ? { reasoning: openaiReasoning } : {}
|
|
289
327
|
};
|
|
290
|
-
}
|
|
291
|
-
if (
|
|
328
|
+
}
|
|
329
|
+
if (hasContent) return {
|
|
330
|
+
content: message.content,
|
|
331
|
+
...openaiReasoning ? { reasoning: openaiReasoning } : {}
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
content: "",
|
|
335
|
+
...openaiReasoning ? { reasoning: openaiReasoning } : {}
|
|
336
|
+
};
|
|
292
337
|
}
|
|
293
338
|
}
|
|
294
339
|
if (Array.isArray(obj.content) && obj.content.length > 0) {
|
|
295
340
|
const blocks = obj.content;
|
|
296
341
|
const toolUseBlocks = blocks.filter((b) => b.type === "tool_use");
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
342
|
+
const textBlocks = blocks.filter((b) => b.type === "text" && typeof b.text === "string");
|
|
343
|
+
const thinkingBlocks = blocks.filter((b) => b.type === "thinking");
|
|
344
|
+
const hasToolCalls = toolUseBlocks.length > 0;
|
|
345
|
+
const joinedText = textBlocks.map((b) => String(b.text ?? "")).join("");
|
|
346
|
+
const hasContent = joinedText.length > 0;
|
|
347
|
+
const anthropicReasoning = thinkingBlocks.length > 0 ? thinkingBlocks.map((b) => String(b.thinking ?? "")).join("") : void 0;
|
|
348
|
+
if (hasToolCalls) {
|
|
349
|
+
const toolCalls = toolUseBlocks.map((b) => ({
|
|
350
|
+
name: String(b.name),
|
|
351
|
+
arguments: typeof b.input === "string" ? b.input : JSON.stringify(b.input),
|
|
352
|
+
...b.id ? { id: String(b.id) } : {}
|
|
353
|
+
}));
|
|
354
|
+
if (hasContent) return {
|
|
355
|
+
content: joinedText,
|
|
356
|
+
toolCalls,
|
|
357
|
+
...anthropicReasoning ? { reasoning: anthropicReasoning } : {}
|
|
358
|
+
};
|
|
359
|
+
return {
|
|
360
|
+
toolCalls,
|
|
361
|
+
...anthropicReasoning ? { reasoning: anthropicReasoning } : {}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (hasContent) return {
|
|
365
|
+
content: joinedText,
|
|
366
|
+
...anthropicReasoning ? { reasoning: anthropicReasoning } : {}
|
|
367
|
+
};
|
|
368
|
+
if (anthropicReasoning) return {
|
|
369
|
+
content: "",
|
|
370
|
+
reasoning: anthropicReasoning
|
|
371
|
+
};
|
|
303
372
|
}
|
|
304
373
|
if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {
|
|
305
374
|
const content = obj.candidates[0].content;
|
|
306
375
|
if (content && Array.isArray(content.parts)) {
|
|
307
376
|
const parts = content.parts;
|
|
308
377
|
const fnCallParts = parts.filter((p) => p.functionCall);
|
|
309
|
-
|
|
310
|
-
|
|
378
|
+
const textParts = parts.filter((p) => typeof p.text === "string" && !p.thought);
|
|
379
|
+
const thoughtParts = parts.filter((p) => p.thought === true && typeof p.text === "string");
|
|
380
|
+
const hasToolCalls = fnCallParts.length > 0;
|
|
381
|
+
const joinedText = textParts.map((p) => String(p.text ?? "")).join("");
|
|
382
|
+
const hasContent = joinedText.length > 0;
|
|
383
|
+
const geminiReasoning = thoughtParts.length > 0 ? thoughtParts.map((p) => String(p.text ?? "")).join("") : void 0;
|
|
384
|
+
if (hasToolCalls) {
|
|
385
|
+
const toolCalls = fnCallParts.map((p) => {
|
|
386
|
+
const fc = p.functionCall;
|
|
387
|
+
return {
|
|
388
|
+
name: String(fc.name),
|
|
389
|
+
arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args)
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
if (hasContent) return {
|
|
393
|
+
content: joinedText,
|
|
394
|
+
toolCalls,
|
|
395
|
+
...geminiReasoning ? { reasoning: geminiReasoning } : {}
|
|
396
|
+
};
|
|
311
397
|
return {
|
|
312
|
-
|
|
313
|
-
|
|
398
|
+
toolCalls,
|
|
399
|
+
...geminiReasoning ? { reasoning: geminiReasoning } : {}
|
|
314
400
|
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
401
|
+
}
|
|
402
|
+
if (hasContent) return {
|
|
403
|
+
content: joinedText,
|
|
404
|
+
...geminiReasoning ? { reasoning: geminiReasoning } : {}
|
|
405
|
+
};
|
|
406
|
+
return {
|
|
407
|
+
content: "",
|
|
408
|
+
...geminiReasoning ? { reasoning: geminiReasoning } : {}
|
|
409
|
+
};
|
|
318
410
|
}
|
|
319
411
|
}
|
|
320
412
|
if (obj.output && typeof obj.output === "object") {
|
|
@@ -322,32 +414,105 @@ function buildFixtureResponse(parsed, status, encodingFormat) {
|
|
|
322
414
|
if (msg && Array.isArray(msg.content)) {
|
|
323
415
|
const blocks = msg.content;
|
|
324
416
|
const toolUseBlocks = blocks.filter((b) => b.toolUse);
|
|
325
|
-
|
|
326
|
-
|
|
417
|
+
const textBlocks = blocks.filter((b) => typeof b.text === "string");
|
|
418
|
+
const reasoningBlocks = blocks.filter((b) => b.reasoningContent);
|
|
419
|
+
const hasToolCalls = toolUseBlocks.length > 0;
|
|
420
|
+
const joinedText = textBlocks.map((b) => String(b.text ?? "")).join("");
|
|
421
|
+
const hasContent = joinedText.length > 0;
|
|
422
|
+
const bedrockReasoning = reasoningBlocks.length > 0 ? reasoningBlocks.map((b) => {
|
|
423
|
+
const rt = b.reasoningContent?.reasoningText;
|
|
424
|
+
return String(rt?.text ?? "");
|
|
425
|
+
}).join("") : void 0;
|
|
426
|
+
if (hasToolCalls) {
|
|
427
|
+
const toolCalls = toolUseBlocks.map((b) => {
|
|
428
|
+
const tu = b.toolUse;
|
|
429
|
+
return {
|
|
430
|
+
name: String(tu.name ?? ""),
|
|
431
|
+
arguments: typeof tu.input === "string" ? tu.input : JSON.stringify(tu.input),
|
|
432
|
+
...tu.toolUseId ? { id: String(tu.toolUseId) } : {}
|
|
433
|
+
};
|
|
434
|
+
});
|
|
435
|
+
if (hasContent) return {
|
|
436
|
+
content: joinedText,
|
|
437
|
+
toolCalls,
|
|
438
|
+
...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
|
|
439
|
+
};
|
|
327
440
|
return {
|
|
328
|
-
|
|
329
|
-
|
|
441
|
+
toolCalls,
|
|
442
|
+
...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
|
|
330
443
|
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
444
|
+
}
|
|
445
|
+
if (hasContent) return {
|
|
446
|
+
content: joinedText,
|
|
447
|
+
...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
|
|
448
|
+
};
|
|
449
|
+
return {
|
|
450
|
+
content: "",
|
|
451
|
+
...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
|
|
452
|
+
};
|
|
334
453
|
}
|
|
335
454
|
}
|
|
455
|
+
if (typeof obj.finish_reason === "string" && obj.message && typeof obj.message === "object" && Array.isArray(obj.message.content)) {
|
|
456
|
+
const msg = obj.message;
|
|
457
|
+
const contentBlocks = msg.content;
|
|
458
|
+
const textBlock = contentBlocks.find((b) => b.type === "text" && typeof b.text === "string");
|
|
459
|
+
const hasContent = textBlock && typeof textBlock.text === "string" && textBlock.text.length > 0;
|
|
460
|
+
const toolCallBlocks = contentBlocks.filter((b) => b.type === "tool_call");
|
|
461
|
+
const msgToolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
462
|
+
if (toolCallBlocks.length > 0) {
|
|
463
|
+
const toolCalls = toolCallBlocks.map((b) => ({
|
|
464
|
+
name: String(b.name ?? b.function?.name ?? ""),
|
|
465
|
+
arguments: typeof b.parameters === "string" ? b.parameters : typeof b.parameters === "object" ? JSON.stringify(b.parameters) : typeof b.function?.arguments === "string" ? String(b.function.arguments) : JSON.stringify(b.function?.arguments),
|
|
466
|
+
...b.id ? { id: String(b.id) } : {}
|
|
467
|
+
}));
|
|
468
|
+
if (hasContent) return {
|
|
469
|
+
content: textBlock.text,
|
|
470
|
+
toolCalls
|
|
471
|
+
};
|
|
472
|
+
return { toolCalls };
|
|
473
|
+
}
|
|
474
|
+
if (msgToolCalls.length > 0) {
|
|
475
|
+
const toolCalls = msgToolCalls.map((tc) => {
|
|
476
|
+
const fn = tc.function;
|
|
477
|
+
return {
|
|
478
|
+
name: String(tc.name ?? fn?.name ?? ""),
|
|
479
|
+
arguments: typeof tc.parameters === "string" ? tc.parameters : typeof tc.parameters === "object" ? JSON.stringify(tc.parameters) : typeof fn?.arguments === "string" ? String(fn.arguments) : JSON.stringify(fn?.arguments),
|
|
480
|
+
...tc.id ? { id: String(tc.id) } : {}
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
if (hasContent) return {
|
|
484
|
+
content: textBlock.text,
|
|
485
|
+
toolCalls
|
|
486
|
+
};
|
|
487
|
+
return { toolCalls };
|
|
488
|
+
}
|
|
489
|
+
if (hasContent) return { content: textBlock.text };
|
|
490
|
+
}
|
|
336
491
|
if (obj.message && typeof obj.message === "object") {
|
|
337
492
|
const msg = obj.message;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
493
|
+
const hasOllamaToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
|
|
494
|
+
const hasOllamaContent = typeof msg.content === "string" && msg.content.length > 0;
|
|
495
|
+
if (hasOllamaToolCalls) {
|
|
496
|
+
const toolCalls = msg.tool_calls.filter((tc) => tc.function != null).map((tc) => {
|
|
497
|
+
const fn = tc.function;
|
|
498
|
+
return {
|
|
499
|
+
name: String(fn.name ?? ""),
|
|
500
|
+
arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments)
|
|
501
|
+
};
|
|
502
|
+
});
|
|
503
|
+
if (hasOllamaContent) return {
|
|
504
|
+
content: msg.content,
|
|
505
|
+
toolCalls
|
|
343
506
|
};
|
|
344
|
-
|
|
345
|
-
|
|
507
|
+
return { toolCalls };
|
|
508
|
+
}
|
|
509
|
+
if (hasOllamaContent) return { content: msg.content };
|
|
346
510
|
if (Array.isArray(msg.content) && msg.content.length > 0) {
|
|
347
511
|
const first = msg.content[0];
|
|
348
512
|
if (typeof first.text === "string") return { content: first.text };
|
|
349
513
|
}
|
|
350
514
|
}
|
|
515
|
+
if (typeof obj.response === "string" && "done" in obj) return { content: obj.response };
|
|
351
516
|
return {
|
|
352
517
|
error: {
|
|
353
518
|
message: "Could not detect response format from upstream",
|