summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--js/bun.lock1
-rw-r--r--js/package.json1
-rw-r--r--js/src/app/config.ts2
-rw-r--r--js/src/llm/llm.ts26
-rw-r--r--js/src/session/session.ts19
5 files changed, 36 insertions, 13 deletions
diff --git a/js/bun.lock b/js/bun.lock
index eda3a2994..e3238427a 100644
--- a/js/bun.lock
+++ b/js/bun.lock
@@ -11,6 +11,7 @@
"ai": "^5.0.0-alpha.4",
"cac": "^6.7.14",
"clipanion": "^4.0.0-rc.4",
+ "decimal.js": "^10.5.0",
"diff": "^8.0.2",
"env-paths": "^3.0.0",
"hono": "^4.7.10",
diff --git a/js/package.json b/js/package.json
index 9cc015a7b..902f703eb 100644
--- a/js/package.json
+++ b/js/package.json
@@ -27,6 +27,7 @@
"ai": "^5.0.0-alpha.4",
"cac": "^6.7.14",
"clipanion": "^4.0.0-rc.4",
+ "decimal.js": "^10.5.0",
"diff": "^8.0.2",
"env-paths": "^3.0.0",
"hono": "^4.7.10",
diff --git a/js/src/app/config.ts b/js/src/app/config.ts
index 77a1f606f..a3edbf889 100644
--- a/js/src/app/config.ts
+++ b/js/src/app/config.ts
@@ -22,7 +22,7 @@ export namespace Config {
export const Provider = z.object({
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>;
diff --git a/js/src/llm/llm.ts b/js/src/llm/llm.ts
index c0ab38530..e34030c50 100644
--- a/js/src/llm/llm.ts
+++ b/js/src/llm/llm.ts
@@ -24,10 +24,10 @@ export namespace LLM {
"claude-sonnet-4-20250514": {
name: "Claude 4 Sonnet",
cost: {
- input: 3.0,
- inputCached: 3.75,
- output: 15.0,
- outputCached: 0.3,
+ input: 3.0 / 1_000_000,
+ inputCached: 3.75 / 1_000_000,
+ output: 15.0 / 1_000_000,
+ outputCached: 0.3 / 1_000_000,
},
contextWindow: 200000,
maxTokens: 50000,
@@ -49,6 +49,10 @@ export namespace LLM {
instance: Provider;
}
> = {};
+ const models = new Map<
+ string,
+ { info: Config.Model; instance: LanguageModel }
+ >();
const list = mergeDeep(NATIVE_PROVIDERS, app.config.providers ?? {});
@@ -82,7 +86,7 @@ export namespace LLM {
}
return {
- models: new Map<string, LanguageModel>(),
+ models,
providers,
};
});
@@ -101,11 +105,19 @@ export namespace LLM {
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, match);
- return match;
+ s.models.set(key, {
+ info,
+ instance: match,
+ });
+ return {
+ info,
+ instance: match,
+ };
} catch (e) {
if (e instanceof NoSuchModelError) throw new ModelNotFoundError(modelID);
throw e;
diff --git a/js/src/session/session.ts b/js/src/session/session.ts
index 06c51625f..406ab27f8 100644
--- a/js/src/session/session.ts
+++ b/js/src/session/session.ts
@@ -17,6 +17,7 @@ import {
} from "ai";
import { z } from "zod";
import * as tools from "../tool";
+import { Decimal } from "decimal.js";
import PROMPT_ANTHROPIC from "./prompt/anthropic.txt";
import PROMPT_TITLE from "./prompt/title.txt";
@@ -31,6 +32,7 @@ export namespace Session {
id: Identifier.schema("session"),
shareID: z.string().optional(),
title: z.string(),
+ cost: z.number().optional(),
tokens: z.object({
input: z.number(),
output: z.number(),
@@ -188,7 +190,7 @@ export namespace Session {
parts: input.parts,
},
]),
- model,
+ model: model.instance,
}).then((result) => {
return Session.update(input.sessionID, (draft) => {
draft.title = result.text;
@@ -226,16 +228,23 @@ export namespace Session {
const result = streamText({
onStepFinish: (step) => {
update(input.sessionID, (draft) => {
- draft.tokens.input += step.usage.inputTokens || 0;
- draft.tokens.output += step.usage.outputTokens || 0;
- draft.tokens.reasoning += step.usage.reasoningTokens || 0;
+ const input = step.usage.inputTokens ?? 0;
+ const output = step.usage.outputTokens ?? 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),
messages: convertToModelMessages(msgs),
temperature: 0,
tools,
- model,
+ model: model.instance,
});
msgs.push(next);