@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.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
- } else if (Array.isArray(delta?.tool_calls)) {
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
- yield {
124
- type: "tool_call",
125
- toolCall: {
126
- id: toolCall.id ?? `${toolCall.function.name}-${Date.now()}`,
127
- name: toolCall.function.name,
128
- args: toolCall.function.arguments ?? "{}"
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
- yield {
148
- type: "tool_call",
149
- toolCall: {
150
- id: event.content_block.id,
151
- name: event.content_block.name,
152
- args: JSON.stringify(event.content_block.input ?? {})
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 text = event.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("");
169
- if (text) yield { type: "text", content: text };
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
- function createStreamSource(doFetch, parse, errorLabel) {
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(abortController.signal);
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