Graph pipeline cost metrics: capture naming spend, fold all channels, cost pill + panel
- 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.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import type { GraphSnapshot } from "../core/contracts.ts";
|
||||
import type { GraphCostSummary } from "../core/cost-summary.ts";
|
||||
import { graphCostSummary } from "../core/cost-summary.ts";
|
||||
import { graphSnapshot } from "../core/graph.ts";
|
||||
import { stores } from "./stores.ts";
|
||||
|
||||
@@ -11,3 +13,25 @@ import { stores } from "./stores.ts";
|
||||
export function graphView(): GraphSnapshot {
|
||||
return graphSnapshot(stores);
|
||||
}
|
||||
|
||||
/** The graph view's cost surface (GET /api/graph/costs — the 2026-09 cost
|
||||
* pill): fold every usage channel into stage totals + recent runs. A pure
|
||||
* read over the chat records (chat-front-door runs), the notes sources
|
||||
* (notes-front-door runs), and the naming ledger (graph-global runs);
|
||||
* dollars are inferred by the pure core fold (core/cost-summary.ts +
|
||||
* prices.ts) — nothing here stores or caches money. */
|
||||
export function graphCosts(): GraphCostSummary {
|
||||
const chats = stores.chatStore
|
||||
.listChats()
|
||||
.flatMap((summary) => {
|
||||
const record = stores.chatStore.getChat(summary.id);
|
||||
return record?.extractions?.length
|
||||
? [{ title: record.title ?? summary.id, extractions: record.extractions }]
|
||||
: [];
|
||||
});
|
||||
const sources = stores.graphStore
|
||||
.readSources()
|
||||
.filter((s) => s.extractions?.length)
|
||||
.map((s) => ({ filePath: s.filePath, extractions: s.extractions! }));
|
||||
return graphCostSummary({ chats, sources, naming: stores.graphStore.readNamingRuns() });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user