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