build: token-monitor v0.1.0 — modular LLM API quota visibility
Implements modular provider probing with two distinct header schemas: - Teams direct (unified schema): 5h/7d utilization floats, status, reset countdown - Shelley proxy (classic schema): token/request counts + Exedev-Gateway-Cost (USD/call) - api-ateam: reports no billing data (confirmed non-existent by recon) Key: uses claude-haiku-4-5-20251001 for minimal probe calls (1 token). Rate-limit headers present on ALL responses (200 and 429). 113/113 tests passing. Built from Face recon (trentuna/a-team#91) — live header capture confirmed unified schema with utilization floats replaces old per-count schema.
This commit is contained in:
parent
760049a25e
commit
07a544c50d
10 changed files with 1093 additions and 1 deletions
128
report.js
Normal file
128
report.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* report.js — human-readable summary generator + severity logic
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute severity for a parsed provider result.
|
||||
* @param {Object} provider
|
||||
* @returns {'critical'|'warning'|'ok'|'unknown'}
|
||||
*/
|
||||
export function getSeverity(provider) {
|
||||
if (provider.type === 'teams-direct') {
|
||||
if (provider.status === 'rejected') return 'critical';
|
||||
if (provider.utilization_7d > 0.85) return 'warning';
|
||||
if (provider.utilization_5h > 0.7) return 'warning';
|
||||
return 'ok';
|
||||
}
|
||||
if (provider.type === 'shelley-proxy') {
|
||||
const tokenPct = 1 - (provider.tokens_remaining / provider.tokens_limit);
|
||||
if (tokenPct > 0.85) return 'warning';
|
||||
return 'ok';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format seconds as "Xh Ym" or "Xm" or "Xs".
|
||||
*/
|
||||
function formatDuration(seconds) {
|
||||
if (seconds == null || isNaN(seconds) || seconds < 0) return '?';
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
if (h > 0) return `${h}h ${m}m`;
|
||||
if (m > 0) return `${m}m`;
|
||||
return `${s}s`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a float as a percentage string.
|
||||
*/
|
||||
function pct(v) {
|
||||
if (v == null) return '?';
|
||||
return `${Math.round(v * 100)}%`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Severity badge string.
|
||||
*/
|
||||
function badge(severity) {
|
||||
switch (severity) {
|
||||
case 'critical': return '[CRITICAL]';
|
||||
case 'warning': return '[WARNING] ';
|
||||
case 'ok': return '[OK] ';
|
||||
default: return '[UNKNOWN] ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a human-readable summary from a full monitor result.
|
||||
* @param {Object} result — { timestamp, providers: { name: {...} } }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateReport(result) {
|
||||
const ts = result.timestamp
|
||||
? result.timestamp.replace('T', ' ').replace(/\.\d+Z$/, ' UTC').replace('Z', ' UTC')
|
||||
: new Date().toUTCString();
|
||||
|
||||
const lines = [];
|
||||
const width = 60;
|
||||
|
||||
lines.push(`Token Monitor — ${ts}`);
|
||||
lines.push('═'.repeat(width));
|
||||
lines.push('');
|
||||
|
||||
const counts = { critical: 0, warning: 0, ok: 0, unknown: 0 };
|
||||
|
||||
for (const [name, p] of Object.entries(result.providers)) {
|
||||
const sev = p.severity || getSeverity(p);
|
||||
counts[sev] = (counts[sev] || 0) + 1;
|
||||
|
||||
const b = badge(sev);
|
||||
let detail = '';
|
||||
|
||||
if (p.type === 'teams-direct') {
|
||||
if (p.status === 'invalid_key') {
|
||||
detail = 'Invalid API key (401)';
|
||||
} else if (p.status === 'rejected') {
|
||||
const resetIn = formatDuration(p.reset_in_seconds);
|
||||
detail = `MAXED — 7d: ${pct(p.utilization_7d)} | resets in ${resetIn}`;
|
||||
} else {
|
||||
detail = `5h: ${pct(p.utilization_5h)} | 7d: ${pct(p.utilization_7d)}`;
|
||||
if (p.reset_in_seconds != null) {
|
||||
detail += ` | resets in ${formatDuration(p.reset_in_seconds)}`;
|
||||
}
|
||||
}
|
||||
} else if (p.type === 'shelley-proxy') {
|
||||
if (p.status === 'error') {
|
||||
detail = `Error: ${p.message || 'unknown'}`;
|
||||
} else {
|
||||
detail = `tokens: ${p.tokens_remaining?.toLocaleString()}/${p.tokens_limit?.toLocaleString()}`;
|
||||
if (p.cost_per_call_usd != null) {
|
||||
detail += ` | cost: $${p.cost_per_call_usd}/call`;
|
||||
}
|
||||
}
|
||||
} else if (p.type === 'api-direct') {
|
||||
detail = p.message || 'No billing data available';
|
||||
} else {
|
||||
detail = p.message || '';
|
||||
}
|
||||
|
||||
// Pad provider name to 14 chars
|
||||
const paddedName = name.padEnd(14);
|
||||
lines.push(`${paddedName} ${b} ${detail}`);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push('─'.repeat(width));
|
||||
|
||||
const parts = [];
|
||||
if (counts.critical) parts.push(`${counts.critical} CRITICAL`);
|
||||
if (counts.warning) parts.push(`${counts.warning} WARNING`);
|
||||
if (counts.ok) parts.push(`${counts.ok} OK`);
|
||||
if (counts.unknown) parts.push(`${counts.unknown} UNKNOWN`);
|
||||
lines.push(`Overall: ${parts.join(', ') || 'no providers'}`);
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue