feat(telemetry): per-1M-token rate annotations + friendly uninitialized-DB note

The cost table now shows the resolved in/out/cached per-1M-token rates
in parens next to each priced model, so an operator can see how a
row's cost was derived. And controller-DB query failures distinguish
"DB reachable but no schema yet" (the normal pre-first-run state,
before the master runs create_all) from a real query error, instead
of surfacing a raw SQL "no such table" dump.
This commit is contained in:
2026-05-19 19:18:23 -04:00
parent 012dabcebe
commit 1058341dd1
3 changed files with 59 additions and 5 deletions
+16 -1
View File
@@ -709,6 +709,13 @@ document.getElementById('cost-group-select').addEventListener('change', () => {
PANES['cost'].refresh();
});
// Format a $/1M-token rate: up to 3 decimals, trailing zeros stripped
// ($5, $0.125, $0.05) so the rates stay readable at a glance.
function fmtRate(n) {
const s = (Number(n) || 0).toFixed(3).replace(/\.?0+$/, '');
return '$' + (s || '0');
}
function renderCost(targetId, data) {
const host = document.getElementById(targetId);
host.innerHTML = '';
@@ -736,6 +743,14 @@ function renderCost(targetId, data) {
el('th', { class: 'num' }, 'cost USD'))));
const tbody = el('tbody');
for (const r of rows) {
// Per-1M-token rates in parens so the operator can see how the
// row's cost was derived. Shown only for priced models — the
// "unpriced" pill already conveys the all-zero case.
const priceAnno = (!byPr && r.priced && r.price)
? el('span', { class: 'muted small', title: 'per 1M tokens' },
` (in ${fmtRate(r.price.in)} · out ${fmtRate(r.price.out)}` +
` · cached ${fmtRate(r.price.cached_in)} /M)`)
: null;
const firstCell = byPr
? el('td', {},
r.pr_number == null
@@ -746,7 +761,7 @@ function renderCost(targetId, data) {
: el('td', {},
el('code', {}, r.model || '_unknown'),
' ',
r.priced ? null : pill('unpriced', 'warn'));
r.priced ? priceAnno : pill('unpriced', 'warn'));
tbody.appendChild(el('tr', {},
firstCell,
byPr ? el('td', { class: 'muted small' },
+24 -4
View File
@@ -190,6 +190,18 @@ def _iso(value: Any) -> str | None:
return value.isoformat() if hasattr(value, "isoformat") else str(value)
def _controller_db_note(exc: Exception) -> str:
"""Friendly note for a controller-DB query failure. A DB that exists
but has no schema yet (the controller master runs create_all on
boot) is the common pre-first-run state distinguish it from a real
query error so the operator isn't shown a raw SQL dump."""
msg = str(exc)
if any(s in msg for s in ("no such table", "does not exist", "UndefinedTable")):
return ("controller DB reachable but not initialized yet — "
"start the controller (its master creates the schema on boot)")
return f"controller DB query failed: {exc}"
# ─── Forgejo API (read-only) ─────────────────────────────────────────────
@@ -646,7 +658,7 @@ def _api_prs(state: str | None) -> dict[str, Any]:
]
except Exception as exc: # noqa: BLE001
return {"rows": [], "states": [],
"note": f"controller DB query failed: {exc}"}
"note": _controller_db_note(exc)}
rows: list[dict[str, Any]] = []
for issue in body:
@@ -1844,12 +1856,20 @@ def _api_cost(days: int, group_by: str = "model") -> dict[str, Any]:
provider, model, prices, in_tok, out_tok, cached,
)
key = _price_key(provider, model)
# Resolved per-1M-token rates (same lookup chain _cost_usd
# uses) so the UI can show how each row's cost was derived.
p = prices.get(key) or prices.get(model) or prices.get("_unknown", {})
rows.append(
{
"model": model, "provider": provider, "n": r["n"],
"tokens_in": in_tok, "tokens_out": out_tok,
"cached": cached, "usd": usd,
"priced": (key in prices or model in prices),
"price": {
"in": p.get("in", 0.0),
"out": p.get("out", 0.0),
"cached_in": p.get("cached_in", p.get("in", 0.0)),
},
}
)
total_usd += usd
@@ -2171,7 +2191,7 @@ def _api_attention() -> dict[str, Any]:
except Exception as exc: # noqa: BLE001
return {
"by_state": [], "reaped_recent": [],
"note": f"controller DB query failed: {exc}",
"note": _controller_db_note(exc),
"instrumentation_pending": True,
}
return {
@@ -2246,7 +2266,7 @@ def _api_workflows(state: str | None) -> dict[str, Any]:
).all()
except Exception as exc: # noqa: BLE001
return {"rows": [], "states": [],
"note": f"controller DB query failed: {exc}",
"note": _controller_db_note(exc),
"instrumentation_pending": True}
return {
"rows": [
@@ -2317,7 +2337,7 @@ def _api_workflow_events(workflow_id: int) -> dict[str, Any]:
).all()
except Exception as exc: # noqa: BLE001
return {"workflow": None, "events": [], "attempts": [],
"note": f"controller DB query failed: {exc}"}
"note": _controller_db_note(exc)}
return {
"workflow": {
"workflow_id": int(wf.workflow_id),
@@ -224,6 +224,25 @@ def test_api_workflows_empty_without_db_url(telemetry, monkeypatch):
assert payload["instrumentation_pending"] is True
def test_api_workflows_uninitialized_db_returns_friendly_note(
telemetry, tmp_path, monkeypatch
):
"""A controller DB that exists but has no schema yet (the controller
has never run create_all) degrades to a friendly note not a raw
SQL 'no such table' dump."""
server, _cache = telemetry
db_path = tmp_path / "empty_controller.sqlite"
db_path.touch() # 0-byte file is a valid empty SQLite DB
monkeypatch.setenv("CLEVERAGENTS_DB_URL", f"sqlite:///{db_path}")
monkeypatch.setattr(server, "_CONTROLLER_ENGINE", None)
monkeypatch.setattr(server, "_CONTROLLER_ENGINE_URL", None)
payload = server._api_workflows(None)
assert payload["rows"] == []
assert payload["instrumentation_pending"] is True
assert "not initialized" in payload["note"]
assert "no such table" not in payload["note"]
def test_api_workflows_lists_seeded_workflows_with_state_summary(controller_db):
"""Seeded workflows round-trip with their attempt counts, and the
state summary aggregates every workflow regardless of the filter."""