diff --git a/src/browser/components/RightSidebar/CostsTab.tsx b/src/browser/components/RightSidebar/CostsTab.tsx index 91e3d25f83..e1d6645616 100644 --- a/src/browser/components/RightSidebar/CostsTab.tsx +++ b/src/browser/components/RightSidebar/CostsTab.tsx @@ -236,7 +236,7 @@ const CostsTabComponent: React.FC = ({ workspaceId }) => { name: "Cache Create", tokens: displayUsage.cacheCreate.tokens, cost: displayUsage.cacheCreate.cost_usd, - color: TOKEN_COMPONENT_COLORS.cached, + color: TOKEN_COMPONENT_COLORS.cacheCreate, show: displayUsage.cacheCreate.tokens > 0, }, { @@ -312,7 +312,7 @@ const CostsTabComponent: React.FC = ({ workspaceId }) => { className="h-full transition-[width] duration-300" style={{ width: `${cacheCreateCostPercentage}%`, - background: TOKEN_COMPONENT_COLORS.cached, + background: TOKEN_COMPONENT_COLORS.cacheCreate, }} /> )} diff --git a/src/browser/stories/App.rightsidebar.stories.tsx b/src/browser/stories/App.rightsidebar.stories.tsx index 836d89155a..1cb11ff19e 100644 --- a/src/browser/stories/App.rightsidebar.stories.tsx +++ b/src/browser/stories/App.rightsidebar.stories.tsx @@ -101,6 +101,60 @@ export const CostsTab: AppStory = { }, }; +/** + * Costs tab showing cache create vs cache read differentiation. + * Cache create (orange) is typically more expensive than cache read (grey). + * This story uses realistic Anthropic-style usage where most input is cached. + */ +export const CostsTabWithCacheCreate: AppStory = { + render: () => ( + { + localStorage.setItem(RIGHT_SIDEBAR_TAB_KEY, JSON.stringify("costs")); + localStorage.setItem(RIGHT_SIDEBAR_COSTS_WIDTH_KEY, "350"); + + return setupSimpleChatStory({ + workspaceId: "ws-cache-create", + workspaceName: "feature/caching", + projectName: "my-app", + messages: [ + createUserMessage("msg-1", "Refactor the auth module", { historySequence: 1 }), + createAssistantMessage("msg-2", "I'll refactor the authentication module.", { + historySequence: 2, + }), + ], + sessionUsage: { + byModel: { + "anthropic:claude-sonnet-4-20250514": { + // Realistic Anthropic usage: heavy caching, cache create is expensive + input: { tokens: 2000, cost_usd: 0.006 }, + cached: { tokens: 45000, cost_usd: 0.0045 }, // Cache read: cheap + cacheCreate: { tokens: 30000, cost_usd: 0.1125 }, // Cache create: expensive! + output: { tokens: 3000, cost_usd: 0.045 }, + reasoning: { tokens: 0, cost_usd: 0 }, + model: "anthropic:claude-sonnet-4-20250514", + }, + }, + version: 1, + }, + }); + }} + /> + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + // Wait for costs to render - cache create should be dominant cost + await waitFor( + () => { + canvas.getByText("Cache Create"); + canvas.getByText("Cache Read"); + }, + { timeout: 5000 } + ); + }, +}; + /** * Review tab selected - click switches from Costs to Review tab * Verifies per-tab width persistence: starts at Costs width (350px), switches to Review width (700px) diff --git a/src/browser/styles/globals.css b/src/browser/styles/globals.css index 6fd6c90d77..2ad1077c8f 100644 --- a/src/browser/styles/globals.css +++ b/src/browser/styles/globals.css @@ -131,6 +131,7 @@ --color-token-input: hsl(120 40% 35%); --color-token-output: hsl(207 100% 40%); --color-token-cached: hsl(0 0% 50%); + --color-token-cache-create: hsl(30 90% 50%); /* Plan surfaces */ --surface-plan-gradient: linear-gradient( @@ -383,6 +384,7 @@ --color-token-input: hsl(125 45% 36%); --color-token-output: hsl(207 90% 40%); --color-token-cached: hsl(210 16% 50%); + --color-token-cache-create: hsl(30 85% 45%); --surface-plan-gradient: linear-gradient( 135deg, @@ -622,6 +624,7 @@ --color-token-input: #859900; --color-token-output: #268bd2; --color-token-cached: #839496; + --color-token-cache-create: #cb4b16; --surface-plan-gradient: linear-gradient(135deg, color-mix(in srgb, var(--color-plan-mode), transparent 94%) 0%, color-mix(in srgb, var(--color-plan-mode), transparent 97%) 100%); --surface-plan-border: color-mix(in srgb, var(--color-plan-mode), transparent 78%); @@ -836,6 +839,7 @@ --color-token-input: #859900; --color-token-output: #268bd2; --color-token-cached: #586e75; + --color-token-cache-create: #cb4b16; --surface-plan-gradient: linear-gradient(135deg, color-mix(in srgb, var(--color-plan-mode), transparent 92%) 0%, color-mix(in srgb, var(--color-plan-mode), transparent 95%) 100%); --surface-plan-border: color-mix(in srgb, var(--color-plan-mode), transparent 70%); diff --git a/src/common/utils/tokens/tokenMeterUtils.ts b/src/common/utils/tokens/tokenMeterUtils.ts index 51caf8774b..2504363c0e 100644 --- a/src/common/utils/tokens/tokenMeterUtils.ts +++ b/src/common/utils/tokens/tokenMeterUtils.ts @@ -4,6 +4,7 @@ import { supports1MContext } from "../ai/models"; export const TOKEN_COMPONENT_COLORS = { cached: "var(--color-token-cached)", + cacheCreate: "var(--color-token-cache-create)", input: "var(--color-token-input)", output: "var(--color-token-output)", thinking: "var(--color-thinking-mode)", @@ -35,7 +36,7 @@ const SEGMENT_DEFS: SegmentDef[] = [ { type: "cacheCreate", key: "cacheCreate", - color: TOKEN_COMPONENT_COLORS.cached, + color: TOKEN_COMPONENT_COLORS.cacheCreate, label: "Cache Create", }, { type: "input", key: "input", color: TOKEN_COMPONENT_COLORS.input, label: "Input" },