@agentskit/adapters 0.4.0 → 0.5.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/README.md +65 -0
- package/dist/index.cjs +557 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +189 -2
- package/dist/index.d.ts +189 -2
- package/dist/index.js +544 -33
- package/dist/index.js.map +1 -1
- package/package.json +23 -5
package/dist/index.cjs
CHANGED
|
@@ -107,8 +107,13 @@ async function* readNDJSONLines(stream) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
async function* parseOpenAIStream(stream) {
|
|
110
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
110
111
|
for await (const data of readSSELines(stream)) {
|
|
111
112
|
if (data === "[DONE]") {
|
|
113
|
+
for (const [, tc] of pendingToolCalls) {
|
|
114
|
+
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
115
|
+
}
|
|
116
|
+
pendingToolCalls.clear();
|
|
112
117
|
yield { type: "done" };
|
|
113
118
|
return;
|
|
114
119
|
}
|
|
@@ -117,56 +122,104 @@ async function* parseOpenAIStream(stream) {
|
|
|
117
122
|
const delta = event.choices?.[0]?.delta;
|
|
118
123
|
if (typeof delta?.content === "string") {
|
|
119
124
|
yield { type: "text", content: delta.content };
|
|
120
|
-
}
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(delta?.tool_calls)) {
|
|
121
127
|
for (const toolCall of delta.tool_calls) {
|
|
128
|
+
const index = toolCall.index ?? 0;
|
|
129
|
+
const existing = pendingToolCalls.get(index);
|
|
122
130
|
if (toolCall?.function?.name) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
};
|
|
131
|
+
pendingToolCalls.set(index, {
|
|
132
|
+
id: toolCall.id ?? existing?.id ?? `tool-${index}-${Date.now()}`,
|
|
133
|
+
name: toolCall.function.name,
|
|
134
|
+
args: (existing?.args ?? "") + (toolCall.function.arguments ?? "")
|
|
135
|
+
});
|
|
136
|
+
} else if (existing && toolCall?.function?.arguments) {
|
|
137
|
+
existing.args += toolCall.function.arguments;
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
} catch {
|
|
135
142
|
}
|
|
136
143
|
}
|
|
144
|
+
for (const [, tc] of pendingToolCalls) {
|
|
145
|
+
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
146
|
+
}
|
|
147
|
+
pendingToolCalls.clear();
|
|
137
148
|
yield { type: "done" };
|
|
138
149
|
}
|
|
139
150
|
async function* parseAnthropicStream(stream) {
|
|
151
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
140
152
|
for await (const data of readSSELines(stream)) {
|
|
141
153
|
if (data === "[DONE]") continue;
|
|
142
154
|
try {
|
|
143
155
|
const event = JSON.parse(data);
|
|
144
|
-
if (event.type === "content_block_delta" && event.delta?.text) {
|
|
156
|
+
if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta?.text) {
|
|
145
157
|
yield { type: "text", content: event.delta.text };
|
|
158
|
+
} else if (event.type === "content_block_delta" && event.delta?.type === "input_json_delta") {
|
|
159
|
+
const index = event.index ?? 0;
|
|
160
|
+
const existing = pendingToolCalls.get(index);
|
|
161
|
+
if (existing) {
|
|
162
|
+
existing.args += event.delta.partial_json ?? "";
|
|
163
|
+
}
|
|
146
164
|
} else if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
165
|
+
const index = event.index ?? 0;
|
|
166
|
+
pendingToolCalls.set(index, {
|
|
167
|
+
id: event.content_block.id,
|
|
168
|
+
name: event.content_block.name,
|
|
169
|
+
args: ""
|
|
170
|
+
});
|
|
171
|
+
} else if (event.type === "content_block_stop") {
|
|
172
|
+
const index = event.index ?? 0;
|
|
173
|
+
const tc = pendingToolCalls.get(index);
|
|
174
|
+
if (tc) {
|
|
175
|
+
yield {
|
|
176
|
+
type: "tool_call",
|
|
177
|
+
toolCall: {
|
|
178
|
+
id: tc.id,
|
|
179
|
+
name: tc.name,
|
|
180
|
+
args: tc.args || "{}"
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
pendingToolCalls.delete(index);
|
|
184
|
+
}
|
|
155
185
|
} else if (event.type === "message_stop") {
|
|
186
|
+
for (const [, tc] of pendingToolCalls) {
|
|
187
|
+
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
188
|
+
}
|
|
189
|
+
pendingToolCalls.clear();
|
|
156
190
|
yield { type: "done" };
|
|
157
191
|
return;
|
|
158
192
|
}
|
|
159
193
|
} catch {
|
|
160
194
|
}
|
|
161
195
|
}
|
|
196
|
+
for (const [, tc] of pendingToolCalls) {
|
|
197
|
+
yield { type: "tool_call", toolCall: { id: tc.id, name: tc.name, args: tc.args || "{}" } };
|
|
198
|
+
}
|
|
199
|
+
pendingToolCalls.clear();
|
|
162
200
|
yield { type: "done" };
|
|
163
201
|
}
|
|
164
202
|
async function* parseGeminiStream(stream) {
|
|
165
203
|
for await (const data of readSSELines(stream)) {
|
|
166
204
|
try {
|
|
167
205
|
const event = JSON.parse(data);
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
206
|
+
const parts = event.candidates?.[0]?.content?.parts;
|
|
207
|
+
if (!Array.isArray(parts)) continue;
|
|
208
|
+
for (const part of parts) {
|
|
209
|
+
if (typeof part.text === "string" && part.text) {
|
|
210
|
+
yield { type: "text", content: part.text };
|
|
211
|
+
} else if (part.functionCall) {
|
|
212
|
+
const fc = part.functionCall;
|
|
213
|
+
yield {
|
|
214
|
+
type: "tool_call",
|
|
215
|
+
toolCall: {
|
|
216
|
+
id: fc.id ?? `${fc.name}-${Date.now()}`,
|
|
217
|
+
name: fc.name,
|
|
218
|
+
args: JSON.stringify(fc.args ?? {})
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
170
223
|
} catch {
|
|
171
224
|
}
|
|
172
225
|
}
|
|
@@ -179,6 +232,20 @@ async function* parseOllamaStream(stream) {
|
|
|
179
232
|
if (event.message?.content) {
|
|
180
233
|
yield { type: "text", content: event.message.content };
|
|
181
234
|
}
|
|
235
|
+
if (Array.isArray(event.message?.tool_calls)) {
|
|
236
|
+
for (const tc of event.message.tool_calls) {
|
|
237
|
+
if (tc?.function?.name) {
|
|
238
|
+
yield {
|
|
239
|
+
type: "tool_call",
|
|
240
|
+
toolCall: {
|
|
241
|
+
id: tc.id ?? `${tc.function.name}-${Date.now()}`,
|
|
242
|
+
name: tc.function.name,
|
|
243
|
+
args: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments ?? {})
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
182
249
|
if (event.done) {
|
|
183
250
|
yield { type: "done" };
|
|
184
251
|
return;
|
|
@@ -188,12 +255,73 @@ async function* parseOllamaStream(stream) {
|
|
|
188
255
|
}
|
|
189
256
|
yield { type: "done" };
|
|
190
257
|
}
|
|
191
|
-
|
|
258
|
+
var DEFAULT_RETRY = {
|
|
259
|
+
maxAttempts: 3,
|
|
260
|
+
baseDelayMs: 500,
|
|
261
|
+
maxDelayMs: 8e3,
|
|
262
|
+
jitter: true,
|
|
263
|
+
retryOn: ({ error, response }) => {
|
|
264
|
+
if (response) {
|
|
265
|
+
return [408, 429, 500, 502, 503, 504].includes(response.status);
|
|
266
|
+
}
|
|
267
|
+
if (error instanceof DOMException && error.name === "AbortError") return false;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
function defaultSleep(ms) {
|
|
272
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
273
|
+
}
|
|
274
|
+
function computeDelay(attempt, opts) {
|
|
275
|
+
const exp = Math.min(opts.maxDelayMs, opts.baseDelayMs * Math.pow(2, attempt - 1));
|
|
276
|
+
if (!opts.jitter) return exp;
|
|
277
|
+
return Math.floor(Math.random() * exp);
|
|
278
|
+
}
|
|
279
|
+
function parseRetryAfter(value) {
|
|
280
|
+
if (!value) return void 0;
|
|
281
|
+
const n = Number(value);
|
|
282
|
+
if (!Number.isNaN(n)) return n * 1e3;
|
|
283
|
+
const date = Date.parse(value);
|
|
284
|
+
if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
async function fetchWithRetry(doFetch, signal, retryOpt = {}) {
|
|
288
|
+
const opts = {
|
|
289
|
+
...DEFAULT_RETRY,
|
|
290
|
+
...retryOpt
|
|
291
|
+
};
|
|
292
|
+
const sleep = retryOpt.sleep ?? defaultSleep;
|
|
293
|
+
let lastError;
|
|
294
|
+
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
295
|
+
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
296
|
+
try {
|
|
297
|
+
const response = await doFetch(signal);
|
|
298
|
+
if (response.ok) return response;
|
|
299
|
+
if (attempt >= opts.maxAttempts || !opts.retryOn({ response, attempt })) {
|
|
300
|
+
return response;
|
|
301
|
+
}
|
|
302
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
|
|
303
|
+
const delay = retryAfterMs ?? computeDelay(attempt, opts);
|
|
304
|
+
retryOpt.onRetry?.({ attempt, delayMs: delay, reason: `HTTP ${response.status}` });
|
|
305
|
+
await sleep(delay);
|
|
306
|
+
response.body?.cancel().catch(() => {
|
|
307
|
+
});
|
|
308
|
+
} catch (err) {
|
|
309
|
+
lastError = err;
|
|
310
|
+
if (err instanceof DOMException && err.name === "AbortError") throw err;
|
|
311
|
+
if (attempt >= opts.maxAttempts || !opts.retryOn({ error: err, attempt })) throw err;
|
|
312
|
+
const delay = computeDelay(attempt, opts);
|
|
313
|
+
retryOpt.onRetry?.({ attempt, delayMs: delay, reason: err.message });
|
|
314
|
+
await sleep(delay);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
throw lastError ?? new Error("fetchWithRetry: exhausted attempts");
|
|
318
|
+
}
|
|
319
|
+
function createStreamSource(doFetch, parse, errorLabel, retry) {
|
|
192
320
|
let abortController = new AbortController();
|
|
193
321
|
return {
|
|
194
322
|
stream: async function* () {
|
|
195
323
|
try {
|
|
196
|
-
const response = await doFetch
|
|
324
|
+
const response = await fetchWithRetry(doFetch, abortController.signal, retry ?? {});
|
|
197
325
|
if (!response.ok || !response.body) {
|
|
198
326
|
yield { type: "error", content: `${errorLabel} error: ${response.status}` };
|
|
199
327
|
return;
|
|
@@ -213,11 +341,78 @@ function createStreamSource(doFetch, parse, errorLabel) {
|
|
|
213
341
|
}
|
|
214
342
|
};
|
|
215
343
|
}
|
|
344
|
+
function chunkText(text, targetSize = 32) {
|
|
345
|
+
if (text.length <= targetSize) return [text];
|
|
346
|
+
const out = [];
|
|
347
|
+
let i = 0;
|
|
348
|
+
while (i < text.length) {
|
|
349
|
+
let end = Math.min(text.length, i + targetSize);
|
|
350
|
+
if (end < text.length) {
|
|
351
|
+
const nextSpace = text.indexOf(" ", end);
|
|
352
|
+
if (nextSpace !== -1 && nextSpace - end <= 8) end = nextSpace + 1;
|
|
353
|
+
}
|
|
354
|
+
out.push(text.slice(i, end));
|
|
355
|
+
i = end;
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
function simulateStream(doFetch, extractText, errorLabel, options = {}) {
|
|
360
|
+
const { chunkSize = 32, delayMs = 8, retry } = options;
|
|
361
|
+
let abortController = new AbortController();
|
|
362
|
+
return {
|
|
363
|
+
stream: async function* () {
|
|
364
|
+
try {
|
|
365
|
+
const response = await fetchWithRetry(doFetch, abortController.signal, retry ?? {});
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
yield { type: "error", content: `${errorLabel} error: ${response.status}` };
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const text = await extractText(response);
|
|
371
|
+
const chunks = chunkText(text, chunkSize);
|
|
372
|
+
for (const chunk of chunks) {
|
|
373
|
+
if (abortController === null) return;
|
|
374
|
+
if (delayMs > 0) {
|
|
375
|
+
await new Promise((resolve2, reject) => {
|
|
376
|
+
const timer = setTimeout(resolve2, delayMs);
|
|
377
|
+
abortController?.signal.addEventListener(
|
|
378
|
+
"abort",
|
|
379
|
+
() => {
|
|
380
|
+
clearTimeout(timer);
|
|
381
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
382
|
+
},
|
|
383
|
+
{ once: true }
|
|
384
|
+
);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
yield { type: "text", content: chunk };
|
|
388
|
+
}
|
|
389
|
+
yield { type: "done" };
|
|
390
|
+
} catch (err) {
|
|
391
|
+
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
392
|
+
yield {
|
|
393
|
+
type: "error",
|
|
394
|
+
content: err instanceof Error ? err.message : String(err)
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
abort: () => {
|
|
399
|
+
abortController?.abort();
|
|
400
|
+
abortController = null;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
216
404
|
|
|
217
405
|
// src/anthropic.ts
|
|
218
406
|
function anthropic(config) {
|
|
219
|
-
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096 } = config;
|
|
407
|
+
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096, retry } = config;
|
|
220
408
|
return {
|
|
409
|
+
capabilities: {
|
|
410
|
+
streaming: true,
|
|
411
|
+
tools: true,
|
|
412
|
+
reasoning: model.includes("sonnet") || model.includes("opus"),
|
|
413
|
+
multiModal: true,
|
|
414
|
+
usage: true
|
|
415
|
+
},
|
|
221
416
|
createSource: (request) => {
|
|
222
417
|
const body = {
|
|
223
418
|
model,
|
|
@@ -243,7 +438,8 @@ function anthropic(config) {
|
|
|
243
438
|
signal
|
|
244
439
|
}),
|
|
245
440
|
parseAnthropicStream,
|
|
246
|
-
"Anthropic API"
|
|
441
|
+
"Anthropic API",
|
|
442
|
+
retry
|
|
247
443
|
);
|
|
248
444
|
}
|
|
249
445
|
};
|
|
@@ -251,8 +447,17 @@ function anthropic(config) {
|
|
|
251
447
|
|
|
252
448
|
// src/openai.ts
|
|
253
449
|
function openai(config) {
|
|
254
|
-
const { apiKey, model, baseUrl = "https://api.openai.com" } = config;
|
|
450
|
+
const { apiKey, model, baseUrl = "https://api.openai.com", retry } = config;
|
|
255
451
|
return {
|
|
452
|
+
capabilities: {
|
|
453
|
+
streaming: true,
|
|
454
|
+
tools: true,
|
|
455
|
+
// o1 / o3 models emit reasoning; older models don't. Accurate per-model
|
|
456
|
+
// detection would need a model registry; 'true' is the safer default here.
|
|
457
|
+
reasoning: model.startsWith("o1") || model.startsWith("o3"),
|
|
458
|
+
multiModal: model.startsWith("gpt-4") || model.startsWith("o"),
|
|
459
|
+
usage: true
|
|
460
|
+
},
|
|
256
461
|
createSource: (request) => {
|
|
257
462
|
const body = {
|
|
258
463
|
model,
|
|
@@ -280,7 +485,8 @@ function openai(config) {
|
|
|
280
485
|
signal
|
|
281
486
|
}),
|
|
282
487
|
parseOpenAIStream,
|
|
283
|
-
"OpenAI API"
|
|
488
|
+
"OpenAI API",
|
|
489
|
+
retry
|
|
284
490
|
);
|
|
285
491
|
}
|
|
286
492
|
};
|
|
@@ -288,8 +494,14 @@ function openai(config) {
|
|
|
288
494
|
|
|
289
495
|
// src/gemini.ts
|
|
290
496
|
function gemini(config) {
|
|
291
|
-
const { apiKey, model, baseUrl = "https://generativelanguage.googleapis.com" } = config;
|
|
497
|
+
const { apiKey, model, baseUrl = "https://generativelanguage.googleapis.com", retry } = config;
|
|
292
498
|
return {
|
|
499
|
+
capabilities: {
|
|
500
|
+
streaming: true,
|
|
501
|
+
tools: true,
|
|
502
|
+
multiModal: true,
|
|
503
|
+
usage: true
|
|
504
|
+
},
|
|
293
505
|
createSource: (request) => {
|
|
294
506
|
const body = {
|
|
295
507
|
contents: request.messages.filter((message) => message.role !== "system").map((message) => ({
|
|
@@ -312,7 +524,8 @@ function gemini(config) {
|
|
|
312
524
|
}
|
|
313
525
|
),
|
|
314
526
|
parseGeminiStream,
|
|
315
|
-
"Gemini API"
|
|
527
|
+
"Gemini API",
|
|
528
|
+
retry
|
|
316
529
|
);
|
|
317
530
|
}
|
|
318
531
|
};
|
|
@@ -333,8 +546,14 @@ var grok = createOpenAICompatibleAdapter("https://api.x.ai");
|
|
|
333
546
|
|
|
334
547
|
// src/ollama.ts
|
|
335
548
|
function ollama(config) {
|
|
336
|
-
const { model, baseUrl = "http://localhost:11434" } = config;
|
|
549
|
+
const { model, baseUrl = "http://localhost:11434", retry } = config;
|
|
337
550
|
return {
|
|
551
|
+
capabilities: {
|
|
552
|
+
streaming: true,
|
|
553
|
+
tools: false,
|
|
554
|
+
// varies by model; default 'false' — enable via capabilities.extensions if your model supports it
|
|
555
|
+
multiModal: model.includes("llava") || model.includes("vision")
|
|
556
|
+
},
|
|
338
557
|
createSource: (request) => {
|
|
339
558
|
const body = {
|
|
340
559
|
model,
|
|
@@ -352,7 +571,8 @@ function ollama(config) {
|
|
|
352
571
|
signal
|
|
353
572
|
}),
|
|
354
573
|
parseOllamaStream,
|
|
355
|
-
"Ollama API"
|
|
574
|
+
"Ollama API",
|
|
575
|
+
retry
|
|
356
576
|
);
|
|
357
577
|
}
|
|
358
578
|
};
|
|
@@ -449,8 +669,12 @@ async function* parseVercelStream(stream) {
|
|
|
449
669
|
yield { type: "done" };
|
|
450
670
|
}
|
|
451
671
|
function vercelAI(config) {
|
|
452
|
-
const { api, headers = {} } = config;
|
|
672
|
+
const { api, headers = {}, retry } = config;
|
|
453
673
|
return {
|
|
674
|
+
capabilities: {
|
|
675
|
+
// Vercel AI routes can do any of these — whether they do depends on
|
|
676
|
+
// your route handler. Omit the field instead of lying.
|
|
677
|
+
},
|
|
454
678
|
createSource: (request) => {
|
|
455
679
|
const body = {
|
|
456
680
|
messages: request.messages.map((message) => ({ role: message.role, content: message.content })),
|
|
@@ -465,23 +689,324 @@ function vercelAI(config) {
|
|
|
465
689
|
signal
|
|
466
690
|
}),
|
|
467
691
|
parseVercelStream,
|
|
468
|
-
"API"
|
|
692
|
+
"API",
|
|
693
|
+
retry
|
|
469
694
|
);
|
|
470
695
|
}
|
|
471
696
|
};
|
|
472
697
|
}
|
|
473
698
|
|
|
699
|
+
// src/mock.ts
|
|
700
|
+
function mockAdapter(options) {
|
|
701
|
+
const { response, delayMs = 0, history } = options;
|
|
702
|
+
let callIndex = 0;
|
|
703
|
+
return {
|
|
704
|
+
capabilities: {
|
|
705
|
+
streaming: true,
|
|
706
|
+
tools: true,
|
|
707
|
+
reasoning: true,
|
|
708
|
+
multiModal: true,
|
|
709
|
+
usage: true
|
|
710
|
+
},
|
|
711
|
+
createSource: (request) => {
|
|
712
|
+
history?.push(request);
|
|
713
|
+
const myCall = callIndex++;
|
|
714
|
+
let cancelled = false;
|
|
715
|
+
return {
|
|
716
|
+
stream: async function* () {
|
|
717
|
+
const chunks = resolve(response, myCall, request);
|
|
718
|
+
let endedExplicitly = false;
|
|
719
|
+
for (const chunk of chunks) {
|
|
720
|
+
if (cancelled) return;
|
|
721
|
+
if (delayMs > 0) await new Promise((r) => setTimeout(r, delayMs));
|
|
722
|
+
yield chunk;
|
|
723
|
+
if (chunk.type === "done" || chunk.type === "error") {
|
|
724
|
+
endedExplicitly = true;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (!endedExplicitly) yield { type: "done" };
|
|
728
|
+
},
|
|
729
|
+
abort: () => {
|
|
730
|
+
cancelled = true;
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function resolve(response, callIndex, request) {
|
|
737
|
+
if (Array.isArray(response) && response.length > 0 && Array.isArray(response[0])) {
|
|
738
|
+
const sequence = response;
|
|
739
|
+
const item = sequence[callIndex % sequence.length];
|
|
740
|
+
return item;
|
|
741
|
+
}
|
|
742
|
+
if (Array.isArray(response) && response.length > 0 && typeof response[0] === "function") {
|
|
743
|
+
const sequence = response;
|
|
744
|
+
return sequence[callIndex % sequence.length](request);
|
|
745
|
+
}
|
|
746
|
+
if (typeof response === "function") {
|
|
747
|
+
return response(request);
|
|
748
|
+
}
|
|
749
|
+
return response;
|
|
750
|
+
}
|
|
751
|
+
function recordingAdapter(inner, sink) {
|
|
752
|
+
return {
|
|
753
|
+
createSource: (request) => {
|
|
754
|
+
const innerSource = inner.createSource(request);
|
|
755
|
+
const captured = [];
|
|
756
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
757
|
+
return {
|
|
758
|
+
stream: async function* () {
|
|
759
|
+
try {
|
|
760
|
+
for await (const chunk of innerSource.stream()) {
|
|
761
|
+
captured.push(chunk);
|
|
762
|
+
yield chunk;
|
|
763
|
+
}
|
|
764
|
+
} finally {
|
|
765
|
+
await sink.push({ recordedAt, request, chunks: captured });
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
abort: () => innerSource.abort()
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function inMemorySink() {
|
|
774
|
+
const fixture = [];
|
|
775
|
+
return {
|
|
776
|
+
fixture,
|
|
777
|
+
push(turn) {
|
|
778
|
+
fixture.push(turn);
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
function replayAdapter(fixture) {
|
|
783
|
+
if (fixture.length === 0) {
|
|
784
|
+
throw new Error("replayAdapter: fixture is empty");
|
|
785
|
+
}
|
|
786
|
+
return mockAdapter({
|
|
787
|
+
response: fixture.map((turn) => turn.chunks)
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/embedders/openai.ts
|
|
792
|
+
async function fetchAvailableModels(baseUrl, apiKey) {
|
|
793
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
794
|
+
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
795
|
+
});
|
|
796
|
+
if (!response.ok) {
|
|
797
|
+
throw new Error(`HTTP ${response.status}`);
|
|
798
|
+
}
|
|
799
|
+
const data = await response.json();
|
|
800
|
+
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
801
|
+
}
|
|
802
|
+
async function buildModelError(baseUrl, apiKey, originalError) {
|
|
803
|
+
try {
|
|
804
|
+
const models = await fetchAvailableModels(baseUrl, apiKey);
|
|
805
|
+
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
806
|
+
return new Error(
|
|
807
|
+
`OpenAI embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
808
|
+
);
|
|
809
|
+
} catch (fetchError) {
|
|
810
|
+
return new Error(
|
|
811
|
+
`OpenAI embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function openaiEmbedder(config) {
|
|
816
|
+
const { apiKey, model = "text-embedding-3-small", baseUrl = "https://api.openai.com" } = config;
|
|
817
|
+
return async (text) => {
|
|
818
|
+
const response = await fetch(`${baseUrl}/v1/embeddings`, {
|
|
819
|
+
method: "POST",
|
|
820
|
+
headers: {
|
|
821
|
+
"Content-Type": "application/json",
|
|
822
|
+
"Authorization": `Bearer ${apiKey}`
|
|
823
|
+
},
|
|
824
|
+
body: JSON.stringify({ model, input: text })
|
|
825
|
+
});
|
|
826
|
+
if (!response.ok) {
|
|
827
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
828
|
+
const message = errorBody.error?.message ?? `HTTP ${response.status}`;
|
|
829
|
+
throw await buildModelError(baseUrl, apiKey, message);
|
|
830
|
+
}
|
|
831
|
+
const data = await response.json();
|
|
832
|
+
return data.data[0].embedding;
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// src/embedders/gemini.ts
|
|
837
|
+
async function fetchAvailableModels2(baseUrl, apiKey) {
|
|
838
|
+
const response = await fetch(`${baseUrl}/v1beta/models?key=${apiKey}`);
|
|
839
|
+
if (!response.ok) {
|
|
840
|
+
throw new Error(`HTTP ${response.status}`);
|
|
841
|
+
}
|
|
842
|
+
const data = await response.json();
|
|
843
|
+
return data.models.filter((m) => m.supportedGenerationMethods.includes("embedContent")).map((m) => m.name.replace("models/", "")).sort();
|
|
844
|
+
}
|
|
845
|
+
async function buildModelError2(baseUrl, apiKey, originalError) {
|
|
846
|
+
try {
|
|
847
|
+
const models = await fetchAvailableModels2(baseUrl, apiKey);
|
|
848
|
+
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
849
|
+
return new Error(
|
|
850
|
+
`Gemini embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
851
|
+
);
|
|
852
|
+
} catch (fetchError) {
|
|
853
|
+
return new Error(
|
|
854
|
+
`Gemini embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function geminiEmbedder(config) {
|
|
859
|
+
const {
|
|
860
|
+
apiKey,
|
|
861
|
+
model = "text-embedding-004",
|
|
862
|
+
baseUrl = "https://generativelanguage.googleapis.com"
|
|
863
|
+
} = config;
|
|
864
|
+
return async (text) => {
|
|
865
|
+
const response = await fetch(
|
|
866
|
+
`${baseUrl}/v1beta/models/${model}:embedContent?key=${apiKey}`,
|
|
867
|
+
{
|
|
868
|
+
method: "POST",
|
|
869
|
+
headers: { "Content-Type": "application/json" },
|
|
870
|
+
body: JSON.stringify({
|
|
871
|
+
model: `models/${model}`,
|
|
872
|
+
content: { parts: [{ text }] }
|
|
873
|
+
})
|
|
874
|
+
}
|
|
875
|
+
);
|
|
876
|
+
if (!response.ok) {
|
|
877
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
878
|
+
const message = errorBody.error?.message ?? `HTTP ${response.status}`;
|
|
879
|
+
throw await buildModelError2(baseUrl, apiKey, message);
|
|
880
|
+
}
|
|
881
|
+
const data = await response.json();
|
|
882
|
+
return data.embedding.values;
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/embedders/ollama.ts
|
|
887
|
+
async function fetchAvailableModels3(baseUrl) {
|
|
888
|
+
const response = await fetch(`${baseUrl}/api/tags`);
|
|
889
|
+
if (!response.ok) {
|
|
890
|
+
throw new Error(`HTTP ${response.status}`);
|
|
891
|
+
}
|
|
892
|
+
const data = await response.json();
|
|
893
|
+
return data.models.map((m) => m.name).filter((name) => name.includes("embed")).sort();
|
|
894
|
+
}
|
|
895
|
+
async function buildModelError3(baseUrl, originalError) {
|
|
896
|
+
try {
|
|
897
|
+
const models = await fetchAvailableModels3(baseUrl);
|
|
898
|
+
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
899
|
+
return new Error(
|
|
900
|
+
`Ollama embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
901
|
+
);
|
|
902
|
+
} catch (fetchError) {
|
|
903
|
+
return new Error(
|
|
904
|
+
`Ollama embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function ollamaEmbedder(config) {
|
|
909
|
+
const { model = "nomic-embed-text", baseUrl = "http://localhost:11434" } = config;
|
|
910
|
+
return async (text) => {
|
|
911
|
+
const response = await fetch(`${baseUrl}/api/embed`, {
|
|
912
|
+
method: "POST",
|
|
913
|
+
headers: { "Content-Type": "application/json" },
|
|
914
|
+
body: JSON.stringify({ model, input: text })
|
|
915
|
+
});
|
|
916
|
+
if (!response.ok) {
|
|
917
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
918
|
+
const message = errorBody.error ?? `HTTP ${response.status}`;
|
|
919
|
+
throw await buildModelError3(baseUrl, message);
|
|
920
|
+
}
|
|
921
|
+
const data = await response.json();
|
|
922
|
+
return data.embeddings[0];
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/embedders/openai-compatible.ts
|
|
927
|
+
async function fetchAvailableModels4(baseUrl, apiKey) {
|
|
928
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
929
|
+
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
930
|
+
});
|
|
931
|
+
if (!response.ok) {
|
|
932
|
+
throw new Error(`HTTP ${response.status}`);
|
|
933
|
+
}
|
|
934
|
+
const data = await response.json();
|
|
935
|
+
return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
|
|
936
|
+
}
|
|
937
|
+
async function buildModelError4(provider, baseUrl, apiKey, originalError) {
|
|
938
|
+
try {
|
|
939
|
+
const models = await fetchAvailableModels4(baseUrl, apiKey);
|
|
940
|
+
const list = models.length > 0 ? models.join(", ") : "none found";
|
|
941
|
+
return new Error(
|
|
942
|
+
`${provider} embedding failed: ${originalError}. Available embedding models: ${list}`
|
|
943
|
+
);
|
|
944
|
+
} catch (fetchError) {
|
|
945
|
+
return new Error(
|
|
946
|
+
`${provider} embedding failed: ${originalError}. Could not fetch available models: ${fetchError.message}`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
function createOpenAICompatibleEmbedder(provider, defaultBaseUrl) {
|
|
951
|
+
return function embedder(config) {
|
|
952
|
+
if (!config.model) {
|
|
953
|
+
throw new Error(`Model is required for ${provider}. Pass { model: "<model-name>" }.`);
|
|
954
|
+
}
|
|
955
|
+
const { apiKey, model, baseUrl = defaultBaseUrl } = config;
|
|
956
|
+
return async (text) => {
|
|
957
|
+
const response = await fetch(`${baseUrl}/v1/embeddings`, {
|
|
958
|
+
method: "POST",
|
|
959
|
+
headers: {
|
|
960
|
+
"Content-Type": "application/json",
|
|
961
|
+
"Authorization": `Bearer ${apiKey}`
|
|
962
|
+
},
|
|
963
|
+
body: JSON.stringify({ model, input: text })
|
|
964
|
+
});
|
|
965
|
+
if (!response.ok) {
|
|
966
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
967
|
+
const message = errorBody.error?.message ?? `HTTP ${response.status}`;
|
|
968
|
+
throw await buildModelError4(provider, baseUrl, apiKey, message);
|
|
969
|
+
}
|
|
970
|
+
const data = await response.json();
|
|
971
|
+
return data.data[0].embedding;
|
|
972
|
+
};
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// src/embedders/deepseek.ts
|
|
977
|
+
var deepseekEmbedder = createOpenAICompatibleEmbedder("DeepSeek", "https://api.deepseek.com");
|
|
978
|
+
|
|
979
|
+
// src/embedders/grok.ts
|
|
980
|
+
var grokEmbedder = createOpenAICompatibleEmbedder("Grok", "https://api.x.ai");
|
|
981
|
+
|
|
982
|
+
// src/embedders/kimi.ts
|
|
983
|
+
var kimiEmbedder = createOpenAICompatibleEmbedder("Kimi", "https://api.moonshot.ai");
|
|
984
|
+
|
|
474
985
|
exports.anthropic = anthropic;
|
|
986
|
+
exports.chunkText = chunkText;
|
|
475
987
|
exports.createAdapter = createAdapter;
|
|
988
|
+
exports.createOpenAICompatibleEmbedder = createOpenAICompatibleEmbedder;
|
|
476
989
|
exports.deepseek = deepseek;
|
|
990
|
+
exports.deepseekEmbedder = deepseekEmbedder;
|
|
991
|
+
exports.fetchWithRetry = fetchWithRetry;
|
|
477
992
|
exports.gemini = gemini;
|
|
993
|
+
exports.geminiEmbedder = geminiEmbedder;
|
|
478
994
|
exports.generic = generic;
|
|
479
995
|
exports.grok = grok;
|
|
996
|
+
exports.grokEmbedder = grokEmbedder;
|
|
997
|
+
exports.inMemorySink = inMemorySink;
|
|
480
998
|
exports.kimi = kimi;
|
|
999
|
+
exports.kimiEmbedder = kimiEmbedder;
|
|
481
1000
|
exports.langchain = langchain;
|
|
482
1001
|
exports.langgraph = langgraph;
|
|
1002
|
+
exports.mockAdapter = mockAdapter;
|
|
483
1003
|
exports.ollama = ollama;
|
|
1004
|
+
exports.ollamaEmbedder = ollamaEmbedder;
|
|
484
1005
|
exports.openai = openai;
|
|
1006
|
+
exports.openaiEmbedder = openaiEmbedder;
|
|
1007
|
+
exports.recordingAdapter = recordingAdapter;
|
|
1008
|
+
exports.replayAdapter = replayAdapter;
|
|
1009
|
+
exports.simulateStream = simulateStream;
|
|
485
1010
|
exports.vercelAI = vercelAI;
|
|
486
1011
|
//# sourceMappingURL=index.cjs.map
|
|
487
1012
|
//# sourceMappingURL=index.cjs.map
|