diff options
| author | Agando <roduccomm@gmail.com> | 2026-06-24 06:01:05 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-06-24 06:01:05 +0200 |
| commit | b08aa5b479395441462effed59ff1ee02d9d8c7f (patch) | |
| tree | 91ff8aeb294b38cc07a63b31b224164aa971cac6 /modules/core/terminal.js | |
| parent | 168a7bc6d50023215f75fa8a0d95362473cfe033 (diff) | |
auto: 2026-06-24_06:01 changes
Diffstat (limited to 'modules/core/terminal.js')
| -rw-r--r-- | modules/core/terminal.js | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/modules/core/terminal.js b/modules/core/terminal.js index 2b41fd3..3abb074 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -1603,6 +1603,61 @@ } return; } + if (cmd === 'usage') { + const om = this.ollamaManager; + if (!om) { this.print('Ollama manager not available', 'error'); return; } + const filters = {}; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--json') { filters._json = true; } + else if (args[i] === '--reset') { + om._usageLog = []; + om._usageBuffer = []; + om._saveUsageLog(); + this.print('Usage log cleared.', 'success'); + return; + } + else if (args[i] === '--model' && args[i+1]) { filters.model = args[++i]; } + else if (args[i] === '--provider' && args[i+1]) { filters.provider = args[++i]; } + else if (args[i] === '--since' && args[i+1]) { filters.since_hours = parseFloat(args[++i]); } + } + const stats = om.getUsageStats(filters); + if (filters._json) { + this.print(JSON.stringify(stats, null, 2), ''); + return; + } + if (stats.total === 0) { + this.print('No usage data recorded yet.', 'info'); + return; + } + const fmt = (n) => n.toLocaleString(); + const timeStr = stats.timeSpan < 60 ? stats.timeSpan + 's' : + stats.timeSpan < 3600 ? Math.round(stats.timeSpan / 60) + 'm' : + Math.round(stats.timeSpan / 3600) + 'h ' + Math.round((stats.timeSpan % 3600) / 60) + 'm'; + this.print('╔══════════════════════════════════════════════════════╗', 'info'); + this.print('║ LLM Usage Stats ║', 'info'); + this.print('╠══════════════════════════════════════════════════════╣', 'info'); + this.print(`║ Total requests: ${fmt(stats.total).padStart(30)} ║`, 'info'); + this.print(`║ Total tokens: ${fmt(stats.totalPrompt + stats.totalCompletion).padStart(30)} ║`, 'info'); + this.print(`║ Input: ${fmt(stats.totalPrompt).padStart(37)} ║`, 'info'); + this.print(`║ Output: ${fmt(stats.totalCompletion).padStart(37)} ║`, 'info'); + this.print(`║ Avg latency: ${(stats.avgLatency / 1000).toFixed(1) + 's'.padStart(30)} ║`, 'info'); + this.print(`║ Time span: ${timeStr.padStart(30)} ║`, 'info'); + this.print('╠══════════════════════════════════════════════════════╣', 'info'); + this.print('║ Per model: ║', 'info'); + for (const [model, data] of Object.entries(stats.byModel).sort((a, b) => b[1].requests - a[1].requests)) { + const label = (model.length > 30 ? model.slice(0, 27) + '...' : model).padEnd(30); + this.print(`║ ${label} ${fmt(data.requests).padStart(4)} req ${fmt(data.tokens).padStart(8)} tok ║`, ''); + } + this.print('╠══════════════════════════════════════════════════════╣', 'info'); + this.print('║ Per provider: ║', 'info'); + for (const [prov, data] of Object.entries(stats.byProvider).sort((a, b) => b[1].requests - a[1].requests)) { + const label = (prov.length > 30 ? prov.slice(0, 27) + '...' : prov).padEnd(30); + this.print(`║ ${label} ${fmt(data.requests).padStart(4)} req ${fmt(data.tokens).padStart(8)} tok ║`, ''); + } + this.print('╚══════════════════════════════════════════════════════╝', 'info'); + this.print('Flags: --json, --model=<name>, --provider=<name>, --since=<hours>, --reset', 'text-muted'); + return; + } if (cmd === 'history') { const entries = this.history; if (!entries.length) { this.print('No command history.', 'info'); return; } |