diff --git a/src/cli/index.js b/src/cli/index.js index 7d4a8a1..7e4c22f 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -9,6 +9,7 @@ const readline = require('readline'); const { ensureConfig } = require('../config'); const { createState } = require('../core/state'); const { buildSystemPrompt } = require('../core/context'); +const { getModelByKey } = require('../config'); const { streamChat } = require('../model/client'); const { executeTool } = require('../tools/dispatcher'); const { openCommandMenu, hasCommandMatch } = require('../ui/command_menu'); @@ -17,7 +18,7 @@ const { Spinner, truncateThinking } = require('../ui/spinner'); const { renderBanner } = require('../ui/banner'); const { buildStartLine, buildFinalLine, startToolDisplay, formatResultLines, printResultLines } = require('../ui/tool_display'); const { createConversation, updateConversation } = require('../storage/conversation_store'); -const { applyUsage, normalizeTokenUsage } = require('../utils/token_usage'); +const { applyUsage, normalizeTokenUsage, normalizeUsagePayload } = require('../utils/token_usage'); const { gray, cyan, green, red, blue } = require('../utils/colors'); const { createIndentedWriter } = require('../ui/indented_writer'); const { createStatusBar } = require('../ui/status_bar'); @@ -264,7 +265,10 @@ initReadline(); statusBar = createStatusBar({ getTokens: () => normalizeTokenUsage(state.tokenUsage).total || 0, - maxTokens: '256k', + getMaxTokens: () => { + const model = getModelByKey(config, state.modelKey); + return model && Number.isFinite(model.max_context) ? model.max_context : null; + }, }); statusBar.render(); @@ -657,13 +661,16 @@ async function runAssistantLoop() { abortSignal: streamController.signal, })) { const choice = chunk.choices && chunk.choices[0]; - if (!choice) continue; - const usage = (choice && (choice.usage || choice.delta?.usage)) || chunk.usage; - if (usage) { - if (Number.isFinite(usage.prompt_tokens)) usagePrompt = usage.prompt_tokens; - if (Number.isFinite(usage.completion_tokens)) usageCompletion = usage.completion_tokens; - if (Number.isFinite(usage.total_tokens)) usageTotal = usage.total_tokens; + const usage = (chunk && chunk.usage) + || (choice && choice.usage) + || (choice && choice.delta && choice.delta.usage); + const normalizedUsage = normalizeUsagePayload(usage); + if (normalizedUsage) { + if (Number.isFinite(normalizedUsage.prompt_tokens)) usagePrompt = normalizedUsage.prompt_tokens; + if (Number.isFinite(normalizedUsage.completion_tokens)) usageCompletion = normalizedUsage.completion_tokens; + if (Number.isFinite(normalizedUsage.total_tokens)) usageTotal = normalizedUsage.total_tokens; } + if (!choice) continue; const delta = choice.delta || {}; if (delta.reasoning_content || delta.reasoning_details || choice.reasoning_details) { diff --git a/src/ui/status_bar.js b/src/ui/status_bar.js index 009824f..cb23335 100644 --- a/src/ui/status_bar.js +++ b/src/ui/status_bar.js @@ -11,7 +11,7 @@ function truncateNoEllipsis(text, maxCols) { return out; } -function createStatusBar({ getTokens, maxTokens }) { +function createStatusBar({ getTokens, maxTokens, getMaxTokens }) { let mode = 'input'; let enabled = true; let scrollApplied = false; @@ -30,8 +30,10 @@ function createStatusBar({ getTokens, maxTokens }) { const k = Math.round((num / 1000) * 10) / 10; return `${k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)}k`; }; - const right = `当前上下文 ${formatCount(total)}/${maxTokens}`; - const rightTail = ` ${formatCount(total)}/${maxTokens}`; + const maxValue = typeof getMaxTokens === 'function' ? getMaxTokens() : maxTokens; + const maxText = typeof maxValue === 'number' ? formatCount(maxValue) : (maxValue || '?'); + const right = `当前上下文 ${formatCount(total)}/${maxText}`; + const rightTail = ` ${formatCount(total)}/${maxText}`; const leftWidth = visibleWidth(left); const rightWidth = visibleWidth(right); const usableCols = Math.max(1, cols - 1);