Răsfoiți Sursa

fix(app): align context tokens with usage (#36015)

Luke Parker 1 lună în urmă
părinte
comite
b94e55c7c0

+ 2 - 3
packages/app/src/components/session-context-usage.tsx

@@ -12,7 +12,7 @@ import { useSync } from "@/context/sync"
 import { useLanguage } from "@/context/language"
 import { useProviders } from "@/hooks/use-providers"
 import { useSDK } from "@/context/sdk"
-import { getSessionContext, getSessionTokenTotal } from "@/components/session/session-context-metrics"
+import { getSessionContext } from "@/components/session/session-context-metrics"
 import { useSessionLayout } from "@/pages/session/session-layout"
 import { createSessionTabs } from "@/pages/session/helpers"
 import { useSettings } from "@/context/settings"
@@ -74,7 +74,6 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
   )
 
   const context = createMemo(() => getSessionContext(messages(), [...providers.all().values()]))
-  const tokens = createMemo(() => info()?.tokens)
   const cost = createMemo(() => {
     return usd().format(info()?.cost ?? 0)
   })
@@ -132,7 +131,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
       <ContextTooltipRow name={language.t("context.usage.usage")} value={`${context()?.usage ?? 0}%`} />
       <ContextTooltipRow
         name={language.t("context.usage.tokens")}
-        value={getSessionTokenTotal(tokens())?.toLocaleString(language.intl()) ?? "0"}
+        value={context()?.total.toLocaleString(language.intl()) ?? "0"}
       />
     </div>
   )

+ 5 - 14
packages/app/src/components/session/session-context-metrics.test.ts

@@ -1,6 +1,6 @@
 import { describe, expect, test } from "bun:test"
 import type { Message } from "@opencode-ai/sdk/v2/client"
-import { getSessionContext, getSessionTokenTotal } from "./session-context-metrics"
+import { getSessionContext } from "./session-context-metrics"
 
 const assistant = (
   id: string,
@@ -38,10 +38,10 @@ const user = (id: string) => {
 }
 
 describe("getSessionContext", () => {
-  test("computes usage from latest assistant with tokens", () => {
+  test("computes token totals and usage from latest assistant with tokens", () => {
     const messages = [
       user("u1"),
-      assistant("a1", { input: 0, output: 0, reasoning: 0, read: 0, write: 0 }, 0.5),
+      assistant("a1", { input: 600, output: 200, reasoning: 100, read: 50, write: 50 }, 0.5),
       assistant("a2", { input: 300, output: 100, reasoning: 50, read: 25, write: 25 }, 1.25),
     ]
     const providers = [
@@ -60,6 +60,8 @@ describe("getSessionContext", () => {
     const ctx = getSessionContext(messages, providers)
 
     expect(ctx?.message.id).toBe("a2")
+    expect(ctx?.total).toBe(500)
+    expect(ctx?.input).toBe(300)
     expect(ctx?.usage).toBe(50)
     expect(ctx?.providerLabel).toBe("OpenAI")
     expect(ctx?.modelLabel).toBe("GPT-4.1")
@@ -94,15 +96,4 @@ describe("getSessionContext", () => {
 
     expect(ctx).toBeUndefined()
   })
-
-  test("computes stored session token totals", () => {
-    expect(
-      getSessionTokenTotal({
-        input: 10,
-        output: 20,
-        reasoning: 30,
-        cache: { read: 40, write: 50 },
-      }),
-    ).toBe(150)
-  })
 })

+ 3 - 6
packages/app/src/components/session/session-context-metrics.ts

@@ -1,4 +1,4 @@
-import type { AssistantMessage, Message, Session } from "@opencode-ai/sdk/v2/client"
+import type { AssistantMessage, Message } from "@opencode-ai/sdk/v2/client"
 
 type Provider = {
   id: string
@@ -21,6 +21,7 @@ type Context = {
   modelLabel: string
   limit: number | undefined
   input: number
+  total: number
   usage: number | null
 }
 
@@ -54,6 +55,7 @@ const build = (messages: Message[] = [], providers: Provider[] = []): Context |
     modelLabel: model?.name ?? message.modelID,
     limit,
     input: message.tokens.input,
+    total,
     usage: limit ? Math.round((total / limit) * 100) : null,
   }
 }
@@ -61,8 +63,3 @@ const build = (messages: Message[] = [], providers: Provider[] = []): Context |
 export function getSessionContext(messages: Message[] = [], providers: Provider[] = []) {
   return build(messages, providers)
 }
-
-export function getSessionTokenTotal(tokens: Session["tokens"] | undefined) {
-  if (!tokens) return undefined
-  return tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write
-}

+ 7 - 7
packages/app/src/components/session/session-context-tab.tsx

@@ -15,7 +15,7 @@ import { useLanguage } from "@/context/language"
 import { useProviders } from "@/hooks/use-providers"
 import { useSDK } from "@/context/sdk"
 import { useSessionLayout } from "@/pages/session/session-layout"
-import { getSessionContext, getSessionTokenTotal } from "./session-context-metrics"
+import { getSessionContext } from "./session-context-metrics"
 import { estimateSessionContextBreakdown, type SessionContextBreakdownKey } from "./session-context-breakdown"
 import { createSessionContextFormatter } from "./session-context-format"
 
@@ -135,7 +135,6 @@ export function SessionContextTab() {
   )
 
   const ctx = createMemo(() => getSessionContext(messages(), [...providers.all().values()]))
-  const tokens = createMemo(() => info()?.tokens)
   const formatter = createMemo(() => createSessionContextFormatter(language.intl()))
 
   const cost = createMemo(() => {
@@ -204,14 +203,15 @@ export function SessionContextTab() {
     { label: "context.stats.provider", value: providerLabel },
     { label: "context.stats.model", value: modelLabel },
     { label: "context.stats.limit", value: () => formatter().number(ctx()?.limit) },
-    { label: "context.stats.totalTokens", value: () => formatter().number(getSessionTokenTotal(tokens())) },
+    { label: "context.stats.totalTokens", value: () => formatter().number(ctx()?.total) },
     { label: "context.stats.usage", value: () => formatter().percent(ctx()?.usage) },
-    { label: "context.stats.inputTokens", value: () => formatter().number(tokens()?.input) },
-    { label: "context.stats.outputTokens", value: () => formatter().number(tokens()?.output) },
-    { label: "context.stats.reasoningTokens", value: () => formatter().number(tokens()?.reasoning) },
+    { label: "context.stats.inputTokens", value: () => formatter().number(ctx()?.input) },
+    { label: "context.stats.outputTokens", value: () => formatter().number(ctx()?.message.tokens.output) },
+    { label: "context.stats.reasoningTokens", value: () => formatter().number(ctx()?.message.tokens.reasoning) },
     {
       label: "context.stats.cacheTokens",
-      value: () => `${formatter().number(tokens()?.cache.read)} / ${formatter().number(tokens()?.cache.write)}`,
+      value: () =>
+        `${formatter().number(ctx()?.message.tokens.cache.read)} / ${formatter().number(ctx()?.message.tokens.cache.write)}`,
     },
     { label: "context.stats.userMessages", value: () => counts().user.toLocaleString(language.intl()) },
     { label: "context.stats.assistantMessages", value: () => counts().assistant.toLocaleString(language.intl()) },