diff --git a/server/claude-sdk.js b/server/claude-sdk.js index 5d8a27d7..e59e503d 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -304,7 +304,11 @@ function extractTokenBudget(sdkMessage) { const messageUsage = sdkMessage.message?.usage || sdkMessage.usage; if (messageUsage && typeof messageUsage === 'object') { - const inputTokens = readNumber(messageUsage.input_tokens ?? messageUsage.inputTokens); + const directInputTokens = readNumber(messageUsage.input_tokens ?? messageUsage.inputTokens); + const cacheCreationTokens = readNumber(messageUsage.cache_creation_input_tokens ?? messageUsage.cacheCreationInputTokens ?? messageUsage.cacheCreationTokens); + const cacheReadTokens = readNumber(messageUsage.cache_read_input_tokens ?? messageUsage.cacheReadInputTokens ?? messageUsage.cacheReadTokens); + const cacheTokens = cacheCreationTokens + cacheReadTokens; + const inputTokens = directInputTokens + cacheTokens; const outputTokens = readNumber(messageUsage.output_tokens ?? messageUsage.outputTokens); const totalUsed = inputTokens + outputTokens; const contextWindow = parseInt(process.env.CONTEXT_WINDOW, 10) || 160000; @@ -314,6 +318,9 @@ function extractTokenBudget(sdkMessage) { total: contextWindow, inputTokens, outputTokens, + cacheReadTokens, + cacheCreationTokens, + cacheTokens, breakdown: { input: inputTokens, output: outputTokens, diff --git a/server/index.js b/server/index.js index a986148e..9f836efd 100755 --- a/server/index.js +++ b/server/index.js @@ -87,6 +87,11 @@ const installMode = fs.existsSync(path.join(APP_ROOT, '.git')) ? 'git' : 'npm'; console.log('SERVER_PORT from env:', process.env.SERVER_PORT); +function readUsageNumber(value) { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : 0; +} + const app = express(); const server = http.createServer(app); @@ -1386,6 +1391,8 @@ app.get('/api/projects/:projectId/sessions/:sessionId/token-usage', authenticate const contextWindow = Number.isFinite(parsedContextWindow) ? parsedContextWindow : 160000; let inputTokens = 0; let outputTokens = 0; + let cacheReadTokens = 0; + let cacheCreationTokens = 0; // Find the latest assistant message with usage data (scan from end) for (let i = lines.length - 1; i >= 0; i--) { @@ -1397,8 +1404,11 @@ app.get('/api/projects/:projectId/sessions/:sessionId/token-usage', authenticate const usage = entry.message.usage; // Use token counts from latest assistant message only - inputTokens = usage.input_tokens || 0; - outputTokens = usage.output_tokens || 0; + const directInputTokens = readUsageNumber(usage.input_tokens ?? usage.inputTokens); + cacheReadTokens = readUsageNumber(usage.cache_read_input_tokens ?? usage.cacheReadInputTokens ?? usage.cacheReadTokens); + cacheCreationTokens = readUsageNumber(usage.cache_creation_input_tokens ?? usage.cacheCreationInputTokens ?? usage.cacheCreationTokens); + inputTokens = directInputTokens + cacheReadTokens + cacheCreationTokens; + outputTokens = readUsageNumber(usage.output_tokens ?? usage.outputTokens); break; // Stop after finding the latest assistant message } @@ -1409,12 +1419,16 @@ app.get('/api/projects/:projectId/sessions/:sessionId/token-usage', authenticate } const totalUsed = inputTokens + outputTokens; + const cacheTokens = cacheReadTokens + cacheCreationTokens; res.json({ used: totalUsed, total: contextWindow, inputTokens, outputTokens, + cacheReadTokens, + cacheCreationTokens, + cacheTokens, breakdown: { input: inputTokens, output: outputTokens diff --git a/server/routes/agent.js b/server/routes/agent.js index a5607588..85e26c9d 100644 --- a/server/routes/agent.js +++ b/server/routes/agent.js @@ -592,12 +592,14 @@ class ResponseCollector { } } + const inputTokens = totalInput + totalCacheRead + totalCacheCreation; + return { - inputTokens: totalInput, + inputTokens, outputTokens: totalOutput, cacheReadTokens: totalCacheRead, cacheCreationTokens: totalCacheCreation, - totalTokens: totalInput + totalOutput + totalCacheRead + totalCacheCreation + totalTokens: inputTokens + totalOutput }; } } diff --git a/server/routes/commands.js b/server/routes/commands.js index 09022e0c..ea223f12 100644 --- a/server/routes/commands.js +++ b/server/routes/commands.js @@ -268,16 +268,35 @@ Custom commands can be created in: tokenUsage.contextWindow ?? 0, ) || 0; - const inputTokensRaw = + const normalizedInputValue = + tokenUsage.inputTokens ?? + tokenUsage.input ?? + tokenUsage.cumulativeInputTokens ?? + tokenUsage.breakdown?.input ?? + tokenUsage.promptTokens; + const directInputTokens = Number( - tokenUsage.inputTokens ?? - tokenUsage.input ?? + normalizedInputValue ?? tokenUsage.input_tokens ?? - tokenUsage.cumulativeInputTokens ?? - tokenUsage.breakdown?.input ?? - tokenUsage.promptTokens ?? + 0 + ) || 0; + const cacheReadTokens = + Number( + tokenUsage.cacheReadTokens ?? + tokenUsage.cache_read_input_tokens ?? + tokenUsage.cacheReadInputTokens ?? 0, ) || 0; + const cacheCreationTokens = + Number( + tokenUsage.cacheCreationTokens ?? + tokenUsage.cache_creation_input_tokens ?? + tokenUsage.cacheCreationInputTokens ?? + 0, + ) || 0; + const inputTokens = normalizedInputValue == null + ? directInputTokens + cacheReadTokens + cacheCreationTokens + : directInputTokens; const outputTokens = Number( tokenUsage.outputTokens ?? @@ -288,8 +307,9 @@ Custom commands can be created in: tokenUsage.completionTokens ?? 0, ) || 0; - const hasTokenBreakdown = inputTokensRaw > 0 || outputTokens > 0; - const used = reportedUsed || inputTokensRaw + outputTokens; + const computedUsed = inputTokens + outputTokens; + const hasTokenBreakdown = computedUsed > 0; + const used = Math.max(reportedUsed, computedUsed); return { type: "builtin", @@ -302,7 +322,7 @@ Custom commands can be created in: ...(hasTokenBreakdown ? { tokenBreakdown: { - input: inputTokensRaw, + input: inputTokens, output: outputTokens, }, }