@agentskit/adapters 0.12.0 → 0.13.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/dist/catalog/index.cjs +153745 -0
- package/dist/catalog/index.cjs.map +1 -0
- package/dist/catalog/index.d.cts +205 -0
- package/dist/catalog/index.d.ts +205 -0
- package/dist/catalog/index.js +153479 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/chunk-6FPLWOVE.js +26 -0
- package/dist/chunk-6FPLWOVE.js.map +1 -0
- package/dist/chunk-726IKNNR.js +486 -0
- package/dist/chunk-726IKNNR.js.map +1 -0
- package/dist/createAdapter-Dfnf3Iio.d.cts +14 -0
- package/dist/createAdapter-Dfnf3Iio.d.ts +14 -0
- package/dist/createAdapter.cjs +28 -0
- package/dist/createAdapter.cjs.map +1 -0
- package/dist/createAdapter.d.cts +2 -0
- package/dist/createAdapter.d.ts +2 -0
- package/dist/createAdapter.js +3 -0
- package/dist/createAdapter.js.map +1 -0
- package/dist/index.cjs +297 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +202 -77
- package/dist/index.d.ts +202 -77
- package/dist/index.js +277 -556
- package/dist/index.js.map +1 -1
- package/dist/utils-ddwu4MN4.d.cts +66 -0
- package/dist/utils-ddwu4MN4.d.ts +66 -0
- package/package.json +19 -6
package/dist/index.js
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
stream: async function* () {
|
|
6
|
-
try {
|
|
7
|
-
const result = await config.send(request);
|
|
8
|
-
const stream = result instanceof Response ? result.body : result;
|
|
9
|
-
yield* config.parse(stream);
|
|
10
|
-
} catch (err) {
|
|
11
|
-
yield {
|
|
12
|
-
type: "error",
|
|
13
|
-
content: err instanceof Error ? err.message : String(err)
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
abort: () => {
|
|
18
|
-
config.abort?.();
|
|
19
|
-
}
|
|
20
|
-
})
|
|
21
|
-
};
|
|
22
|
-
}
|
|
1
|
+
export { createAdapter } from './chunk-6FPLWOVE.js';
|
|
2
|
+
import { createStreamSource, parseAnthropicStream, parseGeminiStream, parseOllamaStream, toProviderMessages, parseOpenAIStream, openai } from './chunk-726IKNNR.js';
|
|
3
|
+
export { chunkText, fetchWithRetry, openai, simulateStream } from './chunk-726IKNNR.js';
|
|
4
|
+
import { ConfigError, ErrorCodes, AdapterError } from '@agentskit/core';
|
|
23
5
|
|
|
24
6
|
// src/generic.ts
|
|
25
7
|
function generic(config) {
|
|
@@ -56,429 +38,6 @@ function generic(config) {
|
|
|
56
38
|
};
|
|
57
39
|
}
|
|
58
40
|
|
|
59
|
-
// src/utils.ts
|
|
60
|
-
function toProviderMessages(messages) {
|
|
61
|
-
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
62
|
-
const output = [];
|
|
63
|
-
for (const message of messages) {
|
|
64
|
-
if (message.role === "tool") {
|
|
65
|
-
const id = message.toolCallId;
|
|
66
|
-
if (!id || !knownToolCallIds.has(id)) continue;
|
|
67
|
-
output.push({
|
|
68
|
-
role: "tool",
|
|
69
|
-
content: message.content,
|
|
70
|
-
tool_call_id: id
|
|
71
|
-
});
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
|
|
75
|
-
for (const tc of message.toolCalls) knownToolCallIds.add(tc.id);
|
|
76
|
-
output.push({
|
|
77
|
-
role: "assistant",
|
|
78
|
-
content: message.content || null,
|
|
79
|
-
tool_calls: message.toolCalls.map((tc) => ({
|
|
80
|
-
id: tc.id,
|
|
81
|
-
type: "function",
|
|
82
|
-
function: {
|
|
83
|
-
name: tc.name,
|
|
84
|
-
arguments: typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args ?? {})
|
|
85
|
-
}
|
|
86
|
-
}))
|
|
87
|
-
});
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (message.role === "assistant" && !message.content) continue;
|
|
91
|
-
output.push({ role: message.role, content: message.content });
|
|
92
|
-
}
|
|
93
|
-
return output;
|
|
94
|
-
}
|
|
95
|
-
async function* readSSELines(stream) {
|
|
96
|
-
const reader = stream.getReader();
|
|
97
|
-
const decoder = new TextDecoder();
|
|
98
|
-
let buffer = "";
|
|
99
|
-
try {
|
|
100
|
-
while (true) {
|
|
101
|
-
const { done, value } = await reader.read();
|
|
102
|
-
if (done) break;
|
|
103
|
-
buffer += decoder.decode(value, { stream: true });
|
|
104
|
-
const lines = buffer.split("\n");
|
|
105
|
-
buffer = lines.pop() ?? "";
|
|
106
|
-
for (const line of lines) {
|
|
107
|
-
if (!line.startsWith("data: ")) continue;
|
|
108
|
-
const data = line.slice(6).trim();
|
|
109
|
-
if (data) yield data;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} finally {
|
|
113
|
-
reader.releaseLock();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
async function* readNDJSONLines(stream) {
|
|
117
|
-
const reader = stream.getReader();
|
|
118
|
-
const decoder = new TextDecoder();
|
|
119
|
-
let buffer = "";
|
|
120
|
-
try {
|
|
121
|
-
while (true) {
|
|
122
|
-
const { done, value } = await reader.read();
|
|
123
|
-
if (done) break;
|
|
124
|
-
buffer += decoder.decode(value, { stream: true });
|
|
125
|
-
const lines = buffer.split("\n");
|
|
126
|
-
buffer = lines.pop() ?? "";
|
|
127
|
-
for (const line of lines) {
|
|
128
|
-
const trimmed = line.trim();
|
|
129
|
-
if (trimmed) yield trimmed;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
} finally {
|
|
133
|
-
reader.releaseLock();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async function* parseOpenAIStream(stream) {
|
|
137
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
138
|
-
for await (const data of readSSELines(stream)) {
|
|
139
|
-
if (data === "[DONE]") {
|
|
140
|
-
for (const [, tc] of pendingToolCalls) {
|
|
141
|
-
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
142
|
-
}
|
|
143
|
-
pendingToolCalls.clear();
|
|
144
|
-
yield { type: "done" };
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
const event = JSON.parse(data);
|
|
149
|
-
const delta = event.choices?.[0]?.delta;
|
|
150
|
-
if (typeof delta?.content === "string") {
|
|
151
|
-
yield { type: "text", content: delta.content };
|
|
152
|
-
}
|
|
153
|
-
if (Array.isArray(delta?.tool_calls)) {
|
|
154
|
-
for (const toolCall of delta.tool_calls) {
|
|
155
|
-
const index = toolCall.index ?? 0;
|
|
156
|
-
const existing = pendingToolCalls.get(index);
|
|
157
|
-
if (toolCall?.function?.name) {
|
|
158
|
-
pendingToolCalls.set(index, {
|
|
159
|
-
id: toolCall.id ?? existing?.id ?? `tool-${index}-${Date.now()}`,
|
|
160
|
-
name: toolCall.function.name,
|
|
161
|
-
args: (existing?.args ?? "") + (toolCall.function.arguments ?? "")
|
|
162
|
-
});
|
|
163
|
-
} else if (existing && toolCall?.function?.arguments) {
|
|
164
|
-
existing.args += toolCall.function.arguments;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (event.usage) {
|
|
169
|
-
yield {
|
|
170
|
-
type: "usage",
|
|
171
|
-
usage: {
|
|
172
|
-
promptTokens: event.usage.prompt_tokens ?? 0,
|
|
173
|
-
completionTokens: event.usage.completion_tokens ?? 0,
|
|
174
|
-
totalTokens: event.usage.total_tokens ?? 0
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
} catch {
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
for (const [, tc] of pendingToolCalls) {
|
|
182
|
-
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
183
|
-
}
|
|
184
|
-
pendingToolCalls.clear();
|
|
185
|
-
yield { type: "done" };
|
|
186
|
-
}
|
|
187
|
-
async function* parseAnthropicStream(stream) {
|
|
188
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
189
|
-
for await (const data of readSSELines(stream)) {
|
|
190
|
-
if (data === "[DONE]") continue;
|
|
191
|
-
try {
|
|
192
|
-
const event = JSON.parse(data);
|
|
193
|
-
if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta?.text) {
|
|
194
|
-
yield { type: "text", content: event.delta.text };
|
|
195
|
-
} else if (event.type === "content_block_delta" && event.delta?.type === "input_json_delta") {
|
|
196
|
-
const index = event.index ?? 0;
|
|
197
|
-
const existing = pendingToolCalls.get(index);
|
|
198
|
-
if (existing) {
|
|
199
|
-
existing.args += event.delta.partial_json ?? "";
|
|
200
|
-
}
|
|
201
|
-
} else if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
202
|
-
const index = event.index ?? 0;
|
|
203
|
-
pendingToolCalls.set(index, {
|
|
204
|
-
id: event.content_block.id,
|
|
205
|
-
name: event.content_block.name,
|
|
206
|
-
args: ""
|
|
207
|
-
});
|
|
208
|
-
} else if (event.type === "content_block_stop") {
|
|
209
|
-
const index = event.index ?? 0;
|
|
210
|
-
const tc = pendingToolCalls.get(index);
|
|
211
|
-
if (tc) {
|
|
212
|
-
yield {
|
|
213
|
-
type: "tool_call",
|
|
214
|
-
toolCall: {
|
|
215
|
-
id: tc.id,
|
|
216
|
-
name: tc.name,
|
|
217
|
-
args: tc.args || "{}"
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
pendingToolCalls.delete(index);
|
|
221
|
-
}
|
|
222
|
-
} else if (event.type === "message_delta" && event.usage) {
|
|
223
|
-
yield {
|
|
224
|
-
type: "usage",
|
|
225
|
-
usage: {
|
|
226
|
-
promptTokens: event.usage.input_tokens ?? 0,
|
|
227
|
-
completionTokens: event.usage.output_tokens ?? 0,
|
|
228
|
-
totalTokens: (event.usage.input_tokens ?? 0) + (event.usage.output_tokens ?? 0)
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
} else if (event.type === "message_start" && event.message?.usage) {
|
|
232
|
-
yield {
|
|
233
|
-
type: "usage",
|
|
234
|
-
usage: {
|
|
235
|
-
promptTokens: event.message.usage.input_tokens ?? 0,
|
|
236
|
-
completionTokens: 0,
|
|
237
|
-
totalTokens: event.message.usage.input_tokens ?? 0
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
} else if (event.type === "message_stop") {
|
|
241
|
-
for (const [, tc] of pendingToolCalls) {
|
|
242
|
-
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
243
|
-
}
|
|
244
|
-
pendingToolCalls.clear();
|
|
245
|
-
yield { type: "done" };
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
} catch {
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
for (const [, tc] of pendingToolCalls) {
|
|
252
|
-
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
253
|
-
}
|
|
254
|
-
pendingToolCalls.clear();
|
|
255
|
-
yield { type: "done" };
|
|
256
|
-
}
|
|
257
|
-
async function* parseGeminiStream(stream) {
|
|
258
|
-
for await (const data of readSSELines(stream)) {
|
|
259
|
-
try {
|
|
260
|
-
const event = JSON.parse(data);
|
|
261
|
-
if (event.usageMetadata) {
|
|
262
|
-
yield {
|
|
263
|
-
type: "usage",
|
|
264
|
-
usage: {
|
|
265
|
-
promptTokens: event.usageMetadata.promptTokenCount ?? 0,
|
|
266
|
-
completionTokens: event.usageMetadata.candidatesTokenCount ?? 0,
|
|
267
|
-
totalTokens: event.usageMetadata.totalTokenCount ?? 0
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
const parts = event.candidates?.[0]?.content?.parts;
|
|
272
|
-
if (!Array.isArray(parts)) continue;
|
|
273
|
-
for (const part of parts) {
|
|
274
|
-
if (typeof part.text === "string" && part.text) {
|
|
275
|
-
yield { type: "text", content: part.text };
|
|
276
|
-
} else if (part.functionCall) {
|
|
277
|
-
const fc = part.functionCall;
|
|
278
|
-
yield {
|
|
279
|
-
type: "tool_call",
|
|
280
|
-
toolCall: {
|
|
281
|
-
id: fc.id ?? `${fc.name}-${Date.now()}`,
|
|
282
|
-
name: fc.name,
|
|
283
|
-
args: JSON.stringify(fc.args ?? {})
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} catch {
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
yield { type: "done" };
|
|
292
|
-
}
|
|
293
|
-
async function* parseOllamaStream(stream) {
|
|
294
|
-
for await (const data of readNDJSONLines(stream)) {
|
|
295
|
-
try {
|
|
296
|
-
const event = JSON.parse(data);
|
|
297
|
-
if (event.message?.content) {
|
|
298
|
-
yield { type: "text", content: event.message.content };
|
|
299
|
-
}
|
|
300
|
-
if (Array.isArray(event.message?.tool_calls)) {
|
|
301
|
-
for (const tc of event.message.tool_calls) {
|
|
302
|
-
if (tc?.function?.name) {
|
|
303
|
-
yield {
|
|
304
|
-
type: "tool_call",
|
|
305
|
-
toolCall: {
|
|
306
|
-
id: tc.id ?? `${tc.function.name}-${Date.now()}`,
|
|
307
|
-
name: tc.function.name,
|
|
308
|
-
args: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments ?? {})
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (event.done) {
|
|
315
|
-
if (typeof event.prompt_eval_count === "number" || typeof event.eval_count === "number") {
|
|
316
|
-
const promptTokens = event.prompt_eval_count ?? 0;
|
|
317
|
-
const completionTokens = event.eval_count ?? 0;
|
|
318
|
-
yield {
|
|
319
|
-
type: "usage",
|
|
320
|
-
usage: {
|
|
321
|
-
promptTokens,
|
|
322
|
-
completionTokens,
|
|
323
|
-
totalTokens: promptTokens + completionTokens
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
yield { type: "done" };
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
} catch {
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
yield { type: "done" };
|
|
334
|
-
}
|
|
335
|
-
var DEFAULT_RETRY = {
|
|
336
|
-
maxAttempts: 3,
|
|
337
|
-
baseDelayMs: 500,
|
|
338
|
-
maxDelayMs: 8e3,
|
|
339
|
-
jitter: true,
|
|
340
|
-
retryOn: ({ error, response }) => {
|
|
341
|
-
if (response) {
|
|
342
|
-
return [408, 429, 500, 502, 503, 504].includes(response.status);
|
|
343
|
-
}
|
|
344
|
-
if (error instanceof DOMException && error.name === "AbortError") return false;
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
function defaultSleep(ms) {
|
|
349
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
350
|
-
}
|
|
351
|
-
function computeDelay(attempt, opts) {
|
|
352
|
-
const exp = Math.min(opts.maxDelayMs, opts.baseDelayMs * Math.pow(2, attempt - 1));
|
|
353
|
-
if (!opts.jitter) return exp;
|
|
354
|
-
return Math.floor(Math.random() * exp);
|
|
355
|
-
}
|
|
356
|
-
function parseRetryAfter(value) {
|
|
357
|
-
if (!value) return void 0;
|
|
358
|
-
const n = Number(value);
|
|
359
|
-
if (!Number.isNaN(n)) return n * 1e3;
|
|
360
|
-
const date = Date.parse(value);
|
|
361
|
-
if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
|
|
362
|
-
return void 0;
|
|
363
|
-
}
|
|
364
|
-
async function fetchWithRetry(doFetch, signal, retryOpt = {}) {
|
|
365
|
-
const opts = {
|
|
366
|
-
...DEFAULT_RETRY,
|
|
367
|
-
...retryOpt
|
|
368
|
-
};
|
|
369
|
-
const sleep = retryOpt.sleep ?? defaultSleep;
|
|
370
|
-
let lastError;
|
|
371
|
-
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
372
|
-
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
373
|
-
try {
|
|
374
|
-
const response = await doFetch(signal);
|
|
375
|
-
if (response.ok) return response;
|
|
376
|
-
if (attempt >= opts.maxAttempts || !opts.retryOn({ response, attempt })) {
|
|
377
|
-
return response;
|
|
378
|
-
}
|
|
379
|
-
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
|
|
380
|
-
const delay = retryAfterMs ?? computeDelay(attempt, opts);
|
|
381
|
-
retryOpt.onRetry?.({ attempt, delayMs: delay, reason: `HTTP ${response.status}` });
|
|
382
|
-
await sleep(delay);
|
|
383
|
-
response.body?.cancel().catch(() => {
|
|
384
|
-
});
|
|
385
|
-
} catch (err) {
|
|
386
|
-
lastError = err;
|
|
387
|
-
if (err instanceof DOMException && err.name === "AbortError") throw err;
|
|
388
|
-
if (attempt >= opts.maxAttempts || !opts.retryOn({ error: err, attempt })) throw err;
|
|
389
|
-
const delay = computeDelay(attempt, opts);
|
|
390
|
-
retryOpt.onRetry?.({ attempt, delayMs: delay, reason: err.message });
|
|
391
|
-
await sleep(delay);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
throw lastError ?? new Error("fetchWithRetry: exhausted attempts");
|
|
395
|
-
}
|
|
396
|
-
function createStreamSource(doFetch, parse, errorLabel, retry) {
|
|
397
|
-
let abortController = new AbortController();
|
|
398
|
-
return {
|
|
399
|
-
stream: async function* () {
|
|
400
|
-
try {
|
|
401
|
-
const response = await fetchWithRetry(doFetch, abortController.signal, retry ?? {});
|
|
402
|
-
if (!response.ok || !response.body) {
|
|
403
|
-
yield { type: "error", content: `${errorLabel} error: ${response.status}` };
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
yield* parse(response.body);
|
|
407
|
-
} catch (err) {
|
|
408
|
-
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
409
|
-
yield {
|
|
410
|
-
type: "error",
|
|
411
|
-
content: err instanceof Error ? err.message : String(err)
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
},
|
|
415
|
-
abort: () => {
|
|
416
|
-
abortController?.abort();
|
|
417
|
-
abortController = null;
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
function chunkText(text, targetSize = 32) {
|
|
422
|
-
if (text.length <= targetSize) return [text];
|
|
423
|
-
const out = [];
|
|
424
|
-
let i = 0;
|
|
425
|
-
while (i < text.length) {
|
|
426
|
-
let end = Math.min(text.length, i + targetSize);
|
|
427
|
-
if (end < text.length) {
|
|
428
|
-
const nextSpace = text.indexOf(" ", end);
|
|
429
|
-
if (nextSpace !== -1 && nextSpace - end <= 8) end = nextSpace + 1;
|
|
430
|
-
}
|
|
431
|
-
out.push(text.slice(i, end));
|
|
432
|
-
i = end;
|
|
433
|
-
}
|
|
434
|
-
return out;
|
|
435
|
-
}
|
|
436
|
-
function simulateStream(doFetch, extractText, errorLabel, options = {}) {
|
|
437
|
-
const { chunkSize = 32, delayMs = 8, retry } = options;
|
|
438
|
-
let abortController = new AbortController();
|
|
439
|
-
return {
|
|
440
|
-
stream: async function* () {
|
|
441
|
-
try {
|
|
442
|
-
const response = await fetchWithRetry(doFetch, abortController.signal, retry ?? {});
|
|
443
|
-
if (!response.ok) {
|
|
444
|
-
yield { type: "error", content: `${errorLabel} error: ${response.status}` };
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
const text = await extractText(response);
|
|
448
|
-
const chunks = chunkText(text, chunkSize);
|
|
449
|
-
for (const chunk of chunks) {
|
|
450
|
-
if (abortController === null) return;
|
|
451
|
-
if (delayMs > 0) {
|
|
452
|
-
await new Promise((resolve2, reject) => {
|
|
453
|
-
const timer = setTimeout(resolve2, delayMs);
|
|
454
|
-
abortController?.signal.addEventListener(
|
|
455
|
-
"abort",
|
|
456
|
-
() => {
|
|
457
|
-
clearTimeout(timer);
|
|
458
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
459
|
-
},
|
|
460
|
-
{ once: true }
|
|
461
|
-
);
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
yield { type: "text", content: chunk };
|
|
465
|
-
}
|
|
466
|
-
yield { type: "done" };
|
|
467
|
-
} catch (err) {
|
|
468
|
-
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
469
|
-
yield {
|
|
470
|
-
type: "error",
|
|
471
|
-
content: err instanceof Error ? err.message : String(err)
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
},
|
|
475
|
-
abort: () => {
|
|
476
|
-
abortController?.abort();
|
|
477
|
-
abortController = null;
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
|
|
482
41
|
// src/anthropic.ts
|
|
483
42
|
function anthropic(config) {
|
|
484
43
|
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096, retry } = config;
|
|
@@ -522,55 +81,6 @@ function anthropic(config) {
|
|
|
522
81
|
};
|
|
523
82
|
}
|
|
524
83
|
|
|
525
|
-
// src/openai.ts
|
|
526
|
-
function openai(config) {
|
|
527
|
-
const { apiKey, model, baseUrl = "https://api.openai.com", retry } = config;
|
|
528
|
-
const includeUsage = config.includeUsage ?? baseUrl.startsWith("https://api.openai.com");
|
|
529
|
-
return {
|
|
530
|
-
capabilities: {
|
|
531
|
-
streaming: true,
|
|
532
|
-
tools: true,
|
|
533
|
-
// o1 / o3 models emit reasoning; older models don't. Accurate per-model
|
|
534
|
-
// detection would need a model registry; 'true' is the safer default here.
|
|
535
|
-
reasoning: model.startsWith("o1") || model.startsWith("o3"),
|
|
536
|
-
multiModal: model.startsWith("gpt-4") || model.startsWith("o"),
|
|
537
|
-
usage: true
|
|
538
|
-
},
|
|
539
|
-
createSource: (request) => {
|
|
540
|
-
const body = {
|
|
541
|
-
model,
|
|
542
|
-
messages: toProviderMessages(request.messages),
|
|
543
|
-
tools: request.context?.tools?.map((tool) => ({
|
|
544
|
-
type: "function",
|
|
545
|
-
function: {
|
|
546
|
-
name: tool.name,
|
|
547
|
-
description: tool.description,
|
|
548
|
-
parameters: tool.schema
|
|
549
|
-
}
|
|
550
|
-
})),
|
|
551
|
-
temperature: request.context?.temperature,
|
|
552
|
-
max_tokens: request.context?.maxTokens,
|
|
553
|
-
stream: true
|
|
554
|
-
};
|
|
555
|
-
if (includeUsage) body.stream_options = { include_usage: true };
|
|
556
|
-
return createStreamSource(
|
|
557
|
-
(signal) => fetch(`${baseUrl}/v1/chat/completions`, {
|
|
558
|
-
method: "POST",
|
|
559
|
-
headers: {
|
|
560
|
-
"Content-Type": "application/json",
|
|
561
|
-
"Authorization": `Bearer ${apiKey}`
|
|
562
|
-
},
|
|
563
|
-
body: JSON.stringify(body),
|
|
564
|
-
signal
|
|
565
|
-
}),
|
|
566
|
-
parseOpenAIStream,
|
|
567
|
-
"OpenAI API",
|
|
568
|
-
retry
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
84
|
// src/gemini.ts
|
|
575
85
|
function gemini(config) {
|
|
576
86
|
const { apiKey, model, baseUrl = "https://generativelanguage.googleapis.com", retry } = config;
|
|
@@ -582,15 +92,13 @@ function gemini(config) {
|
|
|
582
92
|
usage: true
|
|
583
93
|
},
|
|
584
94
|
createSource: (request) => {
|
|
95
|
+
const systemMessage = request.messages.find((message) => message.role === "system");
|
|
585
96
|
const body = {
|
|
586
97
|
contents: request.messages.filter((message) => message.role !== "system").map((message) => ({
|
|
587
98
|
role: message.role === "assistant" ? "model" : "user",
|
|
588
99
|
parts: [{ text: message.content }]
|
|
589
100
|
})),
|
|
590
|
-
systemInstruction:
|
|
591
|
-
role: "system",
|
|
592
|
-
parts: [{ text: request.messages.find((message) => message.role === "system").content }]
|
|
593
|
-
} : void 0
|
|
101
|
+
systemInstruction: systemMessage ? { role: "system", parts: [{ text: systemMessage.content }] } : void 0
|
|
594
102
|
};
|
|
595
103
|
return createStreamSource(
|
|
596
104
|
(signal) => fetch(
|
|
@@ -1056,15 +564,13 @@ function vertex(config) {
|
|
|
1056
564
|
reasoning: model.includes("pro")
|
|
1057
565
|
},
|
|
1058
566
|
createSource: (request) => {
|
|
567
|
+
const systemMessage = request.messages.find((message) => message.role === "system");
|
|
1059
568
|
const body = {
|
|
1060
569
|
contents: request.messages.filter((message) => message.role !== "system").map((message) => ({
|
|
1061
570
|
role: message.role === "assistant" ? "model" : "user",
|
|
1062
571
|
parts: [{ text: message.content }]
|
|
1063
572
|
})),
|
|
1064
|
-
systemInstruction:
|
|
1065
|
-
role: "system",
|
|
1066
|
-
parts: [{ text: request.messages.find((message) => message.role === "system").content }]
|
|
1067
|
-
} : void 0,
|
|
573
|
+
systemInstruction: systemMessage ? { role: "system", parts: [{ text: systemMessage.content }] } : void 0,
|
|
1068
574
|
tools: request.context?.tools && request.context.tools.length > 0 ? [{
|
|
1069
575
|
functionDeclarations: request.context.tools.map((tool) => ({
|
|
1070
576
|
name: tool.name,
|
|
@@ -1094,8 +600,6 @@ function vertex(config) {
|
|
|
1094
600
|
};
|
|
1095
601
|
}
|
|
1096
602
|
var vertexAdapter = vertex;
|
|
1097
|
-
|
|
1098
|
-
// src/bedrock.ts
|
|
1099
603
|
var cachedSdk = null;
|
|
1100
604
|
async function loadSdk() {
|
|
1101
605
|
if (!cachedSdk) {
|
|
@@ -1107,9 +611,11 @@ async function loadSdk() {
|
|
|
1107
611
|
moduleId
|
|
1108
612
|
);
|
|
1109
613
|
} catch {
|
|
1110
|
-
throw new
|
|
1111
|
-
|
|
1112
|
-
|
|
614
|
+
throw new AdapterError({
|
|
615
|
+
code: ErrorCodes.AK_ADAPTER_MISSING,
|
|
616
|
+
message: "Install @aws-sdk/client-bedrock-runtime to use the bedrock adapter: npm install @aws-sdk/client-bedrock-runtime",
|
|
617
|
+
hint: "bedrock depends on the optional peer @aws-sdk/client-bedrock-runtime."
|
|
618
|
+
});
|
|
1113
619
|
}
|
|
1114
620
|
})();
|
|
1115
621
|
}
|
|
@@ -1210,9 +716,11 @@ async function* parseAnthropicBedrockEvents(events) {
|
|
|
1210
716
|
function bedrock(config) {
|
|
1211
717
|
const { model, region, maxTokens = 4096 } = config;
|
|
1212
718
|
if (!isAnthropicModel(model)) {
|
|
1213
|
-
throw new
|
|
1214
|
-
|
|
1215
|
-
|
|
719
|
+
throw new ConfigError({
|
|
720
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
721
|
+
message: `bedrock: model "${model}" is not supported in v1. Only anthropic.* models are wired up; Titan + others are tracked as a follow-up.`,
|
|
722
|
+
hint: 'Pass an anthropic.* model id like "anthropic.claude-3-5-sonnet-20241022-v2:0".'
|
|
723
|
+
});
|
|
1216
724
|
}
|
|
1217
725
|
let clientPromise = null;
|
|
1218
726
|
const getClient = async () => {
|
|
@@ -1271,8 +779,6 @@ function bedrock(config) {
|
|
|
1271
779
|
};
|
|
1272
780
|
}
|
|
1273
781
|
var bedrockAdapter = bedrock;
|
|
1274
|
-
|
|
1275
|
-
// src/webllm.ts
|
|
1276
782
|
var cachedSdk2 = null;
|
|
1277
783
|
async function loadSdk2() {
|
|
1278
784
|
if (!cachedSdk2) {
|
|
@@ -1284,7 +790,11 @@ async function loadSdk2() {
|
|
|
1284
790
|
moduleId
|
|
1285
791
|
);
|
|
1286
792
|
} catch {
|
|
1287
|
-
throw new
|
|
793
|
+
throw new AdapterError({
|
|
794
|
+
code: ErrorCodes.AK_ADAPTER_MISSING,
|
|
795
|
+
message: "Install @mlc-ai/web-llm to use the webllm adapter: npm install @mlc-ai/web-llm",
|
|
796
|
+
hint: "webllm is browser-only and depends on the optional peer @mlc-ai/web-llm."
|
|
797
|
+
});
|
|
1288
798
|
}
|
|
1289
799
|
})();
|
|
1290
800
|
}
|
|
@@ -1334,8 +844,6 @@ function webllm(config) {
|
|
|
1334
844
|
};
|
|
1335
845
|
}
|
|
1336
846
|
var webllmAdapter = webllm;
|
|
1337
|
-
|
|
1338
|
-
// src/router.ts
|
|
1339
847
|
function requireCapabilities(request) {
|
|
1340
848
|
const needs = {};
|
|
1341
849
|
if (request.context?.tools && request.context.tools.length > 0) needs.tools = true;
|
|
@@ -1358,34 +866,75 @@ function pickSyncPolicy(pool, policy) {
|
|
|
1358
866
|
const c = pool.reduce((best, x) => (x.latencyMs ?? Infinity) < (best.latencyMs ?? Infinity) ? x : best, pool[0]);
|
|
1359
867
|
return { c, reason: "fastest" };
|
|
1360
868
|
}
|
|
869
|
+
if (policy === "greenest") {
|
|
870
|
+
const c = pool.reduce((best, x) => (x.gCO2PerKtok ?? Infinity) < (best.gCO2PerKtok ?? Infinity) ? x : best, pool[0]);
|
|
871
|
+
return { c, reason: "greenest" };
|
|
872
|
+
}
|
|
873
|
+
if (policy === "green-cost") {
|
|
874
|
+
const carbonValues = pool.map((p) => p.gCO2PerKtok).filter((v) => v != null);
|
|
875
|
+
const costValues = pool.map((p) => p.cost).filter((v) => v != null);
|
|
876
|
+
const maxC = Math.max(1, ...carbonValues);
|
|
877
|
+
const maxCost = Math.max(1, ...costValues);
|
|
878
|
+
const score = (x) => {
|
|
879
|
+
const carbon = (x.gCO2PerKtok ?? maxC / 2) / maxC;
|
|
880
|
+
const cost = (x.cost ?? maxCost / 2) / maxCost;
|
|
881
|
+
return carbon + cost;
|
|
882
|
+
};
|
|
883
|
+
const c = pool.reduce((best, x) => score(x) < score(best) ? x : best, pool[0]);
|
|
884
|
+
return { c, reason: "green-cost" };
|
|
885
|
+
}
|
|
1361
886
|
return { c: pool[0], reason: "capability-match" };
|
|
1362
887
|
}
|
|
888
|
+
function matchesRegion(candidate, region) {
|
|
889
|
+
return region === void 0 || candidate.region === region;
|
|
890
|
+
}
|
|
1363
891
|
function createRouter(options) {
|
|
1364
892
|
const { candidates } = options;
|
|
1365
|
-
if (candidates.length === 0)
|
|
893
|
+
if (candidates.length === 0) {
|
|
894
|
+
throw new ConfigError({
|
|
895
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
896
|
+
message: "createRouter requires at least one candidate",
|
|
897
|
+
hint: "Pass at least one candidate, e.g. createRouter({ candidates: [{ id, adapter, capabilities }] })."
|
|
898
|
+
});
|
|
899
|
+
}
|
|
1366
900
|
const policy = options.policy ?? "cheapest";
|
|
1367
901
|
return {
|
|
1368
902
|
createSource: (request) => {
|
|
1369
903
|
const need = requireCapabilities(request);
|
|
904
|
+
const region = options.regionOf?.(request) ?? options.region;
|
|
1370
905
|
const capable = candidates.filter((c2) => matchesCapabilities(need, c2.capabilities ?? c2.adapter.capabilities));
|
|
906
|
+
const eligible = capable.filter((c2) => matchesRegion(c2, region));
|
|
907
|
+
if (capable.length > 0 && eligible.length === 0 && region !== void 0) {
|
|
908
|
+
throw new ConfigError({
|
|
909
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
910
|
+
message: `no candidate satisfies required region: ${region}`,
|
|
911
|
+
hint: "Set candidate.region to the requested data-residency region, or remove the router region requirement."
|
|
912
|
+
});
|
|
913
|
+
}
|
|
1371
914
|
const classified = options.classify?.(request);
|
|
1372
915
|
if (typeof classified === "string") {
|
|
1373
|
-
const c2 =
|
|
916
|
+
const c2 = eligible.find((x) => x.id === classified);
|
|
1374
917
|
if (c2) {
|
|
1375
|
-
options.onRoute?.({ id: c2.id, reason: "classify:id", request });
|
|
918
|
+
options.onRoute?.({ id: c2.id, reason: region ? `classify:id:${region}` : "classify:id", request });
|
|
1376
919
|
return c2.adapter.createSource(request);
|
|
1377
920
|
}
|
|
1378
921
|
}
|
|
1379
|
-
let pool =
|
|
922
|
+
let pool = eligible;
|
|
1380
923
|
let pickedBy;
|
|
1381
924
|
if (Array.isArray(classified) && classified.length > 0) {
|
|
1382
|
-
const filtered =
|
|
925
|
+
const filtered = eligible.filter((c2) => c2.tags && classified.every((t) => c2.tags.includes(t)));
|
|
1383
926
|
if (filtered.length > 0) {
|
|
1384
927
|
pool = filtered;
|
|
1385
|
-
pickedBy = "classify:tags";
|
|
928
|
+
pickedBy = region ? `classify:tags:${region}` : "classify:tags";
|
|
1386
929
|
}
|
|
1387
930
|
}
|
|
1388
|
-
if (pool.length === 0)
|
|
931
|
+
if (pool.length === 0) {
|
|
932
|
+
throw new AdapterError({
|
|
933
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
934
|
+
message: "no candidate satisfies the request",
|
|
935
|
+
hint: "Ensure at least one candidate declares the required capabilities (tools, json, etc.)."
|
|
936
|
+
});
|
|
937
|
+
}
|
|
1389
938
|
if (typeof policy === "function") {
|
|
1390
939
|
const maybe = policy({ request, candidates: pool });
|
|
1391
940
|
if (maybe instanceof Promise) {
|
|
@@ -1395,14 +944,26 @@ function createRouter(options) {
|
|
|
1395
944
|
stream: async function* () {
|
|
1396
945
|
const id = await maybe;
|
|
1397
946
|
const c3 = pool.find((x) => x.id === id);
|
|
1398
|
-
if (!c3)
|
|
947
|
+
if (!c3) {
|
|
948
|
+
throw new ConfigError({
|
|
949
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
950
|
+
message: `policy returned unknown id: ${id}`,
|
|
951
|
+
hint: "Custom policy functions must return one of the candidate ids."
|
|
952
|
+
});
|
|
953
|
+
}
|
|
1399
954
|
options.onRoute?.({ id: c3.id, reason: pickedBy ?? "custom policy", request });
|
|
1400
955
|
for await (const chunk of c3.adapter.createSource(request).stream()) yield chunk;
|
|
1401
956
|
}
|
|
1402
957
|
};
|
|
1403
958
|
}
|
|
1404
959
|
const c2 = pool.find((x) => x.id === maybe);
|
|
1405
|
-
if (!c2)
|
|
960
|
+
if (!c2) {
|
|
961
|
+
throw new ConfigError({
|
|
962
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
963
|
+
message: `policy returned unknown id: ${maybe}`,
|
|
964
|
+
hint: "Custom policy functions must return one of the candidate ids."
|
|
965
|
+
});
|
|
966
|
+
}
|
|
1406
967
|
options.onRoute?.({ id: c2.id, reason: pickedBy ?? "custom policy", request });
|
|
1407
968
|
return c2.adapter.createSource(request);
|
|
1408
969
|
}
|
|
@@ -1413,7 +974,56 @@ function createRouter(options) {
|
|
|
1413
974
|
};
|
|
1414
975
|
}
|
|
1415
976
|
|
|
1416
|
-
// src/
|
|
977
|
+
// src/carbon.ts
|
|
978
|
+
var DEFAULT_CARBON_TABLE = {
|
|
979
|
+
// Anthropic — primarily AWS us-east-* / eu-west-* via Bedrock-style fronts.
|
|
980
|
+
"anthropic:us-east-1": 0.42,
|
|
981
|
+
"anthropic:us-west-2": 0.18,
|
|
982
|
+
"anthropic:eu-central-1": 0.32,
|
|
983
|
+
"anthropic:eu-west-1": 0.27,
|
|
984
|
+
// OpenAI — Azure East US / West Europe / Sweden Central.
|
|
985
|
+
"openai:eastus": 0.4,
|
|
986
|
+
"openai:westus3": 0.19,
|
|
987
|
+
"openai:swedencentral": 0.04,
|
|
988
|
+
"openai:westeurope": 0.28,
|
|
989
|
+
// Google (Vertex / Gemini) — typically reports lowest PUE.
|
|
990
|
+
"google:us-central1": 0.34,
|
|
991
|
+
"google:us-west1": 0.16,
|
|
992
|
+
"google:europe-west1": 0.21,
|
|
993
|
+
"google:europe-north1": 0.05,
|
|
994
|
+
// AWS Bedrock direct.
|
|
995
|
+
"aws:us-east-1": 0.42,
|
|
996
|
+
"aws:us-west-2": 0.18,
|
|
997
|
+
"aws:eu-west-1": 0.27,
|
|
998
|
+
// Local / on-device — only the device's grid matters.
|
|
999
|
+
"ollama:local": 0.3,
|
|
1000
|
+
"webllm:browser": 0.3,
|
|
1001
|
+
"lmstudio:local": 0.3,
|
|
1002
|
+
// Self-hosted / community.
|
|
1003
|
+
"groq:us-east-1": 0.4,
|
|
1004
|
+
"fireworks:us-west-2": 0.18,
|
|
1005
|
+
"together:us-east-1": 0.42,
|
|
1006
|
+
"cerebras:us-west-2": 0.18
|
|
1007
|
+
};
|
|
1008
|
+
function defaultKey(candidate) {
|
|
1009
|
+
if (!candidate.region) return void 0;
|
|
1010
|
+
const provider = candidate.id.split(/[-:]/)[0];
|
|
1011
|
+
return `${provider}:${candidate.region}`;
|
|
1012
|
+
}
|
|
1013
|
+
function applyCarbonTable(candidates, options = {}) {
|
|
1014
|
+
const table = options.table ?? DEFAULT_CARBON_TABLE;
|
|
1015
|
+
const keyFor = options.keyFor ?? defaultKey;
|
|
1016
|
+
return candidates.map((c) => {
|
|
1017
|
+
if (c.gCO2PerKtok != null) return c;
|
|
1018
|
+
const key = keyFor(c);
|
|
1019
|
+
const value = key ? table[key] : void 0;
|
|
1020
|
+
return { ...c, gCO2PerKtok: value ?? options.fallback };
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
function estimateCO2Grams(gCO2PerKtok, tokens) {
|
|
1024
|
+
if (gCO2PerKtok == null) return 0;
|
|
1025
|
+
return tokens / 1e3 * gCO2PerKtok;
|
|
1026
|
+
}
|
|
1417
1027
|
async function drain(source, timeoutMs) {
|
|
1418
1028
|
const chunks = [];
|
|
1419
1029
|
let text = "";
|
|
@@ -1458,7 +1068,11 @@ function majorityVote(branches, candidates) {
|
|
|
1458
1068
|
}
|
|
1459
1069
|
function createEnsembleAdapter(options) {
|
|
1460
1070
|
if (options.candidates.length === 0) {
|
|
1461
|
-
throw new
|
|
1071
|
+
throw new ConfigError({
|
|
1072
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1073
|
+
message: "createEnsembleAdapter requires at least one candidate",
|
|
1074
|
+
hint: "Pass at least one candidate, e.g. createEnsembleAdapter({ candidates: [{ id, adapter }] })."
|
|
1075
|
+
});
|
|
1462
1076
|
}
|
|
1463
1077
|
const aggregate = options.aggregate ?? "majority-vote";
|
|
1464
1078
|
return {
|
|
@@ -1498,7 +1112,14 @@ function createEnsembleAdapter(options) {
|
|
|
1498
1112
|
if (aborted) return;
|
|
1499
1113
|
const text = await finalText(branches);
|
|
1500
1114
|
if (!text && branches.every((b) => b.error)) {
|
|
1501
|
-
|
|
1115
|
+
const summary = branches.map((b) => `${b.id}: ${b.error?.message ?? "unknown"}`).join("; ");
|
|
1116
|
+
const err = new AdapterError({
|
|
1117
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1118
|
+
message: `all ensemble branches failed (${summary})`,
|
|
1119
|
+
hint: "Inspect each branch error via options.onBranches or the err.branchErrors field."
|
|
1120
|
+
});
|
|
1121
|
+
err.branchErrors = branches.map((b) => ({ id: b.id, error: b.error }));
|
|
1122
|
+
throw err;
|
|
1502
1123
|
}
|
|
1503
1124
|
yield { type: "text", content: text, metadata: { ensemble: branches.map((b) => ({ id: b.id, bytes: b.text.length, error: b.error?.message })) } };
|
|
1504
1125
|
yield { type: "done" };
|
|
@@ -1507,11 +1128,13 @@ function createEnsembleAdapter(options) {
|
|
|
1507
1128
|
}
|
|
1508
1129
|
};
|
|
1509
1130
|
}
|
|
1510
|
-
|
|
1511
|
-
// src/fallback.ts
|
|
1512
1131
|
function createFallbackAdapter(candidates, options = {}) {
|
|
1513
1132
|
if (candidates.length === 0) {
|
|
1514
|
-
throw new
|
|
1133
|
+
throw new ConfigError({
|
|
1134
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1135
|
+
message: "createFallbackAdapter requires at least one candidate",
|
|
1136
|
+
hint: 'Pass at least one candidate, e.g. createFallbackAdapter([{ id: "primary", adapter }]).'
|
|
1137
|
+
});
|
|
1515
1138
|
}
|
|
1516
1139
|
return {
|
|
1517
1140
|
createSource: (request) => {
|
|
@@ -1566,14 +1189,16 @@ function createFallbackAdapter(candidates, options = {}) {
|
|
|
1566
1189
|
}
|
|
1567
1190
|
}
|
|
1568
1191
|
const summary = errors.map((e) => `${e.id}: ${e.error.message}`).join("; ");
|
|
1569
|
-
throw new
|
|
1192
|
+
throw new AdapterError({
|
|
1193
|
+
code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
|
|
1194
|
+
message: `all fallback candidates failed (${summary})`,
|
|
1195
|
+
hint: "Check each candidate adapter's configuration and provider keys."
|
|
1196
|
+
});
|
|
1570
1197
|
}
|
|
1571
1198
|
};
|
|
1572
1199
|
}
|
|
1573
1200
|
};
|
|
1574
1201
|
}
|
|
1575
|
-
|
|
1576
|
-
// src/mock.ts
|
|
1577
1202
|
function mockAdapter(options) {
|
|
1578
1203
|
const { response, delayMs = 0, history } = options;
|
|
1579
1204
|
let callIndex = 0;
|
|
@@ -1658,21 +1283,34 @@ function inMemorySink() {
|
|
|
1658
1283
|
}
|
|
1659
1284
|
function replayAdapter(fixture) {
|
|
1660
1285
|
if (fixture.length === 0) {
|
|
1661
|
-
throw new
|
|
1286
|
+
throw new ConfigError({
|
|
1287
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1288
|
+
message: "replayAdapter: fixture is empty",
|
|
1289
|
+
hint: "Pass a non-empty fixture; record one with recordingAdapter() first."
|
|
1290
|
+
});
|
|
1662
1291
|
}
|
|
1663
1292
|
return mockAdapter({
|
|
1664
1293
|
response: fixture.map((turn) => turn.chunks)
|
|
1665
1294
|
});
|
|
1666
1295
|
}
|
|
1667
1296
|
|
|
1297
|
+
// src/embedders/shared.ts
|
|
1298
|
+
var ERROR_BODY_PREVIEW_CHARS = 200;
|
|
1299
|
+
async function throwIfNotOk(response, label, url) {
|
|
1300
|
+
if (response.ok) return;
|
|
1301
|
+
const body = await response.text().catch(() => "");
|
|
1302
|
+
throw new Error(
|
|
1303
|
+
`HTTP ${response.status} from ${label} ${url}: ${body.slice(0, ERROR_BODY_PREVIEW_CHARS)}`
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1668
1307
|
// src/embedders/openai.ts
|
|
1669
1308
|
async function fetchAvailableModels(baseUrl, apiKey) {
|
|
1670
|
-
const
|
|
1309
|
+
const url = `${baseUrl}/v1/models`;
|
|
1310
|
+
const response = await fetch(url, {
|
|
1671
1311
|
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
1672
1312
|
});
|
|
1673
|
-
|
|
1674
|
-
throw new Error(`HTTP ${response.status}`);
|
|
1675
|
-
}
|
|
1313
|
+
await throwIfNotOk(response, "openai", url);
|
|
1676
1314
|
const data = await response.json();
|
|
1677
1315
|
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
1678
1316
|
}
|
|
@@ -1712,10 +1350,9 @@ function openaiEmbedder(config) {
|
|
|
1712
1350
|
|
|
1713
1351
|
// src/embedders/gemini.ts
|
|
1714
1352
|
async function fetchAvailableModels2(baseUrl, apiKey) {
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
}
|
|
1353
|
+
const url = `${baseUrl}/v1beta/models?key=${apiKey}`;
|
|
1354
|
+
const response = await fetch(url);
|
|
1355
|
+
await throwIfNotOk(response, "gemini", `${baseUrl}/v1beta/models`);
|
|
1719
1356
|
const data = await response.json();
|
|
1720
1357
|
return data.models.filter((m) => m.supportedGenerationMethods.includes("embedContent")).map((m) => m.name.replace("models/", "")).sort();
|
|
1721
1358
|
}
|
|
@@ -1762,10 +1399,9 @@ function geminiEmbedder(config) {
|
|
|
1762
1399
|
|
|
1763
1400
|
// src/embedders/ollama.ts
|
|
1764
1401
|
async function fetchAvailableModels3(baseUrl) {
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
}
|
|
1402
|
+
const url = `${baseUrl}/api/tags`;
|
|
1403
|
+
const response = await fetch(url);
|
|
1404
|
+
await throwIfNotOk(response, "ollama", url);
|
|
1769
1405
|
const data = await response.json();
|
|
1770
1406
|
return data.models.map((m) => m.name).filter((name) => name.includes("embed")).sort();
|
|
1771
1407
|
}
|
|
@@ -1799,21 +1435,18 @@ function ollamaEmbedder(config) {
|
|
|
1799
1435
|
return data.embeddings[0];
|
|
1800
1436
|
};
|
|
1801
1437
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
1438
|
+
async function fetchAvailableModels4(provider, baseUrl, apiKey) {
|
|
1439
|
+
const url = `${baseUrl}/v1/models`;
|
|
1440
|
+
const response = await fetch(url, {
|
|
1806
1441
|
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
1807
1442
|
});
|
|
1808
|
-
|
|
1809
|
-
throw new Error(`HTTP ${response.status}`);
|
|
1810
|
-
}
|
|
1443
|
+
await throwIfNotOk(response, provider, url);
|
|
1811
1444
|
const data = await response.json();
|
|
1812
1445
|
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
1813
1446
|
}
|
|
1814
1447
|
async function buildModelError4(provider, baseUrl, apiKey, originalError) {
|
|
1815
1448
|
try {
|
|
1816
|
-
const models = await fetchAvailableModels4(baseUrl, apiKey);
|
|
1449
|
+
const models = await fetchAvailableModels4(provider, baseUrl, apiKey);
|
|
1817
1450
|
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
1818
1451
|
return new Error(
|
|
1819
1452
|
`${provider} embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
@@ -1827,7 +1460,11 @@ async function buildModelError4(provider, baseUrl, apiKey, originalError) {
|
|
|
1827
1460
|
function createOpenAICompatibleEmbedder(provider, defaultBaseUrl) {
|
|
1828
1461
|
return function embedder(config) {
|
|
1829
1462
|
if (!config.model) {
|
|
1830
|
-
throw new
|
|
1463
|
+
throw new ConfigError({
|
|
1464
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1465
|
+
message: `Model is required for ${provider}. Pass { model: "<model-name>" }.`,
|
|
1466
|
+
hint: `${provider} does not infer a default; pick a recent embedding model and pass it explicitly.`
|
|
1467
|
+
});
|
|
1831
1468
|
}
|
|
1832
1469
|
const { apiKey, model, baseUrl = defaultBaseUrl } = config;
|
|
1833
1470
|
return async (text) => {
|
|
@@ -1858,7 +1495,91 @@ var grokEmbedder = createOpenAICompatibleEmbedder("Grok", "https://api.x.ai");
|
|
|
1858
1495
|
|
|
1859
1496
|
// src/embedders/kimi.ts
|
|
1860
1497
|
var kimiEmbedder = createOpenAICompatibleEmbedder("Kimi", "https://api.moonshot.ai");
|
|
1498
|
+
var DEFAULT_DEPRECATION_TABLE = [
|
|
1499
|
+
// OpenAI
|
|
1500
|
+
{ provider: "openai", model: "gpt-3.5-turbo-0301", successor: "gpt-4o-mini", sunsetOn: "2024-09-13" },
|
|
1501
|
+
{ provider: "openai", model: "gpt-3.5-turbo-0613", successor: "gpt-4o-mini", sunsetOn: "2024-09-13" },
|
|
1502
|
+
{ provider: "openai", model: "gpt-4-0314", successor: "gpt-4o", sunsetOn: "2024-06-13" },
|
|
1503
|
+
{ provider: "openai", model: "gpt-4-32k", successor: "gpt-4o", sunsetOn: "2024-06-06" },
|
|
1504
|
+
{ provider: "openai", model: "text-davinci-003", successor: "gpt-4o-mini", sunsetOn: "2024-01-04" },
|
|
1505
|
+
// Anthropic
|
|
1506
|
+
{ provider: "anthropic", model: "claude-2.0", successor: "claude-3-5-sonnet-latest", sunsetOn: "2025-07-21" },
|
|
1507
|
+
{ provider: "anthropic", model: "claude-2.1", successor: "claude-3-5-sonnet-latest", sunsetOn: "2025-07-21" },
|
|
1508
|
+
{ provider: "anthropic", model: "claude-instant-1.2", successor: "claude-3-5-haiku-latest", sunsetOn: "2024-07-22" },
|
|
1509
|
+
{ provider: "anthropic", model: "claude-3-sonnet-20240229", successor: "claude-3-5-sonnet-latest", sunsetOn: "2025-07-21" },
|
|
1510
|
+
// Google
|
|
1511
|
+
{ provider: "google", model: "gemini-1.0-pro", successor: "gemini-1.5-pro", sunsetOn: "2025-02-15" },
|
|
1512
|
+
{ provider: "google", model: "gemini-pro-vision", successor: "gemini-1.5-pro", sunsetOn: "2024-07-12" }
|
|
1513
|
+
];
|
|
1514
|
+
function resolveModel(input, policy) {
|
|
1515
|
+
const table = policy.table ?? DEFAULT_DEPRECATION_TABLE;
|
|
1516
|
+
const hit = table.find((d) => d.provider === input.provider && d.model === input.model);
|
|
1517
|
+
if (!hit) return { model: input.model, remapped: false };
|
|
1518
|
+
const log = policy.logger ?? ((msg) => console.warn(msg));
|
|
1519
|
+
const sunset = hit.sunsetOn ? ` (sunset ${hit.sunsetOn})` : "";
|
|
1520
|
+
const successor = hit.successor ? ` Suggested successor: ${hit.successor}.` : "";
|
|
1521
|
+
const base = `[agentskit] Model "${input.provider}:${input.model}" is deprecated${sunset}.${successor}`;
|
|
1522
|
+
switch (policy.onDeprecation) {
|
|
1523
|
+
case "fail":
|
|
1524
|
+
throw new ConfigError({
|
|
1525
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1526
|
+
message: base,
|
|
1527
|
+
hint: hit.successor ? `Set model to "${hit.successor}".` : "Pick a supported model from the provider."
|
|
1528
|
+
});
|
|
1529
|
+
case "remap": {
|
|
1530
|
+
if (!hit.successor) {
|
|
1531
|
+
throw new ConfigError({
|
|
1532
|
+
code: ErrorCodes.AK_CONFIG_INVALID,
|
|
1533
|
+
message: `${base} No successor available \u2014 cannot remap.`
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
log(`${base} Auto-remapping to "${hit.successor}".`);
|
|
1537
|
+
return { model: hit.successor, remapped: true, deprecation: hit };
|
|
1538
|
+
}
|
|
1539
|
+
case "warn":
|
|
1540
|
+
default:
|
|
1541
|
+
log(base);
|
|
1542
|
+
return { model: input.model, remapped: false, deprecation: hit };
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
function withDeprecationPolicy(factory, options) {
|
|
1546
|
+
resolveModel({ provider: options.provider, model: options.model }, options);
|
|
1547
|
+
return factory;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// src/credential-rotation.ts
|
|
1551
|
+
function createRotatingCredentials(initial, options) {
|
|
1552
|
+
let value = initial;
|
|
1553
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
1554
|
+
return {
|
|
1555
|
+
current: () => value,
|
|
1556
|
+
rotate: async (next) => {
|
|
1557
|
+
value = next;
|
|
1558
|
+
const event = {
|
|
1559
|
+
rotatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1560
|
+
id: options.id,
|
|
1561
|
+
fingerprint: next.slice(-4)
|
|
1562
|
+
};
|
|
1563
|
+
for (const h of handlers) h(event);
|
|
1564
|
+
return next;
|
|
1565
|
+
},
|
|
1566
|
+
onRotate: (handler) => {
|
|
1567
|
+
handlers.add(handler);
|
|
1568
|
+
return () => handlers.delete(handler);
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
async function refreshCredentials(adapter, next, options) {
|
|
1573
|
+
const log = options?.logger ?? ((msg) => console.log(msg));
|
|
1574
|
+
if (adapter && typeof adapter === "object" && "refreshCredentials" in adapter && typeof adapter.refreshCredentials === "function") {
|
|
1575
|
+
await adapter.refreshCredentials(next);
|
|
1576
|
+
log(`[agentskit] credentials refreshed${options?.id ? ` (${options.id})` : ""}`);
|
|
1577
|
+
return true;
|
|
1578
|
+
}
|
|
1579
|
+
log(`[agentskit] adapter does not support credential rotation; restart required${options?.id ? ` (${options.id})` : ""}`);
|
|
1580
|
+
return false;
|
|
1581
|
+
}
|
|
1861
1582
|
|
|
1862
|
-
export { anthropic, azureOpenAI, azureOpenAIAdapter, bail, bailAdapter, bedrock, bedrockAdapter, cerebras, cerebrasAdapter,
|
|
1583
|
+
export { DEFAULT_CARBON_TABLE, DEFAULT_DEPRECATION_TABLE, anthropic, applyCarbonTable, azureOpenAI, azureOpenAIAdapter, bail, bailAdapter, bedrock, bedrockAdapter, cerebras, cerebrasAdapter, cohere, cohereAdapter, createEnsembleAdapter, createFallbackAdapter, createOpenAICompatibleEmbedder, createRotatingCredentials, createRouter, deepseek, deepseekEmbedder, estimateCO2Grams, fireworks, gemini, geminiEmbedder, generic, grok, grokEmbedder, groq, groqAdapter, huggingface, inMemorySink, kimi, kimiEmbedder, langchain, langgraph, llamacpp, lmstudio, mistral, mockAdapter, ollama, ollamaEmbedder, openaiEmbedder, openrouter, qwen, recordingAdapter, refreshCredentials, replayAdapter, replicate, replicateAdapter, resolveModel, together, vercelAI, vertex, vertexAdapter, vllm, webllm, webllmAdapter, withDeprecationPolicy };
|
|
1863
1584
|
//# sourceMappingURL=index.js.map
|
|
1864
1585
|
//# sourceMappingURL=index.js.map
|