diff --git a/devlog/_plan/260912_devin_hardening/cached-token-companion.jpg b/devlog/_plan/260912_devin_hardening/cached-token-companion.jpg deleted file mode 100644 index 2110f1f8ed..0000000000 Binary files a/devlog/_plan/260912_devin_hardening/cached-token-companion.jpg and /dev/null differ diff --git a/devlog/_plan/260912_devin_hardening/logs-devin-cached-restored.jpg b/devlog/_plan/260912_devin_hardening/logs-devin-cached-restored.jpg new file mode 100644 index 0000000000..665349ab1c Binary files /dev/null and b/devlog/_plan/260912_devin_hardening/logs-devin-cached-restored.jpg differ diff --git a/gui/src/format-tokens.ts b/gui/src/format-tokens.ts index 737a8dae6e..bc53af6ae5 100644 --- a/gui/src/format-tokens.ts +++ b/gui/src/format-tokens.ts @@ -32,24 +32,3 @@ export function formatTokens(n: number, locale: string): string { if (n < 1_000_000_000_000) return `${trim((n / 1_000_000_000).toFixed(1))}B`; return `${trim((n / 1_000_000_000_000).toFixed(1))}T`; } - -/** - * A token total with its cached subset beside it: `5.8만 c5.7만`, `58K c57K`. - * - * A cached request's total is mostly cache. Printing the total alone makes a - * 58,000-token prompt that is 57,000 cache read and 1,000 fresh look like an - * ordinary 58,000-token prompt, and it reads as a different, smaller request - * than the log row directly below it, which already shows the companion. The - * `c` marker matches the `logs.tokens.cacheRead` label, which reads - * "cache read (c)". - * - * The companion is omitted only when there is no cache to report. It is NOT - * omitted when the cached subset equals the total: a turn served entirely from - * cache is the most interesting row on the page, and hiding its marker there - * would blank exactly the case this exists for. - */ -export function formatTokensWithCache(total: number, cached: number | undefined, locale: string): string { - const base = formatTokens(total, locale); - if (cached === undefined || !Number.isFinite(cached) || cached <= 0) return base; - return `${base} c${formatTokens(cached, locale)}`; -} diff --git a/gui/src/pages/Logs.tsx b/gui/src/pages/Logs.tsx index b3fa00b332..774efc455a 100644 --- a/gui/src/pages/Logs.tsx +++ b/gui/src/pages/Logs.tsx @@ -3,7 +3,6 @@ import { useVirtualizer } from "@tanstack/react-virtual"; import { useI18n, LOCALES, type TFn } from "../i18n/shared"; import { formatProviderDisplayName } from "../provider-icons"; import { formatTokens } from "../format-tokens"; -import { formatTokensWithCache } from "../format-tokens"; import { hashLogConversationQuery } from "../log-conversation-id"; import { statusCodeInfo } from "../status-codes"; import { IconX } from "../icons"; @@ -364,27 +363,19 @@ function formatLogDateTime(ts: number, localeTag?: string, timeZone?: string): s function summarizeFilteredLogs(entries: LogEntry[]): { requests: number; totalTokens: number; - cachedInputTokens: number; estimatedCostUsd: number; priorityLowerBound: boolean; unpricedRequests: number; unmeteredRequests: number; } { let totalTokens = 0; - let cachedInputTokens = 0; for (const entry of entries) { const tokens = displayTokenTotal(entry); if (tokens !== undefined) totalTokens += tokens; - // The banner sits directly above rows that already print `c `, so a - // total with no companion read as a different, smaller figure than the rows - // it summarizes. - const read = cacheSplit(entry).read; - if (read !== undefined && read > 0) cachedInputTokens += read; } return { requests: entries.length, totalTokens, - cachedInputTokens, ...summarizeEstimatedCosts(entries), }; } @@ -696,11 +687,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { {t("logs.conversation.totals", { requests: conversationTotals.requests, - tokens: formatTokensWithCache( - conversationTotals.totalTokens, - conversationTotals.cachedInputTokens, - localeTag ?? locale, - ), + tokens: formatTokens(conversationTotals.totalTokens, localeTag ?? locale), cost: formatEstimatedUsdValue( conversationTotals.estimatedCostUsd, t, diff --git a/gui/src/pages/Usage.tsx b/gui/src/pages/Usage.tsx index 44028824b2..96f0f1db0c 100644 --- a/gui/src/pages/Usage.tsx +++ b/gui/src/pages/Usage.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { useI18n, type TFn, type Locale } from "../i18n/shared"; import { formatProviderDisplayName } from "../provider-icons"; -import { formatTokens, formatTokensWithCache } from "../format-tokens"; +import { formatTokens } from "../format-tokens"; import { formatEstimatedUsdValue as formatUsdEstimate } from "../intl-formatters"; import { readSessionListCache, writeSessionListCache } from "../session-list-cache"; import { EmptyState, Notice } from "../ui"; @@ -64,10 +64,6 @@ interface UsageModel { totalTokens: number; inputTokens: number; outputTokens: number; - // /api/usage has carried these all along; dropping them from the row type is - // what left the token column without its cached companion. - cachedInputTokens?: number; - cacheReadInputTokens?: number; shareRatio: number; } @@ -78,8 +74,6 @@ interface UsageProvider { reportedRequests: number; estimatedRequests: number; totalTokens: number; - cachedInputTokens?: number; - cacheReadInputTokens?: number; shareRatio: number; } @@ -567,7 +561,7 @@ function UsageModelsTable({ {formatProviderDisplayName(model.provider, t)} {model.requests} {model.measuredRequests} - {formatTokensWithCache(model.totalTokens, model.cacheReadInputTokens ?? model.cachedInputTokens, locale)} + {formatTokens(model.totalTokens, locale)}
))} @@ -627,7 +621,7 @@ function UsageProvidersTable({ {formatProviderDisplayName(provider.provider, t)} {provider.requests} {provider.measuredRequests} - {formatTokensWithCache(provider.totalTokens, provider.cacheReadInputTokens ?? provider.cachedInputTokens, locale)} + {formatTokens(provider.totalTokens, locale)}
))} diff --git a/gui/src/pages/dashboard-overview-head.tsx b/gui/src/pages/dashboard-overview-head.tsx index fea1ae94e9..44b3f8ac3c 100644 --- a/gui/src/pages/dashboard-overview-head.tsx +++ b/gui/src/pages/dashboard-overview-head.tsx @@ -1,6 +1,6 @@ import { IconAlert, IconInfo } from "../icons"; import { type TKey, useT } from "../i18n/shared"; -import { formatTokensWithCache } from "../format-tokens"; +import { formatTokens } from "../format-tokens"; import { formatUptime } from "../formatUptime"; import { navigateHash } from "../hash-routing"; import type { useDashboardData } from "./use-dashboard-data"; @@ -81,13 +81,7 @@ export function DashboardOverviewHead({
{t("dash.providers")}
{providers.length}
{t("dash.tokens30d")}
-
{usage30d && usage30d.summary.requests > 0 - ? formatTokensWithCache( - usage30d.summary.totalTokens, - usage30d.summary.cacheReadInputTokens ?? usage30d.summary.cachedInputTokens, - locale, - ) - : "—"}
+
{usage30d && usage30d.summary.requests > 0 ? formatTokens(usage30d.summary.totalTokens, locale) : "—"}
{usage30d && usage30d.summary.requests > 0 ? t("dash.coverage").replace("{pct}", `${Math.round(usage30d.summary.coverageRatio * 100)}%`) diff --git a/gui/src/pages/dashboard-shared.ts b/gui/src/pages/dashboard-shared.ts index cd0f7ac66d..029e39b2da 100644 --- a/gui/src/pages/dashboard-shared.ts +++ b/gui/src/pages/dashboard-shared.ts @@ -123,17 +123,7 @@ export interface SidecarPatch { }; } export interface ShadowCallData { enabled: boolean; model: string; sourceModels?: string[] } -export interface UsageSummary30d { - summary: { - requests: number; - totalTokens: number; - coverageRatio: number; - // Already on /api/usage; the tile showed a bare total only because this - // type dropped them. - cachedInputTokens?: number; - cacheReadInputTokens?: number; - }; -} +export interface UsageSummary30d { summary: { requests: number; totalTokens: number; coverageRatio: number } } export type UpdateChannel = "latest" | "preview"; export type Installer = "npm" | "bun" | "source"; export type UpdateJobStatus = "running" | "restarting" | "succeeded" | "failed"; diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index 18fc2060c0..33b960ac70 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -718,7 +718,6 @@ "grok-sync.test.ts": "providers/xai", "grok-writer-boundary.test.ts": "providers/xai", "gui-api-error.test.ts": "gui", - "gui-format-tokens-cache.test.ts": "gui", "gui-management-session.test.ts": "gui", "gui-pair-capability.test.ts": "gui", "gui-pair-client.test.ts": "gui", diff --git a/src/cli/usage-report.ts b/src/cli/usage-report.ts index 2d2c525cde..3311a781a1 100644 --- a/src/cli/usage-report.ts +++ b/src/cli/usage-report.ts @@ -17,13 +17,6 @@ interface CostRow { model?: string; requests: number; totalTokens: number; - /** - * Cache-read subset of the row's tokens. The API sends it; the CLI dropped it, - * so a mostly-cached provider's TOKENS column read as an ordinary total while - * the summary line two rows above already said `cached N`. - */ - cachedInputTokens?: number; - cacheReadInputTokens?: number; estimatedCostUsd?: number; } @@ -60,8 +53,6 @@ interface UsageReportInput { ambiguous?: boolean; requests: number; totalTokens: number; - cachedInputTokens?: number; - cacheReadInputTokens?: number; estimatedCostUsd?: number; }[]; } @@ -85,16 +76,6 @@ function count(value: number | undefined): string { return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : "—"; } -/** - * A row's token total with its cache-read subset, matching the summary line's - * `cached N` wording rather than inventing a second vocabulary for the tables. - */ -function countWithCache(total: number | undefined, cached: number | undefined): string { - const base = count(total); - if (typeof cached !== "number" || !Number.isFinite(cached) || cached <= 0) return base; - return `${base} (cached ${count(cached)})`; -} - /** * Matches the dashboard's `~$` with four fraction digits. Estimates below a * hundredth of a cent still read as a number rather than collapsing to $0.00, @@ -160,12 +141,7 @@ export function formatUsageReport(data: UsageReportInput): string[] { lines.push(""); lines.push(...table( ["PROVIDER", "REQUESTS", "TOKENS", "EST. COST"], - providers.map(row => [ - row.provider, - count(row.requests), - countWithCache(row.totalTokens, row.cacheReadInputTokens ?? row.cachedInputTokens), - usd(row.estimatedCostUsd), - ]), + providers.map(row => [row.provider, count(row.requests), count(row.totalTokens), usd(row.estimatedCostUsd)]), )); } @@ -187,7 +163,7 @@ export function formatUsageReport(data: UsageReportInput): string[] { // wrong conclusion. Mark it rather than presenting it as a single identity. row.ambiguous ? `${terminalText(row.accountLogLabel)} (ambiguous)` : terminalText(row.accountLogLabel), count(row.requests), - countWithCache(row.totalTokens, row.cacheReadInputTokens ?? row.cachedInputTokens), + count(row.totalTokens), usd(row.estimatedCostUsd), ]), )); @@ -199,13 +175,7 @@ export function formatUsageReport(data: UsageReportInput): string[] { const shown = models.slice(0, MAX_MODEL_ROWS); lines.push(...table( ["MODEL", "PROVIDER", "REQUESTS", "TOKENS", "EST. COST"], - shown.map(row => [ - row.model ?? "-", - row.provider, - count(row.requests), - countWithCache(row.totalTokens, row.cacheReadInputTokens ?? row.cachedInputTokens), - usd(row.estimatedCostUsd), - ]), + shown.map(row => [row.model ?? "-", row.provider, count(row.requests), count(row.totalTokens), usd(row.estimatedCostUsd)]), )); if (models.length > shown.length) { lines.push(`... ${models.length - shown.length} more (use --json)`); diff --git a/tests/cli/cli-usage-report.test.ts b/tests/cli/cli-usage-report.test.ts index 1be03f68f2..5399137a57 100644 --- a/tests/cli/cli-usage-report.test.ts +++ b/tests/cli/cli-usage-report.test.ts @@ -404,28 +404,3 @@ describe("ocx logs --follow output contract", () => { } }); }); - -describe("cached tokens in the per-row tables", () => { - test("a provider and model row name their cache-read subset", () => { - // The summary line has always said "cached N". The tables below it printed a - // bare total, so a mostly-cached provider looked like an ordinary one. - const lines = formatUsageReport({ - range: "today", - summary: { requests: 1, totalTokens: 58_000, cachedInputTokens: 57_000 }, - providers: [{ provider: "devin-cli", requests: 1, totalTokens: 58_000, cacheReadInputTokens: 57_000 }], - models: [{ provider: "devin-cli", model: "swe-2", requests: 1, totalTokens: 58_000, cachedInputTokens: 57_000 }], - }).join("\n"); - expect(lines).toContain("58,000 (cached 57,000)"); - expect(lines.match(/cached 57,000/g)?.length).toBeGreaterThanOrEqual(2); - }); - - test("a provider that reports no cache keeps a bare total", () => { - const lines = formatUsageReport({ - range: "today", - summary: { requests: 1, totalTokens: 58_000 }, - providers: [{ provider: "xai", requests: 1, totalTokens: 58_000 }], - }).join("\n"); - expect(lines).toContain("58,000"); - expect(lines).not.toContain("cached"); - }); -}); diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index 5e57040565..9ea5f32928 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -549,7 +549,6 @@ "grok-sync.test.ts": "providers/xai", "grok-writer-boundary.test.ts": "providers/xai", "gui-api-error.test.ts": "gui", - "gui-format-tokens-cache.test.ts": "gui", "gui-management-session.test.ts": "gui", "gui-pair-capability.test.ts": "gui", "gui-pair-client.test.ts": "gui", diff --git a/tests/gui/gui-format-tokens-cache.test.ts b/tests/gui/gui-format-tokens-cache.test.ts deleted file mode 100644 index fe38fb81da..0000000000 --- a/tests/gui/gui-format-tokens-cache.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { formatTokens, formatTokensWithCache } from "../../gui/src/format-tokens"; - -/** - * A cached request's total is mostly cache. The logs table has always shown the - * total with a stacked `c `; every other surface printed the total alone, - * which reads as a different, smaller request than the rows beside it. - */ -describe("formatTokensWithCache", () => { - test("renders the cached subset beside the total in both number scales", () => { - expect(formatTokensWithCache(58_000, 57_000, "ko")).toBe("5.8만 c5.7만"); - expect(formatTokensWithCache(58_000, 57_000, "en")).toBe("58K c57K"); - expect(formatTokensWithCache(58_000, 57_000, "zh")).toBe("5.8万 c5.7万"); - }); - - test("a provider that reports no cache is left exactly as it was", () => { - for (const cached of [undefined, 0, Number.NaN]) { - expect(formatTokensWithCache(58_000, cached, "ko")).toBe(formatTokens(58_000, "ko")); - } - // A negative count is nonsense rather than a cache miss; treat it as absent. - expect(formatTokensWithCache(58_000, -1, "en")).toBe("58K"); - }); - - test("a turn served entirely from cache still shows the marker", () => { - // This is the most cached row on the page. Hiding the companion when the - // subset equals the total would blank exactly the case worth showing. - expect(formatTokensWithCache(57_000, 57_000, "en")).toBe("57K c57K"); - }); -});