This commit is contained in:
Dax Raad
2025-05-28 13:57:02 -04:00
parent f3b224090c
commit afe741b63e
5 changed files with 36 additions and 13 deletions

View File

@@ -11,6 +11,7 @@
"ai": "^5.0.0-alpha.4", "ai": "^5.0.0-alpha.4",
"cac": "^6.7.14", "cac": "^6.7.14",
"clipanion": "^4.0.0-rc.4", "clipanion": "^4.0.0-rc.4",
"decimal.js": "^10.5.0",
"diff": "^8.0.2", "diff": "^8.0.2",
"env-paths": "^3.0.0", "env-paths": "^3.0.0",
"hono": "^4.7.10", "hono": "^4.7.10",

View File

@@ -27,6 +27,7 @@
"ai": "^5.0.0-alpha.4", "ai": "^5.0.0-alpha.4",
"cac": "^6.7.14", "cac": "^6.7.14",
"clipanion": "^4.0.0-rc.4", "clipanion": "^4.0.0-rc.4",
"decimal.js": "^10.5.0",
"diff": "^8.0.2", "diff": "^8.0.2",
"env-paths": "^3.0.0", "env-paths": "^3.0.0",
"hono": "^4.7.10", "hono": "^4.7.10",

View File

@@ -22,7 +22,7 @@ export namespace Config {
export const Provider = z.object({ export const Provider = z.object({
options: z.record(z.string(), z.any()).optional(), options: z.record(z.string(), z.any()).optional(),
models: z.record(z.string(), Model).optional(), models: z.record(z.string(), Model),
}); });
export type Provider = z.output<typeof Provider>; export type Provider = z.output<typeof Provider>;

View File

@@ -24,10 +24,10 @@ export namespace LLM {
"claude-sonnet-4-20250514": { "claude-sonnet-4-20250514": {
name: "Claude 4 Sonnet", name: "Claude 4 Sonnet",
cost: { cost: {
input: 3.0, input: 3.0 / 1_000_000,
inputCached: 3.75, inputCached: 3.75 / 1_000_000,
output: 15.0, output: 15.0 / 1_000_000,
outputCached: 0.3, outputCached: 0.3 / 1_000_000,
}, },
contextWindow: 200000, contextWindow: 200000,
maxTokens: 50000, maxTokens: 50000,
@@ -49,6 +49,10 @@ export namespace LLM {
instance: Provider; instance: Provider;
} }
> = {}; > = {};
const models = new Map<
string,
{ info: Config.Model; instance: LanguageModel }
>();
const list = mergeDeep(NATIVE_PROVIDERS, app.config.providers ?? {}); const list = mergeDeep(NATIVE_PROVIDERS, app.config.providers ?? {});
@@ -82,7 +86,7 @@ export namespace LLM {
} }
return { return {
models: new Map<string, LanguageModel>(), models,
providers, providers,
}; };
}); });
@@ -101,11 +105,19 @@ export namespace LLM {
providerID, providerID,
modelID, modelID,
}); });
const info = provider.info.models[modelID];
if (!info) throw new ModelNotFoundError(modelID);
try { try {
const match = provider.instance.languageModel(modelID); const match = provider.instance.languageModel(modelID);
log.info("found", { providerID, modelID }); log.info("found", { providerID, modelID });
s.models.set(key, match); s.models.set(key, {
return match; info,
instance: match,
});
return {
info,
instance: match,
};
} catch (e) { } catch (e) {
if (e instanceof NoSuchModelError) throw new ModelNotFoundError(modelID); if (e instanceof NoSuchModelError) throw new ModelNotFoundError(modelID);
throw e; throw e;

View File

@@ -17,6 +17,7 @@ import {
} from "ai"; } from "ai";
import { z } from "zod"; import { z } from "zod";
import * as tools from "../tool"; import * as tools from "../tool";
import { Decimal } from "decimal.js";
import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"; import PROMPT_ANTHROPIC from "./prompt/anthropic.txt";
import PROMPT_TITLE from "./prompt/title.txt"; import PROMPT_TITLE from "./prompt/title.txt";
@@ -31,6 +32,7 @@ export namespace Session {
id: Identifier.schema("session"), id: Identifier.schema("session"),
shareID: z.string().optional(), shareID: z.string().optional(),
title: z.string(), title: z.string(),
cost: z.number().optional(),
tokens: z.object({ tokens: z.object({
input: z.number(), input: z.number(),
output: z.number(), output: z.number(),
@@ -188,7 +190,7 @@ export namespace Session {
parts: input.parts, parts: input.parts,
}, },
]), ]),
model, model: model.instance,
}).then((result) => { }).then((result) => {
return Session.update(input.sessionID, (draft) => { return Session.update(input.sessionID, (draft) => {
draft.title = result.text; draft.title = result.text;
@@ -226,16 +228,23 @@ export namespace Session {
const result = streamText({ const result = streamText({
onStepFinish: (step) => { onStepFinish: (step) => {
update(input.sessionID, (draft) => { update(input.sessionID, (draft) => {
draft.tokens.input += step.usage.inputTokens || 0; const input = step.usage.inputTokens ?? 0;
draft.tokens.output += step.usage.outputTokens || 0; const output = step.usage.outputTokens ?? 0;
draft.tokens.reasoning += step.usage.reasoningTokens || 0; const reasoning = step.usage.reasoningTokens ?? 0;
draft.tokens.input += input;
draft.tokens.output += output;
draft.tokens.reasoning += reasoning;
draft.cost = new Decimal(draft.cost ?? 0)
.add(new Decimal(input).mul(model.info.cost.input))
.add(new Decimal(output).mul(model.info.cost.output))
.toNumber();
}); });
}, },
stopWhen: stepCountIs(1000), stopWhen: stepCountIs(1000),
messages: convertToModelMessages(msgs), messages: convertToModelMessages(msgs),
temperature: 0, temperature: 0,
tools, tools,
model, model: model.instance,
}); });
msgs.push(next); msgs.push(next);