@agentskit/adapters 0.7.1 → 0.9.1
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 +23 -5
- package/dist/index.cjs +281 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +199 -2
- package/dist/index.d.ts +199 -2
- package/dist/index.js +269 -1
- package/dist/index.js.map +1 -1
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Connect to any LLM provider — and swap between them — without touching your
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@agentskit/adapters)
|
|
6
6
|
[](https://www.npmjs.com/package/@agentskit/adapters)
|
|
7
|
-
[](https://bundlejs.com/?q=@agentskit/adapters)
|
|
8
|
+
[](../../LICENSE)
|
|
9
9
|
[](../../docs/STABILITY.md)
|
|
10
10
|
[](https://github.com/AgentsKit-io/agentskit)
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@ Connect to any LLM provider — and swap between them — without touching your
|
|
|
14
14
|
## Why adapters
|
|
15
15
|
|
|
16
16
|
- **Vendor independence** — switch from OpenAI to Anthropic to a local Ollama model by changing one line; your hooks, runtime, and tools stay untouched
|
|
17
|
-
- **
|
|
17
|
+
- **20+ providers included** — Anthropic, OpenAI, Gemini, Ollama, DeepSeek, Grok, Kimi, Mistral, Cohere, Together, Groq, Fireworks, OpenRouter, Hugging Face, LM Studio, vLLM, llama.cpp, LangChain, Vercel AI SDK, and any raw `ReadableStream`
|
|
18
18
|
- **Embedder functions built in** — the same adapter pattern covers text embeddings, so you can reuse provider config for both chat and RAG
|
|
19
19
|
- **One-line local AI** — `ollama({ model: 'llama3.1' })` for fully offline agents with no API key required
|
|
20
20
|
|
|
@@ -57,10 +57,28 @@ const rag = createRAG({
|
|
|
57
57
|
|
|
58
58
|
## Features
|
|
59
59
|
|
|
60
|
-
- Providers: Anthropic, OpenAI, Gemini, Ollama, DeepSeek, Grok, Kimi, LangChain, LangGraph, Vercel AI SDK, generic `ReadableStream`
|
|
61
|
-
- Embedders: `openaiEmbedder`, `geminiEmbedder`, `ollamaEmbedder`
|
|
60
|
+
- Providers: Anthropic, OpenAI, Gemini, Ollama, DeepSeek, Grok, Kimi, Mistral, Cohere, Together, Groq, Fireworks, OpenRouter, Hugging Face, LM Studio, vLLM, llama.cpp, LangChain, LangGraph, Vercel AI SDK, generic `ReadableStream`
|
|
61
|
+
- Embedders: `openaiEmbedder`, `geminiEmbedder`, `ollamaEmbedder`, `deepseekEmbedder`, `grokEmbedder`, `kimiEmbedder`, `createOpenAICompatibleEmbedder`
|
|
62
62
|
- All adapters satisfy `Adapter` contract v1 (ADR 0001) — substitutable anywhere in the ecosystem
|
|
63
63
|
- Custom adapter authoring via `createAdapter()`
|
|
64
|
+
- Higher-order adapters: `createRouter` (cost/latency/classifier), `createEnsembleAdapter` (fan-out + merge), `createFallbackAdapter` (ordered try-next)
|
|
65
|
+
|
|
66
|
+
## Higher-order adapters
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createRouter, anthropic, openai } from '@agentskit/adapters'
|
|
70
|
+
|
|
71
|
+
// Auto-pick cheapest capable candidate per request.
|
|
72
|
+
const router = createRouter({
|
|
73
|
+
candidates: [
|
|
74
|
+
{ id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25 },
|
|
75
|
+
{ id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3 },
|
|
76
|
+
{ id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }), cost: 0.15 },
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
See [Adapter router](https://www.agentskit.io/docs/recipes/adapter-router), [Ensemble](https://www.agentskit.io/docs/recipes/adapter-ensemble), and [Fallback chain](https://www.agentskit.io/docs/recipes/fallback-chain).
|
|
64
82
|
|
|
65
83
|
## Ecosystem
|
|
66
84
|
|
package/dist/index.cjs
CHANGED
|
@@ -777,6 +777,274 @@ function vercelAI(config) {
|
|
|
777
777
|
};
|
|
778
778
|
}
|
|
779
779
|
|
|
780
|
+
// src/mistral.ts
|
|
781
|
+
var mistral = createOpenAICompatibleAdapter("https://api.mistral.ai/v1");
|
|
782
|
+
|
|
783
|
+
// src/cohere.ts
|
|
784
|
+
var cohere = createOpenAICompatibleAdapter("https://api.cohere.com/compatibility/v1");
|
|
785
|
+
|
|
786
|
+
// src/together.ts
|
|
787
|
+
var together = createOpenAICompatibleAdapter("https://api.together.xyz/v1");
|
|
788
|
+
|
|
789
|
+
// src/groq.ts
|
|
790
|
+
var groq = createOpenAICompatibleAdapter("https://api.groq.com/openai/v1");
|
|
791
|
+
|
|
792
|
+
// src/fireworks.ts
|
|
793
|
+
var fireworks = createOpenAICompatibleAdapter("https://api.fireworks.ai/inference/v1");
|
|
794
|
+
|
|
795
|
+
// src/openrouter.ts
|
|
796
|
+
var openrouter = createOpenAICompatibleAdapter("https://openrouter.ai/api/v1");
|
|
797
|
+
|
|
798
|
+
// src/huggingface.ts
|
|
799
|
+
var huggingface = createOpenAICompatibleAdapter("https://router.huggingface.co/v1");
|
|
800
|
+
|
|
801
|
+
// src/lmstudio.ts
|
|
802
|
+
var lmstudio = createOpenAICompatibleAdapter("http://localhost:1234/v1");
|
|
803
|
+
|
|
804
|
+
// src/vllm.ts
|
|
805
|
+
var vllm = createOpenAICompatibleAdapter("http://localhost:8000/v1");
|
|
806
|
+
|
|
807
|
+
// src/llamacpp.ts
|
|
808
|
+
var llamacpp = createOpenAICompatibleAdapter("http://localhost:8080/v1");
|
|
809
|
+
|
|
810
|
+
// src/router.ts
|
|
811
|
+
function requireCapabilities(request) {
|
|
812
|
+
const needs = {};
|
|
813
|
+
if (request.context?.tools && request.context.tools.length > 0) needs.tools = true;
|
|
814
|
+
return needs;
|
|
815
|
+
}
|
|
816
|
+
function matchesCapabilities(need, have) {
|
|
817
|
+
if (!have) return true;
|
|
818
|
+
for (const key of Object.keys(need)) {
|
|
819
|
+
if (key === "extensions") continue;
|
|
820
|
+
if (need[key] && have[key] === false) return false;
|
|
821
|
+
}
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
function pickSyncPolicy(pool, policy) {
|
|
825
|
+
if (policy === "cheapest") {
|
|
826
|
+
const c = pool.reduce((best, x) => (x.cost ?? Infinity) < (best.cost ?? Infinity) ? x : best, pool[0]);
|
|
827
|
+
return { c, reason: "cheapest" };
|
|
828
|
+
}
|
|
829
|
+
if (policy === "fastest") {
|
|
830
|
+
const c = pool.reduce((best, x) => (x.latencyMs ?? Infinity) < (best.latencyMs ?? Infinity) ? x : best, pool[0]);
|
|
831
|
+
return { c, reason: "fastest" };
|
|
832
|
+
}
|
|
833
|
+
return { c: pool[0], reason: "capability-match" };
|
|
834
|
+
}
|
|
835
|
+
function createRouter(options) {
|
|
836
|
+
const { candidates } = options;
|
|
837
|
+
if (candidates.length === 0) throw new Error("createRouter requires at least one candidate");
|
|
838
|
+
const policy = options.policy ?? "cheapest";
|
|
839
|
+
return {
|
|
840
|
+
createSource: (request) => {
|
|
841
|
+
const need = requireCapabilities(request);
|
|
842
|
+
const capable = candidates.filter((c2) => matchesCapabilities(need, c2.capabilities ?? c2.adapter.capabilities));
|
|
843
|
+
const classified = options.classify?.(request);
|
|
844
|
+
if (typeof classified === "string") {
|
|
845
|
+
const c2 = capable.find((x) => x.id === classified);
|
|
846
|
+
if (c2) {
|
|
847
|
+
options.onRoute?.({ id: c2.id, reason: "classify:id", request });
|
|
848
|
+
return c2.adapter.createSource(request);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
let pool = capable;
|
|
852
|
+
let pickedBy;
|
|
853
|
+
if (Array.isArray(classified) && classified.length > 0) {
|
|
854
|
+
const filtered = capable.filter((c2) => c2.tags && classified.every((t) => c2.tags.includes(t)));
|
|
855
|
+
if (filtered.length > 0) {
|
|
856
|
+
pool = filtered;
|
|
857
|
+
pickedBy = "classify:tags";
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (pool.length === 0) throw new Error("no candidate satisfies the request");
|
|
861
|
+
if (typeof policy === "function") {
|
|
862
|
+
const maybe = policy({ request, candidates: pool });
|
|
863
|
+
if (maybe instanceof Promise) {
|
|
864
|
+
return {
|
|
865
|
+
abort: () => {
|
|
866
|
+
},
|
|
867
|
+
stream: async function* () {
|
|
868
|
+
const id = await maybe;
|
|
869
|
+
const c3 = pool.find((x) => x.id === id);
|
|
870
|
+
if (!c3) throw new Error(`policy returned unknown id: ${id}`);
|
|
871
|
+
options.onRoute?.({ id: c3.id, reason: pickedBy ?? "custom policy", request });
|
|
872
|
+
for await (const chunk of c3.adapter.createSource(request).stream()) yield chunk;
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const c2 = pool.find((x) => x.id === maybe);
|
|
877
|
+
if (!c2) throw new Error(`policy returned unknown id: ${maybe}`);
|
|
878
|
+
options.onRoute?.({ id: c2.id, reason: pickedBy ?? "custom policy", request });
|
|
879
|
+
return c2.adapter.createSource(request);
|
|
880
|
+
}
|
|
881
|
+
const { c, reason } = pickSyncPolicy(pool, policy);
|
|
882
|
+
options.onRoute?.({ id: c.id, reason: pickedBy ?? reason, request });
|
|
883
|
+
return c.adapter.createSource(request);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// src/ensemble.ts
|
|
889
|
+
async function drain(source, timeoutMs) {
|
|
890
|
+
const chunks = [];
|
|
891
|
+
let text = "";
|
|
892
|
+
let timer;
|
|
893
|
+
let timedOut = false;
|
|
894
|
+
let error;
|
|
895
|
+
if (timeoutMs !== void 0) {
|
|
896
|
+
timer = setTimeout(() => {
|
|
897
|
+
timedOut = true;
|
|
898
|
+
source.abort();
|
|
899
|
+
}, timeoutMs);
|
|
900
|
+
}
|
|
901
|
+
try {
|
|
902
|
+
for await (const chunk of source.stream()) {
|
|
903
|
+
chunks.push(chunk);
|
|
904
|
+
if (chunk.type === "text" && typeof chunk.content === "string") text += chunk.content;
|
|
905
|
+
}
|
|
906
|
+
} catch (err) {
|
|
907
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
908
|
+
} finally {
|
|
909
|
+
if (timer) clearTimeout(timer);
|
|
910
|
+
}
|
|
911
|
+
if (timedOut) error = new Error(`timeout after ${timeoutMs}ms`);
|
|
912
|
+
return { chunks, text, error };
|
|
913
|
+
}
|
|
914
|
+
function majorityVote(branches, candidates) {
|
|
915
|
+
const counts = /* @__PURE__ */ new Map();
|
|
916
|
+
const weightById = new Map(candidates.map((c) => [c.id, c.weight ?? 1]));
|
|
917
|
+
for (const b of branches) {
|
|
918
|
+
if (b.error) continue;
|
|
919
|
+
counts.set(b.text, (counts.get(b.text) ?? 0) + (weightById.get(b.id) ?? 1));
|
|
920
|
+
}
|
|
921
|
+
let best = "";
|
|
922
|
+
let bestScore = -1;
|
|
923
|
+
for (const [text, score] of counts) {
|
|
924
|
+
if (score > bestScore) {
|
|
925
|
+
bestScore = score;
|
|
926
|
+
best = text;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return best;
|
|
930
|
+
}
|
|
931
|
+
function createEnsembleAdapter(options) {
|
|
932
|
+
if (options.candidates.length === 0) {
|
|
933
|
+
throw new Error("createEnsembleAdapter requires at least one candidate");
|
|
934
|
+
}
|
|
935
|
+
const aggregate = options.aggregate ?? "majority-vote";
|
|
936
|
+
return {
|
|
937
|
+
createSource: (request) => {
|
|
938
|
+
let aborted = false;
|
|
939
|
+
const sources = [];
|
|
940
|
+
const run = async () => {
|
|
941
|
+
const tasks = options.candidates.map(async (c, i) => {
|
|
942
|
+
const source = c.adapter.createSource(request);
|
|
943
|
+
sources[i] = source;
|
|
944
|
+
const { chunks, text, error } = await drain(source, options.timeoutMs);
|
|
945
|
+
return { id: c.id, chunks, text, error };
|
|
946
|
+
});
|
|
947
|
+
return Promise.all(tasks);
|
|
948
|
+
};
|
|
949
|
+
const finalText = async (branches) => {
|
|
950
|
+
if (typeof aggregate === "function") return aggregate(branches);
|
|
951
|
+
if (aggregate === "concat") {
|
|
952
|
+
return branches.filter((b) => !b.error).map((b) => b.text).join("\n---\n");
|
|
953
|
+
}
|
|
954
|
+
if (aggregate === "longest") {
|
|
955
|
+
return branches.filter((b) => !b.error).reduce(
|
|
956
|
+
(best, b) => b.text.length > best.length ? b.text : best,
|
|
957
|
+
""
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
return majorityVote(branches, options.candidates);
|
|
961
|
+
};
|
|
962
|
+
return {
|
|
963
|
+
abort: () => {
|
|
964
|
+
aborted = true;
|
|
965
|
+
for (const s of sources) s?.abort();
|
|
966
|
+
},
|
|
967
|
+
stream: async function* () {
|
|
968
|
+
const branches = await run();
|
|
969
|
+
options.onBranches?.(branches);
|
|
970
|
+
if (aborted) return;
|
|
971
|
+
const text = await finalText(branches);
|
|
972
|
+
if (!text && branches.every((b) => b.error)) {
|
|
973
|
+
throw new Error("all ensemble branches failed");
|
|
974
|
+
}
|
|
975
|
+
yield { type: "text", content: text, metadata: { ensemble: branches.map((b) => ({ id: b.id, bytes: b.text.length, error: b.error?.message })) } };
|
|
976
|
+
yield { type: "done" };
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// src/fallback.ts
|
|
984
|
+
function createFallbackAdapter(candidates, options = {}) {
|
|
985
|
+
if (candidates.length === 0) {
|
|
986
|
+
throw new Error("createFallbackAdapter requires at least one candidate");
|
|
987
|
+
}
|
|
988
|
+
return {
|
|
989
|
+
createSource: (request) => {
|
|
990
|
+
let active;
|
|
991
|
+
let aborted = false;
|
|
992
|
+
return {
|
|
993
|
+
abort: () => {
|
|
994
|
+
aborted = true;
|
|
995
|
+
active?.abort();
|
|
996
|
+
},
|
|
997
|
+
stream: async function* () {
|
|
998
|
+
const errors = [];
|
|
999
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
1000
|
+
if (aborted) return;
|
|
1001
|
+
const candidate = candidates[i];
|
|
1002
|
+
let source;
|
|
1003
|
+
try {
|
|
1004
|
+
source = candidate.adapter.createSource(request);
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1007
|
+
errors.push({ id: candidate.id, error });
|
|
1008
|
+
options.onFallback?.({ id: candidate.id, index: i, error });
|
|
1009
|
+
if (options.shouldRetry && !options.shouldRetry(error, i)) throw error;
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
active = source;
|
|
1013
|
+
const iter = source.stream()[Symbol.asyncIterator]();
|
|
1014
|
+
let first;
|
|
1015
|
+
try {
|
|
1016
|
+
first = await iter.next();
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1019
|
+
errors.push({ id: candidate.id, error });
|
|
1020
|
+
options.onFallback?.({ id: candidate.id, index: i, error });
|
|
1021
|
+
if (options.shouldRetry && !options.shouldRetry(error, i)) throw error;
|
|
1022
|
+
active = void 0;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
if (first.done) {
|
|
1026
|
+
const error = new Error(`candidate ${candidate.id} emitted no chunks`);
|
|
1027
|
+
errors.push({ id: candidate.id, error });
|
|
1028
|
+
options.onFallback?.({ id: candidate.id, index: i, error });
|
|
1029
|
+
if (options.shouldRetry && !options.shouldRetry(error, i)) throw error;
|
|
1030
|
+
active = void 0;
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
yield first.value;
|
|
1034
|
+
while (true) {
|
|
1035
|
+
const next = await iter.next();
|
|
1036
|
+
if (next.done) return;
|
|
1037
|
+
yield next.value;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
const summary = errors.map((e) => `${e.id}: ${e.error.message}`).join("; ");
|
|
1041
|
+
throw new Error(`all fallback candidates failed (${summary})`);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
|
|
780
1048
|
// src/mock.ts
|
|
781
1049
|
function mockAdapter(options) {
|
|
782
1050
|
const { response, delayMs = 0, history } = options;
|
|
@@ -1065,29 +1333,42 @@ var kimiEmbedder = createOpenAICompatibleEmbedder("Kimi", "https://api.moonshot.
|
|
|
1065
1333
|
|
|
1066
1334
|
exports.anthropic = anthropic;
|
|
1067
1335
|
exports.chunkText = chunkText;
|
|
1336
|
+
exports.cohere = cohere;
|
|
1068
1337
|
exports.createAdapter = createAdapter;
|
|
1338
|
+
exports.createEnsembleAdapter = createEnsembleAdapter;
|
|
1339
|
+
exports.createFallbackAdapter = createFallbackAdapter;
|
|
1069
1340
|
exports.createOpenAICompatibleEmbedder = createOpenAICompatibleEmbedder;
|
|
1341
|
+
exports.createRouter = createRouter;
|
|
1070
1342
|
exports.deepseek = deepseek;
|
|
1071
1343
|
exports.deepseekEmbedder = deepseekEmbedder;
|
|
1072
1344
|
exports.fetchWithRetry = fetchWithRetry;
|
|
1345
|
+
exports.fireworks = fireworks;
|
|
1073
1346
|
exports.gemini = gemini;
|
|
1074
1347
|
exports.geminiEmbedder = geminiEmbedder;
|
|
1075
1348
|
exports.generic = generic;
|
|
1076
1349
|
exports.grok = grok;
|
|
1077
1350
|
exports.grokEmbedder = grokEmbedder;
|
|
1351
|
+
exports.groq = groq;
|
|
1352
|
+
exports.huggingface = huggingface;
|
|
1078
1353
|
exports.inMemorySink = inMemorySink;
|
|
1079
1354
|
exports.kimi = kimi;
|
|
1080
1355
|
exports.kimiEmbedder = kimiEmbedder;
|
|
1081
1356
|
exports.langchain = langchain;
|
|
1082
1357
|
exports.langgraph = langgraph;
|
|
1358
|
+
exports.llamacpp = llamacpp;
|
|
1359
|
+
exports.lmstudio = lmstudio;
|
|
1360
|
+
exports.mistral = mistral;
|
|
1083
1361
|
exports.mockAdapter = mockAdapter;
|
|
1084
1362
|
exports.ollama = ollama;
|
|
1085
1363
|
exports.ollamaEmbedder = ollamaEmbedder;
|
|
1086
1364
|
exports.openai = openai;
|
|
1087
1365
|
exports.openaiEmbedder = openaiEmbedder;
|
|
1366
|
+
exports.openrouter = openrouter;
|
|
1088
1367
|
exports.recordingAdapter = recordingAdapter;
|
|
1089
1368
|
exports.replayAdapter = replayAdapter;
|
|
1090
1369
|
exports.simulateStream = simulateStream;
|
|
1370
|
+
exports.together = together;
|
|
1091
1371
|
exports.vercelAI = vercelAI;
|
|
1372
|
+
exports.vllm = vllm;
|
|
1092
1373
|
//# sourceMappingURL=index.cjs.map
|
|
1093
1374
|
//# sourceMappingURL=index.cjs.map
|