| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import { App } from "../app/app";
- import { Log } from "../util/log";
- import { mergeDeep } from "remeda";
- import path from "path";
- import { Provider } from "../provider/provider";
- import type { LanguageModel, Provider as ProviderInstance } from "ai";
- import { NoSuchModelError } from "ai";
- import { Config } from "../config/config";
- import { BunProc } from "../bun";
- import { Global } from "../global";
- export namespace LLM {
- const log = Log.create({ service: "llm" });
- export class ModelNotFoundError extends Error {
- constructor(public readonly model: string) {
- super();
- }
- }
- const NATIVE_PROVIDERS: Record<string, Provider.Info> = {
- anthropic: {
- models: {
- "claude-sonnet-4-20250514": {
- name: "Claude 4 Sonnet",
- cost: {
- input: 3.0 / 1_000_000,
- output: 15.0 / 1_000_000,
- inputCached: 3.75 / 1_000_000,
- outputCached: 0.3 / 1_000_000,
- },
- contextWindow: 200000,
- maxTokens: 50000,
- attachment: true,
- },
- },
- },
- openai: {
- models: {
- "codex-mini-latest": {
- name: "Codex Mini",
- cost: {
- input: 1.5 / 1_000_000,
- inputCached: 0.375 / 1_000_000,
- output: 6.0 / 1_000_000,
- outputCached: 0.0 / 1_000_000,
- },
- contextWindow: 200000,
- maxTokens: 100000,
- attachment: true,
- reasoning: true,
- },
- },
- },
- google: {
- models: {
- "gemini-2.5-pro-preview-03-25": {
- name: "Gemini 2.5 Pro",
- cost: {
- input: 1.25 / 1_000_000,
- inputCached: 0 / 1_000_000,
- output: 10 / 1_000_000,
- outputCached: 0 / 1_000_000,
- },
- contextWindow: 1000000,
- maxTokens: 50000,
- attachment: true,
- },
- },
- },
- };
- const AUTODETECT: Record<string, string[]> = {
- anthropic: ["ANTHROPIC_API_KEY"],
- openai: ["OPENAI_API_KEY"],
- google: ["GOOGLE_GENERATIVE_AI_API_KEY"],
- };
- const state = App.state("llm", async () => {
- const config = await Config.get();
- const providers: Record<
- string,
- {
- info: Provider.Info;
- instance: ProviderInstance;
- }
- > = {};
- const models = new Map<
- string,
- { info: Provider.Model; instance: LanguageModel }
- >();
- const list = mergeDeep(NATIVE_PROVIDERS, config.providers ?? {});
- for (const [providerID, providerInfo] of Object.entries(list)) {
- if (
- !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 {
- models,
- providers,
- };
- });
- export async function providers() {
- return state().then((state) => state.providers);
- }
- export async function findModel(providerID: string, modelID: string) {
- const key = `${providerID}/${modelID}`;
- const s = await state();
- 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,
- });
- const info = provider.info.models[modelID];
- if (!info) throw new ModelNotFoundError(modelID);
- try {
- const match = provider.instance.languageModel(modelID);
- log.info("found", { providerID, modelID });
- s.models.set(key, {
- info,
- instance: match,
- });
- return {
- info,
- instance: match,
- };
- } catch (e) {
- if (e instanceof NoSuchModelError) throw new ModelNotFoundError(modelID);
- throw e;
- }
- }
- }
|