42 lines
2.3 KiB
JavaScript
42 lines
2.3 KiB
JavaScript
// Sacred Valley card: AI Usage — Claude Code token usage + local (OpenClaw/Ollama)
|
|
// performance, summarised from the Homelab Monitor via /api/ai-usage.
|
|
import { el, mount } from '../../dom.js';
|
|
import { api } from '../../api.js';
|
|
|
|
let body;
|
|
const fmt = (n) => { n = Number(n) || 0; if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M'; if (n >= 1e3) return (n / 1e3).toFixed(1) + 'k'; return String(n); };
|
|
const dur = (ms) => ms == null ? '—' : (ms >= 1000 ? (ms / 1000).toFixed(1) + 's' : Math.round(ms) + 'ms');
|
|
const mono = (t, color) => el('span', { style: { fontFamily: 'var(--font-mono)', ...(color ? { color } : {}) } }, t);
|
|
|
|
async function load() {
|
|
if (!body) return;
|
|
try {
|
|
const u = await api.get('/api/ai-usage');
|
|
if (!u.ok) { mount(body, el('span', { class: 'muted' }, 'Monitor unreachable (' + (u.url || '') + ')')); return; }
|
|
const c = u.claude, l = u.local;
|
|
mount(body,
|
|
el('div', { class: 'aiu-sec' },
|
|
el('div', { class: 'aiu-h' }, 'Claude Code'),
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'today'), mono(`${fmt(c.today.input)}↑ ${fmt(c.today.output)}↓`)),
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'cache · turns'), mono(`${fmt(c.today.cache)} · ${c.today.turns}`)),
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'week'), mono(`${fmt(c.week.input + c.week.output)} tok`)),
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'top model'), mono((c.top_model || '—').replace(/^claude-/, ''), 'var(--accent)'))
|
|
),
|
|
el('div', { class: 'aiu-sec' },
|
|
el('div', { class: 'aiu-h' }, 'Local · OpenClaw'),
|
|
l.top
|
|
? el('div', {},
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, l.top.model), mono(`${dur(l.top.p50_ms)} p50 · ${dur(l.top.p95_ms)} p95`)),
|
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'runs · err'), mono(`${l.runs} · ${(l.top.error_rate * 100).toFixed(0)}%`)))
|
|
: el('span', { class: 'muted' }, 'No local runs yet')
|
|
),
|
|
el('a', { class: 'aiu-link', href: '#/ai-usage' }, 'Full dashboard ↗')
|
|
);
|
|
} catch { mount(body, el('span', { class: 'muted' }, 'No usage data')); }
|
|
}
|
|
|
|
export default {
|
|
id: 'ai-usage', title: 'AI Usage', size: 'm',
|
|
mount(e) { body = e; load(); }, start() {}, stop() { body = null; }
|
|
};
|