diff options
| author | Dax Raad <[email protected]> | 2025-05-28 15:39:51 -0400 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-05-28 15:39:51 -0400 |
| commit | 6183398543bbd3ff9d23c5ba2ee40149c9ac7b68 (patch) | |
| tree | 836b9898baab37ad92561b67fe3272b15cc2860a | |
| parent | ff786d9139280b36f0214cb71afa18affb676095 (diff) | |
| download | opencode-6183398543bbd3ff9d23c5ba2ee40149c9ac7b68.tar.gz opencode-6183398543bbd3ff9d23c5ba2ee40149c9ac7b68.zip | |
sync
| -rw-r--r-- | app/packages/web/src/components/Share.tsx | 55 | ||||
| -rw-r--r-- | js/src/server/server.ts | 3 | ||||
| -rw-r--r-- | js/src/session/session.ts | 49 |
3 files changed, 79 insertions, 28 deletions
diff --git a/app/packages/web/src/components/Share.tsx b/app/packages/web/src/components/Share.tsx index 292921ec7..78f901f67 100644 --- a/app/packages/web/src/components/Share.tsx +++ b/app/packages/web/src/components/Share.tsx @@ -23,6 +23,16 @@ type SessionMessage = UIMessage<{ created: number completed?: number } + assistant?: { + modelID: string; + providerID: string; + cost: number; + tokens: { + input: number; + output: number; + reasoning: number; + }; + }; sessionID: string tool: Record<string, { properties: Record<string, any> @@ -36,11 +46,6 @@ type SessionMessage = UIMessage<{ type SessionInfo = { title: string cost?: number - tokens?: { - input?: number - output?: number - reasoning?: number - } } function getPartTitle(role: string, type: string): string | undefined { @@ -229,6 +234,26 @@ export default function Share(props: { api: string }) { ) } + const metrics = createMemo(() => { + const result = { + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + } + } + for (const msg of messages()) { + const assistant = msg.metadata?.assistant + if (!assistant) continue + result.cost += assistant.cost + result.tokens.input += assistant.tokens.input + result.tokens.output += assistant.tokens.output + result.tokens.reasoning += assistant.tokens.reasoning + } + return result + }) + return ( <main class={`${styles.root} not-content`}> <div class={styles.header}> @@ -242,25 +267,33 @@ export default function Share(props: { api: string }) { <div data-section="row"> <ul data-section="stats"> <li> + <span data-element-label>Cost</span> + {metrics().cost ? + <span>{metrics().cost}</span> + : + <span data-placeholder>—</span> + } + </li> + <li> <span data-element-label>Input Tokens</span> - {store.info?.tokens?.input ? - <span>{store.info?.tokens?.input}</span> + {metrics().tokens.input ? + <span>{metrics().tokens.input}</span> : <span data-placeholder>—</span> } </li> <li> <span data-element-label>Output Tokens</span> - {store.info?.tokens?.output ? - <span>{store.info?.tokens?.output}</span> + {metrics().tokens.output ? + <span>{metrics().tokens.output}</span> : <span data-placeholder>—</span> } </li> <li> <span data-element-label>Reasoning Tokens</span> - {store.info?.tokens?.reasoning ? - <span>{store.info?.tokens?.reasoning}</span> + {metrics().tokens.reasoning ? + <span>{metrics().tokens.reasoning}</span> : <span data-placeholder>—</span> } diff --git a/js/src/server/server.ts b/js/src/server/server.ts index 11262f4fd..5fb0dfe62 100644 --- a/js/src/server/server.ts +++ b/js/src/server/server.ts @@ -18,6 +18,7 @@ const SessionInfo = Session.Info.openapi({ const ProviderInfo = Config.Provider.openapi({ ref: "Provider.Info", }); + type ProviderInfo = z.output<typeof ProviderInfo>; export namespace Server { @@ -210,7 +211,7 @@ export namespace Server { ), async (c) => { const body = c.req.valid("json"); - const msg = await Session.chat(body as any); + const msg = await Session.chat(body); return c.json(msg); }, ) diff --git a/js/src/session/session.ts b/js/src/session/session.ts index 011519048..197d4d398 100644 --- a/js/src/session/session.ts +++ b/js/src/session/session.ts @@ -6,7 +6,6 @@ import { Storage } from "../storage/storage"; import { Log } from "../util/log"; import { convertToModelMessages, - generateText, stepCountIs, streamText, type TextUIPart, @@ -20,7 +19,6 @@ import * as tools from "../tool"; import { Decimal } from "decimal.js"; import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"; -import PROMPT_TITLE from "./prompt/title.txt"; import type { Tool } from "../tool/tool"; import { Share } from "../share/share"; @@ -42,6 +40,16 @@ export namespace Session { export type Info = z.output<typeof Info>; export type Message = UIMessage<{ + assistant?: { + modelID: string; + providerID: string; + cost: number; + tokens: { + input: number; + output: number; + reasoning: number; + }; + }; time: { created: number; completed?: number; @@ -71,8 +79,8 @@ export namespace Session { }, }; log.info("created", result); - await Storage.writeJSON("session/info/" + result.id, result); state().sessions.set(result.id, result); + await Storage.writeJSON("session/info/" + result.id, result); return result; } @@ -184,6 +192,7 @@ export namespace Session { } msgs.push(system); state().messages.set(input.sessionID, msgs); + /* generateText({ messages: convertToModelMessages([ { @@ -206,6 +215,7 @@ export namespace Session { draft.title = result.text; }); }); + */ await write(system); } const msg: Message = { @@ -228,6 +238,16 @@ export namespace Session { role: "assistant", parts: [], metadata: { + assistant: { + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + }, + modelID: input.modelID, + providerID: input.providerID, + }, time: { created: Date.now(), }, @@ -238,19 +258,16 @@ export namespace Session { const controller = new AbortController(); pending.set(input.sessionID, controller); const result = streamText({ - onStepFinish: (step) => { - update(input.sessionID, (draft) => { - 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(); - }); + onStepFinish: async (step) => { + const assistant = next.metadata!.assistant!; + assistant.tokens.input = step.usage.inputTokens ?? 0; + assistant.tokens.output = step.usage.outputTokens ?? 0; + assistant.tokens.reasoning = step.usage.reasoningTokens ?? 0; + assistant.cost = new Decimal(0) + .add(new Decimal(assistant.tokens.input).mul(model.info.cost.input)) + .add(new Decimal(assistant.tokens.output).mul(model.info.cost.output)) + .toNumber(); + await write(next); }, abortSignal: controller.signal, maxRetries: 6, |
