- Cluster namer bills the money channel now: per-step usage + model captured via onStep, persisted as NamingRunUsage records to data/graph/naming-runs.jsonl (idempotent per startedAt; failed/aborted runs persist too — money spent either way). callUsage promoted to extraction.ts module scope for reuse. - core/cost-summary.ts: the pure fold behind the pill — chat runs, notes-source runs, and naming runs into stage totals (extraction / audit / naming) + newest-first recent runs. Dollars INFERRED read-time via prices.ts, never stored; unpriced models excluded-but-counted (render '—', never a silent zero); tokens always sum. - GET /api/graph/costs (src/server graphCosts composition, thin route). - Graph view: bottom-left cost pill (cumulative dollars headline) + expandable breakdown panel (stage rows with tokens secondary, recent runs with time/title/merged/cost); fetched on mount, refetched when a pipeline run finishes; merged into one bottom-left stack with the running-pipeline pills. - Tests: pure-fold suite, naming capture wiring (full importNotes -> naming path), store idempotency + strict reads.
20 lines
762 B
TypeScript
20 lines
762 B
TypeScript
/**
|
|
* GET /api/graph/costs — the graph view's cost pill (2026-09 graph cost
|
|
* metrics wave): what the graph PIPELINE cost, folded read-time from the
|
|
* three usage channels (chat extraction runs, notes-source runs,
|
|
* cluster-naming runs). Thin transport over src/server's graphCosts();
|
|
* the fold itself is pure core (core/cost-summary.ts). Dollars are
|
|
* inferred from PRICES at read time — never stored, so a price sync
|
|
* re-prices the whole history for free.
|
|
*
|
|
* Scope (user-decided): the graph pipeline ONLY — teaching turns and
|
|
* sub-agent jobs have their own cost channels elsewhere.
|
|
*/
|
|
import { graphCosts } from "@/src/server";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function GET() {
|
|
return Response.json(graphCosts());
|
|
}
|