Add the ranked legacy-interface backlog (T01), rename dashboard navigation and user-facing copy to workplan while preserving wire-compat API keys (T02), and activate the retirement workplan with T01/T02 marked done.
281 lines
10 KiB
Markdown
281 lines
10 KiB
Markdown
---
|
|
title: Token Cost
|
|
---
|
|
|
|
```js
|
|
import {apiFetch, pollDelay, waitForVisible} from "./components/config.js";
|
|
import {refCell} from "./components/ref-cell.js";
|
|
const POLL = 60_000;
|
|
```
|
|
|
|
```js
|
|
const evidenceSel = Inputs.radio(
|
|
["Measured only", "Active evidence", "All evidence"],
|
|
{value: "Measured only", label: "Evidence"}
|
|
);
|
|
const sortSel = Inputs.select(
|
|
["Tokens Total", "Event Count"],
|
|
{label: "Sort by"}
|
|
);
|
|
const maxSel = Inputs.select(
|
|
[10, 20, 50, 100, 500],
|
|
{value: 20, label: "Show"}
|
|
);
|
|
display(html`<div style="display:flex;gap:1.5rem;align-items:flex-end;flex-wrap:wrap;margin:0.5rem 0 1.5rem">${evidenceSel}${sortSel}${maxSel}</div>`);
|
|
const evidenceMode = view(evidenceSel);
|
|
const sortOrder = view(sortSel);
|
|
const maxResults = view(maxSel);
|
|
```
|
|
|
|
```js
|
|
function aggregatePath(mode) {
|
|
if (mode === "Measured only") return "/token-events/aggregate/?measurement_kind=measured&include_superseded=false";
|
|
if (mode === "All evidence") return "/token-events/aggregate/?include_superseded=true";
|
|
return "/token-events/aggregate/?include_superseded=false";
|
|
}
|
|
|
|
const tokenState = (async function*() {
|
|
let failures = 0;
|
|
while (true) {
|
|
let aggregate = null, quality = null, ok = false;
|
|
try {
|
|
const [r1, r2] = await Promise.all([
|
|
apiFetch(aggregatePath(evidenceMode)),
|
|
apiFetch("/token-events/quality/"),
|
|
]);
|
|
ok = r1.ok && r2.ok;
|
|
if (ok) {
|
|
aggregate = await r1.json();
|
|
quality = await r2.json();
|
|
}
|
|
} catch {}
|
|
failures = ok ? 0 : failures + 1;
|
|
yield {aggregate, quality, ok, ts: new Date()};
|
|
await waitForVisible(pollDelay({ok, base: POLL, failures}));
|
|
}
|
|
})();
|
|
```
|
|
|
|
```js
|
|
function nameCell(name, fullName) {
|
|
const s = String(name ?? fullName ?? "—");
|
|
const full = String(fullName ?? name ?? "—");
|
|
const el = document.createElement("span");
|
|
el.title = full;
|
|
el.textContent = s.length > 80 ? s.slice(0, 80) + "…" : s;
|
|
return el;
|
|
}
|
|
|
|
function sortRows(rows, sortField) {
|
|
const s = [...rows];
|
|
if (sortField === "Event Count") s.sort((a, b) => (b.event_count || 0) - (a.event_count || 0));
|
|
else s.sort((a, b) => (b.tokens_total || 0) - (a.tokens_total || 0));
|
|
return s;
|
|
}
|
|
|
|
function dictRows(obj, labelKey) {
|
|
return Object.entries(obj ?? {})
|
|
.map(([label, tokens_total]) => ({[labelKey]: label, tokens_total}))
|
|
.sort((a, b) => b.tokens_total - a.tokens_total);
|
|
}
|
|
|
|
function metricRows(quality) {
|
|
if (!quality) return [];
|
|
return [
|
|
{metric: "Measured", value: quality.measured_event_count},
|
|
{metric: "Allocated", value: quality.allocated_event_count},
|
|
{metric: "Estimated", value: quality.estimated_event_count},
|
|
{metric: "Superseded", value: quality.superseded_event_count},
|
|
{metric: "Fallback", value: quality.fallback_event_count},
|
|
{metric: "Unattributed measured", value: quality.unattributed_measured_event_count},
|
|
{metric: "Missing provenance", value: quality.missing_provenance_event_count},
|
|
{metric: "Duplicate sources", value: quality.duplicate_source_count},
|
|
];
|
|
}
|
|
```
|
|
|
|
```js
|
|
const aggregate = tokenState.aggregate ?? {
|
|
tokens_in: 0, tokens_out: 0, tokens_total: 0, event_count: 0,
|
|
by_repo: [], by_workstream: [], by_task: [], by_model: [],
|
|
by_measurement_kind: {}, by_source_provider: {},
|
|
};
|
|
const quality = tokenState.quality ?? null;
|
|
const _ok = tokenState.ok ?? false;
|
|
const _ts = tokenState.ts;
|
|
```
|
|
|
|
# Token Cost
|
|
|
|
```js
|
|
display(html`<div style="font-size:0.8rem;color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">
|
|
● ${_ok ? `Live · ${_ts?.toLocaleTimeString()} · ${aggregate.event_count.toLocaleString()} events · ${aggregate.tokens_total.toLocaleString()} tokens` : "API offline"}
|
|
</div>`);
|
|
```
|
|
|
|
```js
|
|
display(html`<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0.75rem;margin:1rem 0">
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Tokens</div>
|
|
<div style="font-size:1.4rem;font-weight:650">${aggregate.tokens_total.toLocaleString()}</div>
|
|
</div>
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Events</div>
|
|
<div style="font-size:1.4rem;font-weight:650">${aggregate.event_count.toLocaleString()}</div>
|
|
</div>
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Last Event</div>
|
|
<div style="font-size:1rem;font-weight:650">${aggregate.last_event_at ? new Date(aggregate.last_event_at).toLocaleString() : "—"}</div>
|
|
</div>
|
|
<div style="border:1px solid var(--theme-foreground-faint);border-radius:6px;padding:0.75rem">
|
|
<div style="font-size:0.75rem;color:var(--theme-foreground-muted)">Last Ingested</div>
|
|
<div style="font-size:1rem;font-weight:650">${aggregate.last_ingested_at ? new Date(aggregate.last_ingested_at).toLocaleString() : "—"}</div>
|
|
</div>
|
|
</div>`);
|
|
```
|
|
|
|
## By Repo
|
|
|
|
```js
|
|
{
|
|
const sorted = sortRows(aggregate.by_repo ?? [], sortOrder);
|
|
const rows = sorted.slice(0, maxResults);
|
|
if (rows.length === 0) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No token events with repo association yet.</p>`);
|
|
} else {
|
|
display(Plot.plot({
|
|
title: "Token consumption by repo",
|
|
marginLeft: 160,
|
|
width: Math.min(900, width),
|
|
x: {label: "Tokens", tickFormat: "~s"},
|
|
y: {label: null},
|
|
marks: [Plot.barX(rows, {x: "tokens_total", y: "label", fill: "#4e79a7", tip: true})],
|
|
}));
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total", "event_count"],
|
|
header: {_ref: "REF", label: "Repo", tokens_in: "Tokens In", tokens_out: "Tokens Out", tokens_total: "Total", event_count: "Events"},
|
|
format: {
|
|
_ref: (_, i) => refCell(i + 1, "repos", rows[i].label),
|
|
label: d => nameCell(d, d),
|
|
tokens_in: d => d.toLocaleString(),
|
|
tokens_out: d => d.toLocaleString(),
|
|
tokens_total: d => d.toLocaleString(),
|
|
},
|
|
width: {_ref: 50, label: 160, tokens_in: 110, tokens_out: 110, tokens_total: 110, event_count: 80},
|
|
}));
|
|
}
|
|
}
|
|
```
|
|
|
|
## By Workplan
|
|
|
|
```js
|
|
{
|
|
const sorted = sortRows(aggregate.by_workstream ?? [], sortOrder);
|
|
const rows = sorted.slice(0, maxResults);
|
|
if (rows.length === 0) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No workplan data yet.</p>`);
|
|
} else {
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total", "event_count"],
|
|
header: {_ref: "REF", label: "Workplan", tokens_in: "Tokens In", tokens_out: "Tokens Out", tokens_total: "Total", event_count: "Events"},
|
|
format: {
|
|
_ref: (_, i) => refCell(i + 1, "workplans", rows[i].scope_id),
|
|
label: d => nameCell(d, d),
|
|
tokens_in: d => d.toLocaleString(),
|
|
tokens_out: d => d.toLocaleString(),
|
|
tokens_total: d => d.toLocaleString(),
|
|
},
|
|
width: {_ref: 50, label: 240, tokens_in: 110, tokens_out: 110, tokens_total: 110, event_count: 80},
|
|
}));
|
|
}
|
|
}
|
|
```
|
|
|
|
## By Evidence
|
|
|
|
```js
|
|
{
|
|
const kindRows = dictRows(aggregate.by_measurement_kind, "kind");
|
|
const sourceRows = dictRows(aggregate.by_source_provider, "source");
|
|
if (kindRows.length === 0 && sourceRows.length === 0) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No evidence breakdown yet.</p>`);
|
|
} else {
|
|
display(html`<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:1rem">
|
|
<div>${Inputs.table(kindRows, {
|
|
columns: ["kind", "tokens_total"],
|
|
header: {kind: "Kind", tokens_total: "Tokens"},
|
|
format: {tokens_total: d => d.toLocaleString()},
|
|
})}</div>
|
|
<div>${Inputs.table(sourceRows, {
|
|
columns: ["source", "tokens_total"],
|
|
header: {source: "Source", tokens_total: "Tokens"},
|
|
format: {tokens_total: d => d.toLocaleString()},
|
|
})}</div>
|
|
</div>`);
|
|
}
|
|
}
|
|
```
|
|
|
|
## By Model
|
|
|
|
```js
|
|
{
|
|
const rows = (aggregate.by_model ?? []).slice(0, maxResults);
|
|
if (rows.length === 0) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No model data yet.</p>`);
|
|
} else {
|
|
display(Plot.plot({
|
|
title: "Token consumption by model",
|
|
marginLeft: 200,
|
|
width: Math.min(700, width),
|
|
x: {label: "Total tokens", tickFormat: "~s"},
|
|
marks: [Plot.barX(rows, {x: "tokens_total", y: "label", fill: "#59a14f", tip: true})],
|
|
}));
|
|
}
|
|
}
|
|
```
|
|
|
|
## Data Quality
|
|
|
|
```js
|
|
if (!quality) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No quality data yet.</p>`);
|
|
} else {
|
|
display(Inputs.table(metricRows(quality), {
|
|
columns: ["metric", "value"],
|
|
header: {metric: "Signal", value: "Count"},
|
|
format: {value: d => d.toLocaleString()},
|
|
}));
|
|
display(html`<p style="font-size:0.8rem;color:var(--theme-foreground-muted)">
|
|
Codex: ${quality.last_codex_ingested_at ? new Date(quality.last_codex_ingested_at).toLocaleString() : "—"}
|
|
· Claude: ${quality.last_claude_ingested_at ? new Date(quality.last_claude_ingested_at).toLocaleString() : "—"}
|
|
· Reconcile: ${quality.last_reconciliation_at ? new Date(quality.last_reconciliation_at).toLocaleString() : "—"}
|
|
</p>`);
|
|
}
|
|
```
|
|
|
|
## Top Tasks by Tokens
|
|
|
|
```js
|
|
{
|
|
const sorted = sortRows(aggregate.by_task ?? [], sortOrder);
|
|
const rows = sorted.slice(0, maxResults);
|
|
if (rows.length === 0) {
|
|
display(html`<p style="color:var(--theme-foreground-muted)">No task-level data yet.</p>`);
|
|
} else {
|
|
display(Inputs.table(rows.map((r, i) => ({...r, _ref: i})), {
|
|
columns: ["_ref", "label", "tokens_in", "tokens_out", "tokens_total"],
|
|
header: {_ref: "REF", label: "Task", tokens_in: "In", tokens_out: "Out", tokens_total: "Total"},
|
|
format: {
|
|
_ref: (_, i) => refCell(i + 1, "tasks", rows[i].scope_id),
|
|
label: d => nameCell(d, d),
|
|
tokens_in: d => d.toLocaleString(),
|
|
tokens_out: d => d.toLocaleString(),
|
|
tokens_total: d => d.toLocaleString(),
|
|
},
|
|
width: {_ref: 50, label: 260},
|
|
}));
|
|
}
|
|
}
|
|
```
|