UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

92 lines 4.04 kB
/** * /usage command * Displays token usage statistics */ import React from 'react'; import { UsageDisplay } from '../components/usage/usage-display.js'; import { getToolManager } from '../message-handler.js'; import { getModelContextLimit, getSessionContextLimit } from '../models/index.js'; import { createTokenizer } from '../tokenization/index.js'; import { calculateTokenBreakdown, calculateToolDefinitionsTokens, } from '../usage/calculator.js'; import { processPromptTemplate } from '../utils/prompt-processor.js'; export const usageCommand = { name: 'usage', description: 'Display token usage statistics', handler: async (_args, messages, metadata) => { const { provider, model, getMessageTokens } = metadata; let tokenizer; let tokenizerName = 'fallback'; try { // Create tokenizer for accurate breakdown tokenizer = createTokenizer(provider, model); tokenizerName = tokenizer.getName(); } catch { // Fallback to a simple tokenizer if creation fails tokenizer = { encode: (text) => Math.ceil((text || '').length / 4), countTokens: (message) => Math.ceil(((message.content || '') + (message.role || '')).length / 4), getName: () => 'fallback', }; tokenizerName = 'fallback (error)'; } // Generate the system prompt to include in token calculation const toolManager = getToolManager(); const systemPrompt = processPromptTemplate(); // Create system message to include in token calculation const systemMessage = { role: 'system', content: systemPrompt, }; // Calculate token breakdown from messages including system prompt // Note: We don't use getMessageTokens for the system message since it's freshly generated // and won't be in the cache. Instead, we use the tokenizer directly for accurate counting. const baseBreakdown = calculateTokenBreakdown([systemMessage, ...messages], tokenizer, message => { try { // For system message, always use tokenizer directly to avoid cache misses if (message.role === 'system') { return tokenizer.countTokens(message); } // For other messages, use cached token counts const tokens = getMessageTokens(message); // Ensure we always return a valid number return typeof tokens === 'number' && !Number.isNaN(tokens) ? tokens : 0; } catch { // Fallback to simple estimation if tokenization fails return Math.ceil(((message.content || '') + (message.role || '')).length / 4); } }); // Clean up tokenizer resources if (tokenizer.free) { tokenizer.free(); } // Calculate tool definitions tokens and create final breakdown (immutable) // Note: Tool definitions are sent separately to the API and add token overhead const toolDefinitions = toolManager ? calculateToolDefinitionsTokens(Object.keys(toolManager.getToolRegistry()).length) : 0; const breakdown = { ...baseBreakdown, toolDefinitions, total: baseBreakdown.total + toolDefinitions, }; // Get context limit: session override takes priority const sessionLimit = getSessionContextLimit(); const contextLimit = sessionLimit ?? (await getModelContextLimit(model)); return React.createElement(UsageDisplay, { key: `usage-${Date.now()}`, provider, model, contextLimit, currentTokens: breakdown.total, breakdown, messages, tokenizerName, getMessageTokens, }); }, }; //# sourceMappingURL=usage.js.map