@agentskit/adapters 0.13.12 → 0.13.16
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/README.md +33 -3
- package/dist/catalog/index.cjs +197 -60
- package/dist/catalog/index.cjs.map +1 -1
- package/dist/catalog/index.d.cts +1 -1
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +2 -1
- package/dist/catalog/index.js.map +1 -1
- package/dist/{chunk-77QS3QRT.js → chunk-4KZTWGRB.js} +249 -119
- package/dist/chunk-4KZTWGRB.js.map +1 -0
- package/dist/chunk-627FHZZY.js +101 -0
- package/dist/chunk-627FHZZY.js.map +1 -0
- package/dist/chunk-6CIB2QCH.js +81 -0
- package/dist/chunk-6CIB2QCH.js.map +1 -0
- package/dist/createAdapter-C-pKR1aO.d.cts +19 -0
- package/dist/createAdapter-C-pKR1aO.d.ts +19 -0
- package/dist/createAdapter.cjs +138 -15
- package/dist/createAdapter.cjs.map +1 -1
- package/dist/createAdapter.d.cts +1 -1
- package/dist/createAdapter.d.ts +1 -1
- package/dist/createAdapter.js +2 -1
- package/dist/index.cjs +1090 -319
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -13
- package/dist/index.d.ts +33 -13
- package/dist/index.js +686 -191
- package/dist/index.js.map +1 -1
- package/package.json +11 -6
- package/dist/chunk-6FPLWOVE.js +0 -26
- package/dist/chunk-6FPLWOVE.js.map +0 -1
- package/dist/chunk-77QS3QRT.js.map +0 -1
- package/dist/createAdapter-Dfnf3Iio.d.cts +0 -14
- package/dist/createAdapter-Dfnf3Iio.d.ts +0 -14
package/dist/index.js
CHANGED
|
@@ -1,43 +1,206 @@
|
|
|
1
|
-
export { createAdapter } from './chunk-
|
|
2
|
-
import { createStreamSource, parseAnthropicStream, parseGeminiStream, parseOllamaStream, toProviderMessages, parseOpenAIStream, openai } from './chunk-
|
|
3
|
-
export { chunkText, fetchWithRetry, openai, simulateStream } from './chunk-
|
|
1
|
+
export { createAdapter } from './chunk-627FHZZY.js';
|
|
2
|
+
import { createStreamSource, parseAnthropicStream, parseGeminiStream, parseOllamaStream, toProviderMessages, parseOpenAIStream, openai, readSSELines } from './chunk-4KZTWGRB.js';
|
|
3
|
+
export { chunkText, fetchWithRetry, openai, simulateStream } from './chunk-4KZTWGRB.js';
|
|
4
|
+
import { raceAbort, isAbortError, adapterErrorChunk, abortableSleep, parseCompleteToolArgs } from './chunk-6CIB2QCH.js';
|
|
4
5
|
import { ConfigError, ErrorCodes, AdapterError } from '@agentskit/core';
|
|
5
6
|
|
|
6
7
|
// src/generic.ts
|
|
7
8
|
function generic(config) {
|
|
8
9
|
return {
|
|
9
10
|
createSource: (request) => {
|
|
11
|
+
const controller = new AbortController();
|
|
10
12
|
let reader = null;
|
|
11
13
|
const decoder = new TextDecoder();
|
|
12
14
|
return {
|
|
13
15
|
stream: async function* () {
|
|
16
|
+
if (controller.signal.aborted) return;
|
|
14
17
|
try {
|
|
15
|
-
const stream = await
|
|
18
|
+
const stream = await raceAbort(
|
|
19
|
+
config.send(request, controller.signal),
|
|
20
|
+
controller.signal
|
|
21
|
+
);
|
|
22
|
+
if (controller.signal.aborted) {
|
|
23
|
+
try {
|
|
24
|
+
await stream.cancel();
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
16
29
|
reader = stream.getReader();
|
|
17
30
|
while (true) {
|
|
18
|
-
|
|
31
|
+
if (controller.signal.aborted) {
|
|
32
|
+
try {
|
|
33
|
+
await reader.cancel();
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const { done, value } = await raceAbort(reader.read(), controller.signal);
|
|
19
39
|
if (done) break;
|
|
20
40
|
const text = decoder.decode(value, { stream: true });
|
|
21
41
|
yield { type: "text", content: text };
|
|
22
42
|
}
|
|
43
|
+
if (controller.signal.aborted) return;
|
|
23
44
|
yield { type: "done" };
|
|
24
45
|
} catch (err) {
|
|
46
|
+
if (isAbortError(err) || controller.signal.aborted) return;
|
|
25
47
|
yield {
|
|
26
48
|
type: "error",
|
|
27
|
-
content: err instanceof Error ? err.message : String(err)
|
|
49
|
+
content: err instanceof Error ? err.message : String(err),
|
|
50
|
+
metadata: {
|
|
51
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
52
|
+
}
|
|
28
53
|
};
|
|
29
54
|
} finally {
|
|
30
|
-
|
|
55
|
+
try {
|
|
56
|
+
reader?.releaseLock();
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
31
59
|
}
|
|
32
60
|
},
|
|
33
61
|
abort: () => {
|
|
34
|
-
|
|
62
|
+
if (!controller.signal.aborted) controller.abort();
|
|
63
|
+
void reader?.cancel().catch(() => {
|
|
64
|
+
});
|
|
35
65
|
}
|
|
36
66
|
};
|
|
37
67
|
}
|
|
38
68
|
};
|
|
39
69
|
}
|
|
40
70
|
|
|
71
|
+
// src/tool-history.ts
|
|
72
|
+
function toolInput(args) {
|
|
73
|
+
if (args == null) return {};
|
|
74
|
+
if (typeof args === "string") {
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(args);
|
|
77
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
78
|
+
return parsed;
|
|
79
|
+
}
|
|
80
|
+
return { value: parsed };
|
|
81
|
+
} catch {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (typeof args === "object" && !Array.isArray(args)) {
|
|
86
|
+
return args;
|
|
87
|
+
}
|
|
88
|
+
return { value: args };
|
|
89
|
+
}
|
|
90
|
+
function toolResponsePayload(content) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(content);
|
|
93
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
return { result: parsed };
|
|
97
|
+
} catch {
|
|
98
|
+
return { result: content };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function toAnthropicMessages(messages) {
|
|
102
|
+
const knownNames = /* @__PURE__ */ new Map();
|
|
103
|
+
const output = [];
|
|
104
|
+
for (const message of messages) {
|
|
105
|
+
if (message.role === "system") continue;
|
|
106
|
+
if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
|
|
107
|
+
for (const tc of message.toolCalls) knownNames.set(tc.id, tc.name);
|
|
108
|
+
const blocks = [];
|
|
109
|
+
if (message.content) {
|
|
110
|
+
blocks.push({ type: "text", text: message.content });
|
|
111
|
+
}
|
|
112
|
+
for (const tc of message.toolCalls) {
|
|
113
|
+
blocks.push({
|
|
114
|
+
type: "tool_use",
|
|
115
|
+
id: tc.id,
|
|
116
|
+
name: tc.name,
|
|
117
|
+
input: toolInput(tc.args)
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
output.push({ role: "assistant", content: blocks });
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (message.role === "tool") {
|
|
124
|
+
const id = message.toolCallId;
|
|
125
|
+
if (!id || !knownNames.has(id)) continue;
|
|
126
|
+
const block = {
|
|
127
|
+
type: "tool_result",
|
|
128
|
+
tool_use_id: id,
|
|
129
|
+
content: message.content
|
|
130
|
+
};
|
|
131
|
+
const previous2 = output[output.length - 1];
|
|
132
|
+
if (previous2?.role === "user" && Array.isArray(previous2.content) && previous2.content.every((item) => item && typeof item === "object" && item.type === "tool_result")) {
|
|
133
|
+
previous2.content.push(block);
|
|
134
|
+
} else {
|
|
135
|
+
output.push({ role: "user", content: [block] });
|
|
136
|
+
}
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (message.role === "assistant" && !message.content) continue;
|
|
140
|
+
const previous = output[output.length - 1];
|
|
141
|
+
if (message.role === "user" && previous?.role === "user") {
|
|
142
|
+
const previousBlocks = Array.isArray(previous.content) ? previous.content : [{ type: "text", text: previous.content }];
|
|
143
|
+
previous.content = [...previousBlocks, { type: "text", text: message.content }];
|
|
144
|
+
} else {
|
|
145
|
+
output.push({ role: message.role, content: message.content });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
function toGeminiContents(messages) {
|
|
151
|
+
const knownNames = /* @__PURE__ */ new Map();
|
|
152
|
+
const output = [];
|
|
153
|
+
for (const message of messages) {
|
|
154
|
+
if (message.role === "system") continue;
|
|
155
|
+
if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
|
|
156
|
+
for (const tc of message.toolCalls) knownNames.set(tc.id, tc.name);
|
|
157
|
+
const parts = [];
|
|
158
|
+
if (message.content) {
|
|
159
|
+
parts.push({ text: message.content });
|
|
160
|
+
}
|
|
161
|
+
for (const tc of message.toolCalls) {
|
|
162
|
+
parts.push({
|
|
163
|
+
functionCall: {
|
|
164
|
+
id: tc.id,
|
|
165
|
+
name: tc.name,
|
|
166
|
+
args: toolInput(tc.args)
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
output.push({ role: "model", parts });
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (message.role === "tool") {
|
|
174
|
+
const id = message.toolCallId;
|
|
175
|
+
const name = id ? knownNames.get(id) : void 0;
|
|
176
|
+
if (!name) continue;
|
|
177
|
+
const part = {
|
|
178
|
+
functionResponse: {
|
|
179
|
+
id,
|
|
180
|
+
name,
|
|
181
|
+
response: toolResponsePayload(message.content)
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const previous2 = output[output.length - 1];
|
|
185
|
+
if (previous2?.role === "user" && previous2.parts.every((item) => item.functionResponse !== void 0)) {
|
|
186
|
+
previous2.parts.push(part);
|
|
187
|
+
} else {
|
|
188
|
+
output.push({ role: "user", parts: [part] });
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (message.role === "assistant" && !message.content) continue;
|
|
193
|
+
const role = message.role === "assistant" ? "model" : "user";
|
|
194
|
+
const previous = output[output.length - 1];
|
|
195
|
+
if (previous?.role === role) {
|
|
196
|
+
previous.parts.push({ text: message.content });
|
|
197
|
+
} else {
|
|
198
|
+
output.push({ role, parts: [{ text: message.content }] });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return output;
|
|
202
|
+
}
|
|
203
|
+
|
|
41
204
|
// src/anthropic.ts
|
|
42
205
|
function anthropic(config) {
|
|
43
206
|
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096, retry } = config;
|
|
@@ -53,7 +216,7 @@ function anthropic(config) {
|
|
|
53
216
|
const body = {
|
|
54
217
|
model,
|
|
55
218
|
max_tokens: request.context?.maxTokens ?? maxTokens,
|
|
56
|
-
messages: request.messages
|
|
219
|
+
messages: toAnthropicMessages(request.messages),
|
|
57
220
|
system: request.messages.find((message) => message.role === "system")?.content,
|
|
58
221
|
tools: request.context?.tools?.map((tool) => ({
|
|
59
222
|
name: tool.name,
|
|
@@ -94,18 +257,25 @@ function gemini(config) {
|
|
|
94
257
|
createSource: (request) => {
|
|
95
258
|
const systemMessage = request.messages.find((message) => message.role === "system");
|
|
96
259
|
const body = {
|
|
97
|
-
contents: request.messages
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
260
|
+
contents: toGeminiContents(request.messages),
|
|
261
|
+
systemInstruction: systemMessage ? { role: "system", parts: [{ text: systemMessage.content }] } : void 0,
|
|
262
|
+
tools: request.context?.tools && request.context.tools.length > 0 ? [{
|
|
263
|
+
functionDeclarations: request.context.tools.map((tool) => ({
|
|
264
|
+
name: tool.name,
|
|
265
|
+
description: tool.description,
|
|
266
|
+
parameters: tool.schema
|
|
267
|
+
}))
|
|
268
|
+
}] : void 0
|
|
102
269
|
};
|
|
103
270
|
return createStreamSource(
|
|
104
271
|
(signal) => fetch(
|
|
105
|
-
`${baseUrl}/v1beta/models/${model}:streamGenerateContent?alt=sse
|
|
272
|
+
`${baseUrl}/v1beta/models/${model}:streamGenerateContent?alt=sse`,
|
|
106
273
|
{
|
|
107
274
|
method: "POST",
|
|
108
|
-
headers: {
|
|
275
|
+
headers: {
|
|
276
|
+
"Content-Type": "application/json",
|
|
277
|
+
"x-goog-api-key": apiKey
|
|
278
|
+
},
|
|
109
279
|
body: JSON.stringify(body),
|
|
110
280
|
signal
|
|
111
281
|
}
|
|
@@ -180,56 +350,71 @@ function asText(value) {
|
|
|
180
350
|
}
|
|
181
351
|
return "";
|
|
182
352
|
}
|
|
353
|
+
function isToolStartEvent(eventName) {
|
|
354
|
+
return eventName === "on_tool_start" || eventName.endsWith("_tool_start");
|
|
355
|
+
}
|
|
183
356
|
function langchain(config) {
|
|
184
357
|
const { runnable, mode = "stream" } = config;
|
|
185
358
|
return {
|
|
186
|
-
createSource: (request) =>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
359
|
+
createSource: (request) => {
|
|
360
|
+
const controller = new AbortController();
|
|
361
|
+
return {
|
|
362
|
+
stream: async function* () {
|
|
363
|
+
if (controller.signal.aborted) return;
|
|
364
|
+
try {
|
|
365
|
+
const runnableConfig = {
|
|
366
|
+
version: "v2",
|
|
367
|
+
signal: controller.signal
|
|
368
|
+
};
|
|
369
|
+
if (mode === "events" && runnable.streamEvents) {
|
|
370
|
+
const events = await runnable.streamEvents(
|
|
371
|
+
{ messages: request.messages },
|
|
372
|
+
runnableConfig
|
|
373
|
+
);
|
|
374
|
+
for await (const event of events) {
|
|
375
|
+
if (controller.signal.aborted) return;
|
|
376
|
+
const eventName = String(event.event ?? "");
|
|
377
|
+
if (eventName.endsWith("_stream")) {
|
|
378
|
+
const chunk = asText(event.data);
|
|
379
|
+
if (chunk) yield { type: "text", content: chunk };
|
|
380
|
+
} else if (isToolStartEvent(eventName) && event.name) {
|
|
381
|
+
yield {
|
|
382
|
+
type: "tool_call",
|
|
383
|
+
toolCall: {
|
|
384
|
+
id: String(event.run_id ?? `${event.name}-${Date.now()}`),
|
|
385
|
+
name: String(event.name),
|
|
386
|
+
args: JSON.stringify(event.data ?? {})
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else if (runnable.stream) {
|
|
392
|
+
const stream = await runnable.stream(
|
|
393
|
+
{ messages: request.messages },
|
|
394
|
+
runnableConfig
|
|
395
|
+
);
|
|
396
|
+
for await (const value of stream) {
|
|
397
|
+
if (controller.signal.aborted) return;
|
|
398
|
+
const chunk = asText(value);
|
|
198
399
|
if (chunk) yield { type: "text", content: chunk };
|
|
199
|
-
} else if (eventName.endsWith("_start") && event.name) {
|
|
200
|
-
yield {
|
|
201
|
-
type: "tool_call",
|
|
202
|
-
toolCall: {
|
|
203
|
-
id: String(event.run_id ?? `${event.name}-${Date.now()}`),
|
|
204
|
-
name: String(event.name),
|
|
205
|
-
args: JSON.stringify(event.data ?? {})
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
} else if (eventName.endsWith("_end")) {
|
|
209
|
-
continue;
|
|
210
400
|
}
|
|
401
|
+
} else {
|
|
402
|
+
yield adapterErrorChunk("Runnable does not implement stream() or streamEvents()");
|
|
403
|
+
return;
|
|
211
404
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
} else {
|
|
219
|
-
yield { type: "error", content: "Runnable does not implement stream() or streamEvents()" };
|
|
220
|
-
return;
|
|
405
|
+
if (controller.signal.aborted) return;
|
|
406
|
+
yield { type: "done" };
|
|
407
|
+
} catch (error) {
|
|
408
|
+
if (isAbortError(error) || controller.signal.aborted) return;
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
yield adapterErrorChunk(message, { cause: error });
|
|
221
411
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
type: "error",
|
|
226
|
-
content: error instanceof Error ? error.message : String(error)
|
|
227
|
-
};
|
|
412
|
+
},
|
|
413
|
+
abort: () => {
|
|
414
|
+
controller.abort();
|
|
228
415
|
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
})
|
|
416
|
+
};
|
|
417
|
+
}
|
|
233
418
|
};
|
|
234
419
|
}
|
|
235
420
|
function langgraph(config) {
|
|
@@ -240,7 +425,8 @@ function langgraph(config) {
|
|
|
240
425
|
}
|
|
241
426
|
|
|
242
427
|
// src/vercel-ai.ts
|
|
243
|
-
|
|
428
|
+
var UI_STREAM_HEADER = "x-vercel-ai-ui-message-stream";
|
|
429
|
+
async function* parseVercelTextStream(stream) {
|
|
244
430
|
const reader = stream.getReader();
|
|
245
431
|
const decoder = new TextDecoder();
|
|
246
432
|
try {
|
|
@@ -255,6 +441,48 @@ async function* parseVercelStream(stream) {
|
|
|
255
441
|
}
|
|
256
442
|
yield { type: "done" };
|
|
257
443
|
}
|
|
444
|
+
async function* parseVercelUiMessageStreamV1(stream) {
|
|
445
|
+
for await (const data of readSSELines(stream)) {
|
|
446
|
+
if (data === "[DONE]") {
|
|
447
|
+
yield { type: "done" };
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
const part = JSON.parse(data);
|
|
452
|
+
const type = part.type;
|
|
453
|
+
if (type === "text-delta") {
|
|
454
|
+
const delta = part.delta ?? part.textDelta;
|
|
455
|
+
if (typeof delta === "string" && delta) {
|
|
456
|
+
yield { type: "text", content: delta };
|
|
457
|
+
}
|
|
458
|
+
} else if (type === "reasoning-delta") {
|
|
459
|
+
const delta = part.delta ?? part.textDelta;
|
|
460
|
+
if (typeof delta === "string" && delta) {
|
|
461
|
+
yield { type: "reasoning", content: delta };
|
|
462
|
+
}
|
|
463
|
+
} else if (type === "error") {
|
|
464
|
+
const message = part.errorText ?? part.message ?? "Vercel AI UI stream error";
|
|
465
|
+
yield adapterErrorChunk(String(message));
|
|
466
|
+
return;
|
|
467
|
+
} else if (type === "abort") {
|
|
468
|
+
yield adapterErrorChunk(part.reason ?? "Vercel AI UI stream aborted by provider");
|
|
469
|
+
return;
|
|
470
|
+
} else if (type === "finish") {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
yield adapterErrorChunk("Vercel AI UI stream truncated before [DONE]");
|
|
477
|
+
}
|
|
478
|
+
async function* parseVercelStream(stream, response) {
|
|
479
|
+
const header = response?.headers.get(UI_STREAM_HEADER) ?? response?.headers.get(UI_STREAM_HEADER.toLowerCase());
|
|
480
|
+
if (header === "v1") {
|
|
481
|
+
yield* parseVercelUiMessageStreamV1(stream);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
yield* parseVercelTextStream(stream);
|
|
485
|
+
}
|
|
258
486
|
function vercelAI(config) {
|
|
259
487
|
const { api, headers = {}, retry } = config;
|
|
260
488
|
return {
|
|
@@ -428,6 +656,7 @@ async function* parseReplicateStream(stream) {
|
|
|
428
656
|
const decoder = new TextDecoder();
|
|
429
657
|
let buffer = "";
|
|
430
658
|
let currentEvent = "";
|
|
659
|
+
let sawDone = false;
|
|
431
660
|
try {
|
|
432
661
|
while (true) {
|
|
433
662
|
const { done, value } = await reader.read();
|
|
@@ -445,10 +674,11 @@ async function* parseReplicateStream(stream) {
|
|
|
445
674
|
if (currentEvent === "output" && data) {
|
|
446
675
|
yield { type: "text", content: data };
|
|
447
676
|
} else if (currentEvent === "done") {
|
|
677
|
+
sawDone = true;
|
|
448
678
|
yield { type: "done" };
|
|
449
679
|
return;
|
|
450
680
|
} else if (currentEvent === "error") {
|
|
451
|
-
yield
|
|
681
|
+
yield adapterErrorChunk(data || "replicate stream error");
|
|
452
682
|
return;
|
|
453
683
|
}
|
|
454
684
|
}
|
|
@@ -456,6 +686,10 @@ async function* parseReplicateStream(stream) {
|
|
|
456
686
|
} finally {
|
|
457
687
|
reader.releaseLock();
|
|
458
688
|
}
|
|
689
|
+
if (!sawDone) {
|
|
690
|
+
yield adapterErrorChunk("Replicate stream ended without done event");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
459
693
|
yield { type: "done" };
|
|
460
694
|
}
|
|
461
695
|
function replicate(config) {
|
|
@@ -488,17 +722,17 @@ function replicate(config) {
|
|
|
488
722
|
signal: controller.signal
|
|
489
723
|
});
|
|
490
724
|
if (!response.ok) {
|
|
491
|
-
yield
|
|
725
|
+
yield adapterErrorChunk(`Replicate API error: ${response.status}`);
|
|
492
726
|
return;
|
|
493
727
|
}
|
|
494
728
|
const prediction = await response.json();
|
|
495
729
|
if (prediction.error) {
|
|
496
|
-
yield
|
|
730
|
+
yield adapterErrorChunk(prediction.error);
|
|
497
731
|
return;
|
|
498
732
|
}
|
|
499
733
|
const streamUrl = prediction.urls?.stream;
|
|
500
734
|
if (!streamUrl) {
|
|
501
|
-
yield
|
|
735
|
+
yield adapterErrorChunk("Replicate prediction has no stream URL");
|
|
502
736
|
return;
|
|
503
737
|
}
|
|
504
738
|
const streamResponse = await fetch(streamUrl, {
|
|
@@ -507,7 +741,7 @@ function replicate(config) {
|
|
|
507
741
|
signal: controller.signal
|
|
508
742
|
});
|
|
509
743
|
if (!streamResponse.ok || !streamResponse.body) {
|
|
510
|
-
yield
|
|
744
|
+
yield adapterErrorChunk(`Replicate stream error: ${streamResponse.status}`);
|
|
511
745
|
return;
|
|
512
746
|
}
|
|
513
747
|
for await (const chunk of parseReplicateStream(streamResponse.body)) {
|
|
@@ -515,8 +749,9 @@ function replicate(config) {
|
|
|
515
749
|
yield chunk;
|
|
516
750
|
}
|
|
517
751
|
} catch (err) {
|
|
518
|
-
if (err
|
|
519
|
-
|
|
752
|
+
if (isAbortError(err) || aborted) return;
|
|
753
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
754
|
+
yield adapterErrorChunk(message, { cause: err });
|
|
520
755
|
}
|
|
521
756
|
},
|
|
522
757
|
abort: () => {
|
|
@@ -566,10 +801,7 @@ function vertex(config) {
|
|
|
566
801
|
createSource: (request) => {
|
|
567
802
|
const systemMessage = request.messages.find((message) => message.role === "system");
|
|
568
803
|
const body = {
|
|
569
|
-
contents: request.messages
|
|
570
|
-
role: message.role === "assistant" ? "model" : "user",
|
|
571
|
-
parts: [{ text: message.content }]
|
|
572
|
-
})),
|
|
804
|
+
contents: toGeminiContents(request.messages),
|
|
573
805
|
systemInstruction: systemMessage ? { role: "system", parts: [{ text: systemMessage.content }] } : void 0,
|
|
574
806
|
tools: request.context?.tools && request.context.tools.length > 0 ? [{
|
|
575
807
|
functionDeclarations: request.context.tools.map((tool) => ({
|
|
@@ -625,7 +857,7 @@ function isAnthropicModel(model) {
|
|
|
625
857
|
return model.startsWith("anthropic.");
|
|
626
858
|
}
|
|
627
859
|
function buildAnthropicBody(request, maxTokens) {
|
|
628
|
-
const messages = request.messages
|
|
860
|
+
const messages = toAnthropicMessages(request.messages);
|
|
629
861
|
const system = request.messages.find((m) => m.role === "system")?.content;
|
|
630
862
|
const tools = request.context?.tools?.map((tool) => ({
|
|
631
863
|
name: tool.name,
|
|
@@ -644,6 +876,19 @@ function buildAnthropicBody(request, maxTokens) {
|
|
|
644
876
|
async function* parseAnthropicBedrockEvents(events) {
|
|
645
877
|
const decoder = new TextDecoder();
|
|
646
878
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
879
|
+
const emitTool = function* (tc) {
|
|
880
|
+
const parsed = parseCompleteToolArgs(tc.args);
|
|
881
|
+
if (!parsed.ok) {
|
|
882
|
+
yield {
|
|
883
|
+
type: "error",
|
|
884
|
+
content: parsed.error.message,
|
|
885
|
+
metadata: { error: parsed.error }
|
|
886
|
+
};
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: parsed.args } };
|
|
890
|
+
return true;
|
|
891
|
+
};
|
|
647
892
|
for await (const item of events) {
|
|
648
893
|
const bytes = item.chunk?.bytes;
|
|
649
894
|
if (!bytes) continue;
|
|
@@ -673,7 +918,10 @@ async function* parseAnthropicBedrockEvents(events) {
|
|
|
673
918
|
const index = event.index ?? 0;
|
|
674
919
|
const tc = pendingToolCalls.get(index);
|
|
675
920
|
if (tc) {
|
|
676
|
-
yield
|
|
921
|
+
if (!(yield* emitTool(tc))) {
|
|
922
|
+
pendingToolCalls.clear();
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
677
925
|
pendingToolCalls.delete(index);
|
|
678
926
|
}
|
|
679
927
|
} else if (type === "message_delta") {
|
|
@@ -700,18 +948,18 @@ async function* parseAnthropicBedrockEvents(events) {
|
|
|
700
948
|
}
|
|
701
949
|
} else if (type === "message_stop") {
|
|
702
950
|
for (const [, tc] of pendingToolCalls) {
|
|
703
|
-
yield
|
|
951
|
+
if (!(yield* emitTool(tc))) {
|
|
952
|
+
pendingToolCalls.clear();
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
704
955
|
}
|
|
705
956
|
pendingToolCalls.clear();
|
|
706
957
|
yield { type: "done" };
|
|
707
958
|
return;
|
|
708
959
|
}
|
|
709
960
|
}
|
|
710
|
-
for (const [, tc] of pendingToolCalls) {
|
|
711
|
-
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
712
|
-
}
|
|
713
961
|
pendingToolCalls.clear();
|
|
714
|
-
yield
|
|
962
|
+
yield adapterErrorChunk("Bedrock stream ended before message_stop");
|
|
715
963
|
}
|
|
716
964
|
function bedrock(config) {
|
|
717
965
|
const { model, region, maxTokens = 4096 } = config;
|
|
@@ -746,9 +994,10 @@ function bedrock(config) {
|
|
|
746
994
|
let aborted = false;
|
|
747
995
|
return {
|
|
748
996
|
stream: async function* () {
|
|
749
|
-
if (aborted) return;
|
|
997
|
+
if (aborted || controller.signal.aborted) return;
|
|
750
998
|
try {
|
|
751
999
|
const client = await getClient();
|
|
1000
|
+
if (aborted || controller.signal.aborted) return;
|
|
752
1001
|
const sdk = config.client ? null : await loadSdk();
|
|
753
1002
|
const input = {
|
|
754
1003
|
modelId: model,
|
|
@@ -757,17 +1006,19 @@ function bedrock(config) {
|
|
|
757
1006
|
body: buildAnthropicBody(request, maxTokens)
|
|
758
1007
|
};
|
|
759
1008
|
const command = sdk ? new sdk.InvokeModelWithResponseStreamCommand(input) : { input };
|
|
760
|
-
const response = await client.send(command);
|
|
1009
|
+
const response = await client.send(command, { abortSignal: controller.signal });
|
|
761
1010
|
if (!response.body) {
|
|
762
|
-
yield
|
|
1011
|
+
yield adapterErrorChunk("Bedrock: empty response body");
|
|
763
1012
|
return;
|
|
764
1013
|
}
|
|
765
1014
|
for await (const chunk of parseAnthropicBedrockEvents(response.body)) {
|
|
766
|
-
if (aborted) return;
|
|
1015
|
+
if (aborted || controller.signal.aborted) return;
|
|
767
1016
|
yield chunk;
|
|
768
1017
|
}
|
|
769
1018
|
} catch (err) {
|
|
770
|
-
|
|
1019
|
+
if (isAbortError(err) || aborted) return;
|
|
1020
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1021
|
+
yield adapterErrorChunk(message, { cause: err });
|
|
771
1022
|
}
|
|
772
1023
|
},
|
|
773
1024
|
abort: () => {
|
|
@@ -818,26 +1069,62 @@ function webllm(config) {
|
|
|
818
1069
|
tools: false
|
|
819
1070
|
},
|
|
820
1071
|
createSource: (request) => {
|
|
1072
|
+
const controller = new AbortController();
|
|
821
1073
|
let aborted = false;
|
|
1074
|
+
let activeIter = null;
|
|
1075
|
+
const softAbort = () => {
|
|
1076
|
+
aborted = true;
|
|
1077
|
+
if (!controller.signal.aborted) controller.abort();
|
|
1078
|
+
if (activeIter && typeof activeIter.return === "function") {
|
|
1079
|
+
void activeIter.return(void 0).catch(() => {
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
822
1083
|
return {
|
|
823
1084
|
stream: async function* () {
|
|
824
|
-
if (aborted) return;
|
|
1085
|
+
if (aborted || controller.signal.aborted) return;
|
|
825
1086
|
try {
|
|
826
|
-
const engine = await getEngine();
|
|
1087
|
+
const engine = await raceAbort(getEngine(), controller.signal);
|
|
1088
|
+
if (aborted || controller.signal.aborted) return;
|
|
827
1089
|
const messages = request.messages.map((m) => ({ role: m.role, content: m.content }));
|
|
828
|
-
const completion = await
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1090
|
+
const completion = await raceAbort(
|
|
1091
|
+
Promise.resolve(
|
|
1092
|
+
engine.chat.completions.create({ messages, stream: true })
|
|
1093
|
+
),
|
|
1094
|
+
controller.signal
|
|
1095
|
+
);
|
|
1096
|
+
if (aborted || controller.signal.aborted) return;
|
|
1097
|
+
activeIter = completion[Symbol.asyncIterator]();
|
|
1098
|
+
while (true) {
|
|
1099
|
+
if (aborted || controller.signal.aborted) {
|
|
1100
|
+
if (typeof activeIter.return === "function") {
|
|
1101
|
+
await activeIter.return(void 0).catch(() => {
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
const next = await raceAbort(activeIter.next(), controller.signal);
|
|
1107
|
+
if (next.done) break;
|
|
1108
|
+
if (aborted || controller.signal.aborted) {
|
|
1109
|
+
if (typeof activeIter.return === "function") {
|
|
1110
|
+
await activeIter.return(void 0).catch(() => {
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const delta = next.value.choices[0]?.delta?.content;
|
|
832
1116
|
if (delta) yield { type: "text", content: delta };
|
|
833
1117
|
}
|
|
1118
|
+
if (aborted || controller.signal.aborted) return;
|
|
834
1119
|
yield { type: "done" };
|
|
835
1120
|
} catch (err) {
|
|
836
|
-
|
|
1121
|
+
if (isAbortError(err) || aborted || controller.signal.aborted) return;
|
|
1122
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1123
|
+
yield adapterErrorChunk(message, { cause: err });
|
|
837
1124
|
}
|
|
838
1125
|
},
|
|
839
1126
|
abort: () => {
|
|
840
|
-
|
|
1127
|
+
softAbort();
|
|
841
1128
|
}
|
|
842
1129
|
};
|
|
843
1130
|
}
|
|
@@ -937,22 +1224,57 @@ function createRouter(options) {
|
|
|
937
1224
|
}
|
|
938
1225
|
if (typeof policy === "function") {
|
|
939
1226
|
const maybe = policy({ request, candidates: pool });
|
|
940
|
-
if (maybe
|
|
1227
|
+
if (typeof maybe !== "string") {
|
|
1228
|
+
const controller = new AbortController();
|
|
1229
|
+
let child;
|
|
941
1230
|
return {
|
|
942
1231
|
abort: () => {
|
|
1232
|
+
controller.abort();
|
|
1233
|
+
child?.abort();
|
|
943
1234
|
},
|
|
944
1235
|
stream: async function* () {
|
|
945
|
-
|
|
1236
|
+
let id;
|
|
1237
|
+
try {
|
|
1238
|
+
id = await raceAbort(maybe, controller.signal);
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
if (isAbortError(err)) return;
|
|
1241
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1242
|
+
yield {
|
|
1243
|
+
type: "error",
|
|
1244
|
+
content: error.message,
|
|
1245
|
+
metadata: { error }
|
|
1246
|
+
};
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
946
1249
|
const c3 = pool.find((x) => x.id === id);
|
|
947
1250
|
if (!c3) {
|
|
948
|
-
|
|
1251
|
+
const error = new ConfigError({
|
|
949
1252
|
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
950
1253
|
message: `policy returned unknown id: ${id}`,
|
|
951
1254
|
hint: "Custom policy functions must return one of the candidate ids."
|
|
952
1255
|
});
|
|
1256
|
+
yield {
|
|
1257
|
+
type: "error",
|
|
1258
|
+
content: error.message,
|
|
1259
|
+
metadata: { error }
|
|
1260
|
+
};
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
try {
|
|
1264
|
+
options.onRoute?.({ id: c3.id, reason: pickedBy ?? "custom policy", request });
|
|
1265
|
+
child = c3.adapter.createSource(request);
|
|
1266
|
+
for await (const chunk of child.stream()) {
|
|
1267
|
+
yield chunk;
|
|
1268
|
+
}
|
|
1269
|
+
} catch (err) {
|
|
1270
|
+
if (controller.signal.aborted || isAbortError(err)) return;
|
|
1271
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1272
|
+
yield {
|
|
1273
|
+
type: "error",
|
|
1274
|
+
content: error.message,
|
|
1275
|
+
metadata: { error }
|
|
1276
|
+
};
|
|
953
1277
|
}
|
|
954
|
-
options.onRoute?.({ id: c3.id, reason: pickedBy ?? "custom policy", request });
|
|
955
|
-
for await (const chunk of c3.adapter.createSource(request).stream()) yield chunk;
|
|
956
1278
|
}
|
|
957
1279
|
};
|
|
958
1280
|
}
|
|
@@ -1040,9 +1362,17 @@ async function drain(source, timeoutMs) {
|
|
|
1040
1362
|
for await (const chunk of source.stream()) {
|
|
1041
1363
|
chunks.push(chunk);
|
|
1042
1364
|
if (chunk.type === "text" && typeof chunk.content === "string") text += chunk.content;
|
|
1365
|
+
if (chunk.type === "error") {
|
|
1366
|
+
const metaErr = chunk.metadata?.error;
|
|
1367
|
+
error = metaErr instanceof Error ? metaErr : new Error(chunk.content ?? "stream error");
|
|
1368
|
+
}
|
|
1043
1369
|
}
|
|
1044
1370
|
} catch (err) {
|
|
1045
|
-
|
|
1371
|
+
if (!isAbortError(err)) {
|
|
1372
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
1373
|
+
} else if (!timedOut) {
|
|
1374
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
1375
|
+
}
|
|
1046
1376
|
} finally {
|
|
1047
1377
|
if (timer) clearTimeout(timer);
|
|
1048
1378
|
}
|
|
@@ -1081,10 +1411,19 @@ function createEnsembleAdapter(options) {
|
|
|
1081
1411
|
const sources = [];
|
|
1082
1412
|
const run = async () => {
|
|
1083
1413
|
const tasks = options.candidates.map(async (c, i) => {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1414
|
+
try {
|
|
1415
|
+
const source = c.adapter.createSource(request);
|
|
1416
|
+
sources[i] = source;
|
|
1417
|
+
if (aborted) {
|
|
1418
|
+
source.abort();
|
|
1419
|
+
return { id: c.id, chunks: [], text: "", error: new Error("aborted") };
|
|
1420
|
+
}
|
|
1421
|
+
const { chunks, text, error } = await drain(source, options.timeoutMs);
|
|
1422
|
+
return { id: c.id, chunks, text, error };
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1425
|
+
return { id: c.id, chunks: [], text: "", error };
|
|
1426
|
+
}
|
|
1088
1427
|
});
|
|
1089
1428
|
return Promise.all(tasks);
|
|
1090
1429
|
};
|
|
@@ -1107,22 +1446,51 @@ function createEnsembleAdapter(options) {
|
|
|
1107
1446
|
for (const s of sources) s?.abort();
|
|
1108
1447
|
},
|
|
1109
1448
|
stream: async function* () {
|
|
1449
|
+
if (aborted) return;
|
|
1110
1450
|
const branches = await run();
|
|
1111
1451
|
options.onBranches?.(branches);
|
|
1112
|
-
if (aborted)
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1452
|
+
if (aborted) {
|
|
1453
|
+
for (const s of sources) s?.abort();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
try {
|
|
1457
|
+
const text = await finalText(branches);
|
|
1458
|
+
if (!text && branches.every((b) => b.error)) {
|
|
1459
|
+
const summary = branches.map((b) => `${b.id}: ${b.error?.message ?? "unknown"}`).join("; ");
|
|
1460
|
+
const err = new AdapterError({
|
|
1461
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1462
|
+
message: `all ensemble branches failed (${summary})`,
|
|
1463
|
+
hint: "Inspect each branch error via options.onBranches or the err.branchErrors field."
|
|
1464
|
+
});
|
|
1465
|
+
err.branchErrors = branches.map((b) => ({ id: b.id, error: b.error }));
|
|
1466
|
+
yield {
|
|
1467
|
+
type: "error",
|
|
1468
|
+
content: err.message,
|
|
1469
|
+
metadata: { error: err }
|
|
1470
|
+
};
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
yield {
|
|
1474
|
+
type: "text",
|
|
1475
|
+
content: text,
|
|
1476
|
+
metadata: {
|
|
1477
|
+
ensemble: branches.map((b) => ({
|
|
1478
|
+
id: b.id,
|
|
1479
|
+
bytes: b.text.length,
|
|
1480
|
+
error: b.error?.message
|
|
1481
|
+
}))
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
yield { type: "done" };
|
|
1485
|
+
} catch (err) {
|
|
1486
|
+
if (aborted || isAbortError(err)) return;
|
|
1487
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1488
|
+
yield {
|
|
1489
|
+
type: "error",
|
|
1490
|
+
content: error.message,
|
|
1491
|
+
metadata: { error }
|
|
1492
|
+
};
|
|
1123
1493
|
}
|
|
1124
|
-
yield { type: "text", content: text, metadata: { ensemble: branches.map((b) => ({ id: b.id, bytes: b.text.length, error: b.error?.message })) } };
|
|
1125
|
-
yield { type: "done" };
|
|
1126
1494
|
}
|
|
1127
1495
|
};
|
|
1128
1496
|
}
|
|
@@ -1153,57 +1521,119 @@ function createFallbackAdapter(candidates, options = {}) {
|
|
|
1153
1521
|
let source;
|
|
1154
1522
|
try {
|
|
1155
1523
|
source = candidate.adapter.createSource(request);
|
|
1156
|
-
} catch (
|
|
1157
|
-
const error =
|
|
1524
|
+
} catch (err2) {
|
|
1525
|
+
const error = err2 instanceof Error ? err2 : new Error(String(err2));
|
|
1158
1526
|
errors.push({ id: candidate.id, error });
|
|
1159
1527
|
options.onFallback?.({ id: candidate.id, index: i, error });
|
|
1160
|
-
if (options.shouldRetry && !options.shouldRetry(error, i))
|
|
1528
|
+
if (options.shouldRetry && !options.shouldRetry(error, i)) {
|
|
1529
|
+
yield {
|
|
1530
|
+
type: "error",
|
|
1531
|
+
content: error.message,
|
|
1532
|
+
metadata: { error }
|
|
1533
|
+
};
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1161
1536
|
continue;
|
|
1162
1537
|
}
|
|
1163
1538
|
active = source;
|
|
1164
1539
|
const iter = source.stream()[Symbol.asyncIterator]();
|
|
1165
1540
|
let firstReal;
|
|
1166
1541
|
let branchError;
|
|
1542
|
+
const leadingUsage = [];
|
|
1167
1543
|
try {
|
|
1168
1544
|
while (true) {
|
|
1545
|
+
if (aborted) {
|
|
1546
|
+
source.abort();
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1169
1549
|
const r = await iter.next();
|
|
1170
1550
|
if (r.done) {
|
|
1171
1551
|
branchError = new Error(`candidate ${candidate.id} emitted no chunks`);
|
|
1172
1552
|
break;
|
|
1173
1553
|
}
|
|
1174
1554
|
if (r.value.type === "error") {
|
|
1175
|
-
|
|
1555
|
+
const metaErr = r.value.metadata?.error;
|
|
1556
|
+
branchError = metaErr instanceof Error ? metaErr : new Error(
|
|
1176
1557
|
`candidate ${candidate.id}: ${r.value.content ?? "stream error"}`
|
|
1177
1558
|
);
|
|
1178
1559
|
break;
|
|
1179
1560
|
}
|
|
1561
|
+
if (r.value.type === "usage") {
|
|
1562
|
+
leadingUsage.push(r.value);
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1180
1565
|
firstReal = r.value;
|
|
1181
1566
|
break;
|
|
1182
1567
|
}
|
|
1183
|
-
} catch (
|
|
1184
|
-
|
|
1568
|
+
} catch (err2) {
|
|
1569
|
+
if (isAbortError(err2) || aborted) {
|
|
1570
|
+
source.abort();
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
branchError = err2 instanceof Error ? err2 : new Error(String(err2));
|
|
1185
1574
|
}
|
|
1186
1575
|
if (branchError || !firstReal) {
|
|
1187
1576
|
const error = branchError ?? new Error(`candidate ${candidate.id} emitted no chunks`);
|
|
1188
1577
|
errors.push({ id: candidate.id, error });
|
|
1189
1578
|
options.onFallback?.({ id: candidate.id, index: i, error });
|
|
1190
|
-
|
|
1579
|
+
try {
|
|
1580
|
+
source.abort();
|
|
1581
|
+
} catch {
|
|
1582
|
+
}
|
|
1583
|
+
if (typeof iter.return === "function") {
|
|
1584
|
+
try {
|
|
1585
|
+
await iter.return(void 0);
|
|
1586
|
+
} catch {
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
if (options.shouldRetry && !options.shouldRetry(error, i)) {
|
|
1590
|
+
yield {
|
|
1591
|
+
type: "error",
|
|
1592
|
+
content: error.message,
|
|
1593
|
+
metadata: { error }
|
|
1594
|
+
};
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1191
1597
|
active = void 0;
|
|
1192
1598
|
continue;
|
|
1193
1599
|
}
|
|
1600
|
+
for (const chunk of leadingUsage) yield chunk;
|
|
1194
1601
|
yield firstReal;
|
|
1195
1602
|
while (true) {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1603
|
+
if (aborted) {
|
|
1604
|
+
source.abort();
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
try {
|
|
1608
|
+
const next = await iter.next();
|
|
1609
|
+
if (next.done) return;
|
|
1610
|
+
yield next.value;
|
|
1611
|
+
} catch (err2) {
|
|
1612
|
+
if (isAbortError(err2) || aborted) {
|
|
1613
|
+
source.abort();
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
const error = err2 instanceof Error ? err2 : new Error(String(err2));
|
|
1617
|
+
yield {
|
|
1618
|
+
type: "error",
|
|
1619
|
+
content: error.message,
|
|
1620
|
+
metadata: { error }
|
|
1621
|
+
};
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1199
1624
|
}
|
|
1200
1625
|
}
|
|
1201
1626
|
const summary = errors.map((e) => `${e.id}: ${e.error.message}`).join("; ");
|
|
1202
|
-
|
|
1627
|
+
const err = new AdapterError({
|
|
1203
1628
|
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1204
1629
|
message: `all fallback candidates failed (${summary})`,
|
|
1205
1630
|
hint: "Check each candidate adapter's configuration and provider keys."
|
|
1206
1631
|
});
|
|
1632
|
+
yield {
|
|
1633
|
+
type: "error",
|
|
1634
|
+
content: err.message,
|
|
1635
|
+
metadata: { error: err }
|
|
1636
|
+
};
|
|
1207
1637
|
}
|
|
1208
1638
|
};
|
|
1209
1639
|
}
|
|
@@ -1221,25 +1651,48 @@ function mockAdapter(options) {
|
|
|
1221
1651
|
usage: true
|
|
1222
1652
|
},
|
|
1223
1653
|
createSource: (request) => {
|
|
1224
|
-
|
|
1225
|
-
|
|
1654
|
+
const controller = new AbortController();
|
|
1655
|
+
let myCall;
|
|
1226
1656
|
let cancelled = false;
|
|
1227
1657
|
return {
|
|
1228
1658
|
stream: async function* () {
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1659
|
+
if (cancelled || controller.signal.aborted) return;
|
|
1660
|
+
try {
|
|
1661
|
+
if (myCall === void 0) {
|
|
1662
|
+
myCall = callIndex++;
|
|
1663
|
+
history?.push(request);
|
|
1664
|
+
}
|
|
1665
|
+
const chunks = resolve(response, myCall, request);
|
|
1666
|
+
for (const chunk of chunks) {
|
|
1667
|
+
if (cancelled || controller.signal.aborted) return;
|
|
1668
|
+
if (delayMs > 0) {
|
|
1669
|
+
await abortableSleep(delayMs, controller.signal, (ms) => new Promise((resolveTimer) => setTimeout(resolveTimer, ms)));
|
|
1670
|
+
}
|
|
1671
|
+
if (cancelled || controller.signal.aborted) return;
|
|
1672
|
+
if (chunk.type === "error" && !(chunk.metadata?.error instanceof Error)) {
|
|
1673
|
+
const normalized = adapterErrorChunk(
|
|
1674
|
+
chunk.content ?? "Mock adapter error",
|
|
1675
|
+
{ cause: chunk.metadata?.error }
|
|
1676
|
+
);
|
|
1677
|
+
yield {
|
|
1678
|
+
...chunk,
|
|
1679
|
+
metadata: { ...chunk.metadata, ...normalized.metadata }
|
|
1680
|
+
};
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
yield chunk;
|
|
1684
|
+
if (chunk.type === "done" || chunk.type === "error") return;
|
|
1237
1685
|
}
|
|
1686
|
+
yield { type: "done" };
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
if (cancelled || controller.signal.aborted || isAbortError(error)) return;
|
|
1689
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1690
|
+
yield adapterErrorChunk(message, { cause: error });
|
|
1238
1691
|
}
|
|
1239
|
-
if (!endedExplicitly) yield { type: "done" };
|
|
1240
1692
|
},
|
|
1241
1693
|
abort: () => {
|
|
1242
1694
|
cancelled = true;
|
|
1695
|
+
controller.abort();
|
|
1243
1696
|
}
|
|
1244
1697
|
};
|
|
1245
1698
|
}
|
|
@@ -1262,22 +1715,43 @@ function resolve(response, callIndex, request) {
|
|
|
1262
1715
|
}
|
|
1263
1716
|
function recordingAdapter(inner, sink) {
|
|
1264
1717
|
return {
|
|
1718
|
+
capabilities: inner.capabilities,
|
|
1265
1719
|
createSource: (request) => {
|
|
1266
|
-
|
|
1720
|
+
let innerSource;
|
|
1267
1721
|
const captured = [];
|
|
1268
|
-
|
|
1722
|
+
let aborted = false;
|
|
1269
1723
|
return {
|
|
1270
1724
|
stream: async function* () {
|
|
1725
|
+
if (aborted) return;
|
|
1726
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1271
1727
|
try {
|
|
1728
|
+
innerSource = inner.createSource(request);
|
|
1272
1729
|
for await (const chunk of innerSource.stream()) {
|
|
1730
|
+
if (aborted) return;
|
|
1273
1731
|
captured.push(chunk);
|
|
1274
1732
|
yield chunk;
|
|
1733
|
+
if (chunk.type === "done" || chunk.type === "error") return;
|
|
1275
1734
|
}
|
|
1735
|
+
const errorChunk = adapterErrorChunk("Recorded adapter ended without a terminal chunk");
|
|
1736
|
+
captured.push(errorChunk);
|
|
1737
|
+
yield errorChunk;
|
|
1738
|
+
} catch (error) {
|
|
1739
|
+
if (aborted || isAbortError(error)) return;
|
|
1740
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1741
|
+
const errorChunk = adapterErrorChunk(message, { cause: error });
|
|
1742
|
+
captured.push(errorChunk);
|
|
1743
|
+
yield errorChunk;
|
|
1276
1744
|
} finally {
|
|
1277
|
-
|
|
1745
|
+
try {
|
|
1746
|
+
await sink.push({ recordedAt, request, chunks: captured });
|
|
1747
|
+
} catch {
|
|
1748
|
+
}
|
|
1278
1749
|
}
|
|
1279
1750
|
},
|
|
1280
|
-
abort: () =>
|
|
1751
|
+
abort: () => {
|
|
1752
|
+
aborted = true;
|
|
1753
|
+
innerSource?.abort();
|
|
1754
|
+
}
|
|
1281
1755
|
};
|
|
1282
1756
|
}
|
|
1283
1757
|
};
|
|
@@ -1303,15 +1777,40 @@ function replayAdapter(fixture) {
|
|
|
1303
1777
|
response: fixture.map((turn) => turn.chunks)
|
|
1304
1778
|
});
|
|
1305
1779
|
}
|
|
1306
|
-
|
|
1307
|
-
// src/embedders/shared.ts
|
|
1308
|
-
var ERROR_BODY_PREVIEW_CHARS = 200;
|
|
1309
|
-
async function throwIfNotOk(response, label, url) {
|
|
1780
|
+
async function throwIfNotOk(response, label, _url) {
|
|
1310
1781
|
if (response.ok) return;
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1782
|
+
void response.body?.cancel().catch(() => {
|
|
1783
|
+
});
|
|
1784
|
+
throw embeddingError(label, `HTTP ${response.status}`);
|
|
1785
|
+
}
|
|
1786
|
+
function embeddingError(label, detail, cause) {
|
|
1787
|
+
return new AdapterError({
|
|
1788
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1789
|
+
message: `${label} embedding failed: ${detail}`,
|
|
1790
|
+
cause
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
function requireEmbeddingVector(value, label) {
|
|
1794
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
1795
|
+
throw new AdapterError({
|
|
1796
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1797
|
+
message: `${label} returned an empty or missing embedding vector`,
|
|
1798
|
+
hint: "The provider responded 2xx but the embedding payload was empty or malformed."
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
const vector = [];
|
|
1802
|
+
for (let i = 0; i < value.length; i++) {
|
|
1803
|
+
const n = value[i];
|
|
1804
|
+
if (typeof n !== "number" || !Number.isFinite(n)) {
|
|
1805
|
+
throw new AdapterError({
|
|
1806
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1807
|
+
message: `${label} returned a non-finite embedding value at index ${i}`,
|
|
1808
|
+
hint: "Embedding vectors must be finite numbers only."
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
vector.push(n);
|
|
1812
|
+
}
|
|
1813
|
+
return vector;
|
|
1315
1814
|
}
|
|
1316
1815
|
|
|
1317
1816
|
// src/embedders/openai.ts
|
|
@@ -1320,7 +1819,7 @@ async function fetchAvailableModels(baseUrl, apiKey) {
|
|
|
1320
1819
|
const response = await fetch(url, {
|
|
1321
1820
|
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
1322
1821
|
});
|
|
1323
|
-
await throwIfNotOk(response, "openai"
|
|
1822
|
+
await throwIfNotOk(response, "openai");
|
|
1324
1823
|
const data = await response.json();
|
|
1325
1824
|
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
1326
1825
|
}
|
|
@@ -1328,13 +1827,9 @@ async function buildModelError(baseUrl, apiKey, originalError) {
|
|
|
1328
1827
|
try {
|
|
1329
1828
|
const models = await fetchAvailableModels(baseUrl, apiKey);
|
|
1330
1829
|
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
1331
|
-
return
|
|
1332
|
-
`OpenAI embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
1333
|
-
);
|
|
1830
|
+
return embeddingError("OpenAI", `${originalError}. Available embedding models: ${list}`);
|
|
1334
1831
|
} catch (fetchError) {
|
|
1335
|
-
return
|
|
1336
|
-
`OpenAI embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
1337
|
-
);
|
|
1832
|
+
return embeddingError("OpenAI", `${originalError}. Could not fetch available models`, fetchError);
|
|
1338
1833
|
}
|
|
1339
1834
|
}
|
|
1340
1835
|
function openaiEmbedder(config) {
|
|
@@ -1349,20 +1844,24 @@ function openaiEmbedder(config) {
|
|
|
1349
1844
|
body: JSON.stringify({ model, input: text })
|
|
1350
1845
|
});
|
|
1351
1846
|
if (!response.ok) {
|
|
1352
|
-
|
|
1353
|
-
|
|
1847
|
+
void response.body?.cancel().catch(() => {
|
|
1848
|
+
});
|
|
1849
|
+
const message = `HTTP ${response.status}`;
|
|
1354
1850
|
throw await buildModelError(baseUrl, apiKey, message);
|
|
1355
1851
|
}
|
|
1356
1852
|
const data = await response.json();
|
|
1357
|
-
|
|
1853
|
+
const embedding = data.data?.[0]?.embedding;
|
|
1854
|
+
return requireEmbeddingVector(embedding, "OpenAI");
|
|
1358
1855
|
};
|
|
1359
1856
|
}
|
|
1360
1857
|
|
|
1361
1858
|
// src/embedders/gemini.ts
|
|
1362
1859
|
async function fetchAvailableModels2(baseUrl, apiKey) {
|
|
1363
|
-
const url = `${baseUrl}/v1beta/models
|
|
1364
|
-
const response = await fetch(url
|
|
1365
|
-
|
|
1860
|
+
const url = `${baseUrl}/v1beta/models`;
|
|
1861
|
+
const response = await fetch(url, {
|
|
1862
|
+
headers: { "x-goog-api-key": apiKey }
|
|
1863
|
+
});
|
|
1864
|
+
await throwIfNotOk(response, "gemini");
|
|
1366
1865
|
const data = await response.json();
|
|
1367
1866
|
return data.models.filter((m) => m.supportedGenerationMethods.includes("embedContent")).map((m) => m.name.replace("models/", "")).sort();
|
|
1368
1867
|
}
|
|
@@ -1370,13 +1869,9 @@ async function buildModelError2(baseUrl, apiKey, originalError) {
|
|
|
1370
1869
|
try {
|
|
1371
1870
|
const models = await fetchAvailableModels2(baseUrl, apiKey);
|
|
1372
1871
|
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
1373
|
-
return
|
|
1374
|
-
`Gemini embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
1375
|
-
);
|
|
1872
|
+
return embeddingError("Gemini", `${originalError}. Available embedding models: ${list}`);
|
|
1376
1873
|
} catch (fetchError) {
|
|
1377
|
-
return
|
|
1378
|
-
`Gemini embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
1379
|
-
);
|
|
1874
|
+
return embeddingError("Gemini", `${originalError}. Could not fetch available models`, fetchError);
|
|
1380
1875
|
}
|
|
1381
1876
|
}
|
|
1382
1877
|
function geminiEmbedder(config) {
|
|
@@ -1387,10 +1882,13 @@ function geminiEmbedder(config) {
|
|
|
1387
1882
|
} = config;
|
|
1388
1883
|
return async (text) => {
|
|
1389
1884
|
const response = await fetch(
|
|
1390
|
-
`${baseUrl}/v1beta/models/${model}:embedContent
|
|
1885
|
+
`${baseUrl}/v1beta/models/${model}:embedContent`,
|
|
1391
1886
|
{
|
|
1392
1887
|
method: "POST",
|
|
1393
|
-
headers: {
|
|
1888
|
+
headers: {
|
|
1889
|
+
"Content-Type": "application/json",
|
|
1890
|
+
"x-goog-api-key": apiKey
|
|
1891
|
+
},
|
|
1394
1892
|
body: JSON.stringify({
|
|
1395
1893
|
model: `models/${model}`,
|
|
1396
1894
|
content: { parts: [{ text }] }
|
|
@@ -1398,12 +1896,13 @@ function geminiEmbedder(config) {
|
|
|
1398
1896
|
}
|
|
1399
1897
|
);
|
|
1400
1898
|
if (!response.ok) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1899
|
+
void response.body?.cancel().catch(() => {
|
|
1900
|
+
});
|
|
1901
|
+
const message = `HTTP ${response.status}`;
|
|
1403
1902
|
throw await buildModelError2(baseUrl, apiKey, message);
|
|
1404
1903
|
}
|
|
1405
1904
|
const data = await response.json();
|
|
1406
|
-
return data.embedding
|
|
1905
|
+
return requireEmbeddingVector(data.embedding?.values, "Gemini");
|
|
1407
1906
|
};
|
|
1408
1907
|
}
|
|
1409
1908
|
|
|
@@ -1411,7 +1910,7 @@ function geminiEmbedder(config) {
|
|
|
1411
1910
|
async function fetchAvailableModels3(baseUrl) {
|
|
1412
1911
|
const url = `${baseUrl}/api/tags`;
|
|
1413
1912
|
const response = await fetch(url);
|
|
1414
|
-
await throwIfNotOk(response, "ollama"
|
|
1913
|
+
await throwIfNotOk(response, "ollama");
|
|
1415
1914
|
const data = await response.json();
|
|
1416
1915
|
return data.models.map((m) => m.name).filter((name) => name.includes("embed")).sort();
|
|
1417
1916
|
}
|
|
@@ -1419,13 +1918,9 @@ async function buildModelError3(baseUrl, originalError) {
|
|
|
1419
1918
|
try {
|
|
1420
1919
|
const models = await fetchAvailableModels3(baseUrl);
|
|
1421
1920
|
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
1422
|
-
return
|
|
1423
|
-
`Ollama embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
1424
|
-
);
|
|
1921
|
+
return embeddingError("Ollama", `${originalError}. Available embedding models: ${list}`);
|
|
1425
1922
|
} catch (fetchError) {
|
|
1426
|
-
return
|
|
1427
|
-
`Ollama embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
1428
|
-
);
|
|
1923
|
+
return embeddingError("Ollama", `${originalError}. Could not fetch available models`, fetchError);
|
|
1429
1924
|
}
|
|
1430
1925
|
}
|
|
1431
1926
|
function ollamaEmbedder(config) {
|
|
@@ -1437,12 +1932,14 @@ function ollamaEmbedder(config) {
|
|
|
1437
1932
|
body: JSON.stringify({ model, input: text })
|
|
1438
1933
|
});
|
|
1439
1934
|
if (!response.ok) {
|
|
1440
|
-
|
|
1441
|
-
|
|
1935
|
+
void response.body?.cancel().catch(() => {
|
|
1936
|
+
});
|
|
1937
|
+
const message = `HTTP ${response.status}`;
|
|
1442
1938
|
throw await buildModelError3(baseUrl, message);
|
|
1443
1939
|
}
|
|
1444
1940
|
const data = await response.json();
|
|
1445
|
-
|
|
1941
|
+
const first = Array.isArray(data.embeddings) ? data.embeddings[0] : void 0;
|
|
1942
|
+
return requireEmbeddingVector(first, "Ollama");
|
|
1446
1943
|
};
|
|
1447
1944
|
}
|
|
1448
1945
|
async function fetchAvailableModels4(provider, baseUrl, apiKey) {
|
|
@@ -1450,7 +1947,7 @@ async function fetchAvailableModels4(provider, baseUrl, apiKey) {
|
|
|
1450
1947
|
const response = await fetch(url, {
|
|
1451
1948
|
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
1452
1949
|
});
|
|
1453
|
-
await throwIfNotOk(response, provider
|
|
1950
|
+
await throwIfNotOk(response, provider);
|
|
1454
1951
|
const data = await response.json();
|
|
1455
1952
|
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
1456
1953
|
}
|
|
@@ -1458,13 +1955,9 @@ async function buildModelError4(provider, baseUrl, apiKey, originalError) {
|
|
|
1458
1955
|
try {
|
|
1459
1956
|
const models = await fetchAvailableModels4(provider, baseUrl, apiKey);
|
|
1460
1957
|
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
1461
|
-
return
|
|
1462
|
-
`${provider} embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
1463
|
-
);
|
|
1958
|
+
return embeddingError(provider, `${originalError}. Available embedding models: ${list}`);
|
|
1464
1959
|
} catch (fetchError) {
|
|
1465
|
-
return
|
|
1466
|
-
`${provider} embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
1467
|
-
);
|
|
1960
|
+
return embeddingError(provider, `${originalError}. Could not fetch available models`, fetchError);
|
|
1468
1961
|
}
|
|
1469
1962
|
}
|
|
1470
1963
|
function createOpenAICompatibleEmbedder(provider, defaultBaseUrl) {
|
|
@@ -1487,12 +1980,14 @@ function createOpenAICompatibleEmbedder(provider, defaultBaseUrl) {
|
|
|
1487
1980
|
body: JSON.stringify({ model, input: text })
|
|
1488
1981
|
});
|
|
1489
1982
|
if (!response.ok) {
|
|
1490
|
-
|
|
1491
|
-
|
|
1983
|
+
void response.body?.cancel().catch(() => {
|
|
1984
|
+
});
|
|
1985
|
+
const message = `HTTP ${response.status}`;
|
|
1492
1986
|
throw await buildModelError4(provider, baseUrl, apiKey, message);
|
|
1493
1987
|
}
|
|
1494
1988
|
const data = await response.json();
|
|
1495
|
-
|
|
1989
|
+
const embedding = data.data?.[0]?.embedding;
|
|
1990
|
+
return requireEmbeddingVector(embedding, provider);
|
|
1496
1991
|
};
|
|
1497
1992
|
};
|
|
1498
1993
|
}
|