summaryrefslogtreecommitdiffhomepage
path: root/js/src/llm
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/llm')
-rw-r--r--js/src/llm/llm.ts109
-rw-r--r--js/src/llm/models/anthropic.ts0
-rw-r--r--js/src/llm/models/index.ts1
-rw-r--r--js/src/llm/models/model.ts11
4 files changed, 94 insertions, 27 deletions
diff --git a/js/src/llm/llm.ts b/js/src/llm/llm.ts
index 5d962219c..c0ab38530 100644
--- a/js/src/llm/llm.ts
+++ b/js/src/llm/llm.ts
@@ -1,9 +1,13 @@
import { App } from "../app";
import { Log } from "../util/log";
+import { mergeDeep } from "remeda";
+import path from "node:path";
-import { createAnthropic } from "@ai-sdk/anthropic";
import type { LanguageModel, Provider } from "ai";
import { NoSuchModelError } from "ai";
+import type { Config } from "../app/config";
+import { BunProc } from "../bun";
+import { Global } from "../global";
export namespace LLM {
const log = Log.create({ service: "llm" });
@@ -14,17 +18,67 @@ export namespace LLM {
}
}
+ const NATIVE_PROVIDERS: Record<string, Config.Provider> = {
+ anthropic: {
+ models: {
+ "claude-sonnet-4-20250514": {
+ name: "Claude 4 Sonnet",
+ cost: {
+ input: 3.0,
+ inputCached: 3.75,
+ output: 15.0,
+ outputCached: 0.3,
+ },
+ contextWindow: 200000,
+ maxTokens: 50000,
+ attachment: true,
+ },
+ },
+ },
+ };
+
+ const AUTODETECT: Record<string, string[]> = {
+ anthropic: ["ANTHROPIC_API_KEY"],
+ };
+
const state = App.state("llm", async (app) => {
- const providers: Provider[] = [];
-
- if (process.env["ANTHROPIC_API_KEY"] || app.config.providers?.anthropic) {
- log.info("loaded anthropic");
- const provider = createAnthropic({
- apiKey: app.config.providers?.anthropic?.apiKey,
- baseURL: app.config.providers?.anthropic?.baseURL,
- headers: app.config.providers?.anthropic?.headers,
- });
- providers.push(provider);
+ const providers: Record<
+ string,
+ {
+ info: Config.Provider;
+ instance: Provider;
+ }
+ > = {};
+
+ const list = mergeDeep(NATIVE_PROVIDERS, app.config.providers ?? {});
+
+ for (const [providerID, providerInfo] of Object.entries(list)) {
+ if (
+ !app.config.providers?.[providerID] &&
+ !AUTODETECT[providerID]?.some((env) => process.env[env])
+ )
+ continue;
+ const dir = path.join(
+ Global.cache(),
+ `node_modules`,
+ `@ai-sdk`,
+ providerID,
+ );
+ if (!(await Bun.file(path.join(dir, "package.json")).exists())) {
+ BunProc.run(["add", "--exact", `@ai-sdk/${providerID}@alpha`], {
+ cwd: Global.cache(),
+ });
+ }
+ const mod = await import(
+ path.join(Global.cache(), `node_modules`, `@ai-sdk`, providerID)
+ );
+ const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!];
+ const loaded = fn(providerInfo.options);
+ log.info("loaded", { provider: providerID });
+ providers[providerID] = {
+ info: providerInfo,
+ instance: loaded,
+ };
}
return {
@@ -37,23 +91,24 @@ export namespace LLM {
return state().then((state) => state.providers);
}
- export async function findModel(model: string) {
+ export async function findModel(providerID: string, modelID: string) {
+ const key = `${providerID}/${modelID}`;
const s = await state();
- if (s.models.has(model)) {
- return s.models.get(model)!;
- }
- log.info("loading", { model });
- for (const provider of s.providers) {
- try {
- const match = provider.languageModel(model);
- log.info("found", { model });
- s.models.set(model, match);
- return match;
- } catch (e) {
- if (e instanceof NoSuchModelError) continue;
- throw e;
- }
+ if (s.models.has(key)) return s.models.get(key)!;
+ const provider = s.providers[providerID];
+ if (!provider) throw new ModelNotFoundError(modelID);
+ log.info("loading", {
+ providerID,
+ modelID,
+ });
+ try {
+ const match = provider.instance.languageModel(modelID);
+ log.info("found", { providerID, modelID });
+ s.models.set(key, match);
+ return match;
+ } catch (e) {
+ if (e instanceof NoSuchModelError) throw new ModelNotFoundError(modelID);
+ throw e;
}
- throw new ModelNotFoundError(model);
}
}
diff --git a/js/src/llm/models/anthropic.ts b/js/src/llm/models/anthropic.ts
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/llm/models/anthropic.ts
diff --git a/js/src/llm/models/index.ts b/js/src/llm/models/index.ts
new file mode 100644
index 000000000..f974b4d3e
--- /dev/null
+++ b/js/src/llm/models/index.ts
@@ -0,0 +1 @@
+export * as anthropic from "./anthropic";
diff --git a/js/src/llm/models/model.ts b/js/src/llm/models/model.ts
new file mode 100644
index 000000000..e78dbb87f
--- /dev/null
+++ b/js/src/llm/models/model.ts
@@ -0,0 +1,11 @@
+export interface ModelInfo {
+ cost: {
+ input: number;
+ inputCached: number;
+ output: number;
+ outputCached: number;
+ };
+ contextWindow: number;
+ maxTokens: number;
+ attachment: boolean;
+}