@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/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
- } else if (Array.isArray(delta?.tool_calls)) {
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
- yield {
122
- type: "tool_call",
123
- toolCall: {
124
- id: toolCall.id ?? `${toolCall.function.name}-${Date.now()}`,
125
- name: toolCall.function.name,
126
- args: toolCall.function.arguments ?? "{}"
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
- yield {
146
- type: "tool_call",
147
- toolCall: {
148
- id: event.content_block.id,
149
- name: event.content_block.name,
150
- args: JSON.stringify(event.content_block.input ?? {})
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 text = event.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("");
167
- if (text) yield { type: "text", content: text };
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
- function createStreamSource(doFetch, parse, errorLabel) {
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(abortController.signal);
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
- export { anthropic, createAdapter, deepseek, gemini, generic, grok, kimi, langchain, langgraph, ollama, openai, vercelAI };
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