AI cost/ROI page: pricing table, cost per call, Cost tab, ROI endpoint

- templates/web/pricing.py: pricing table with source and as-of date; cost_for_call
  (input rate + (output + thinking) at output rate, long-context above threshold;
  unknown model raises naming the known models).
- Cost tab: coverage pill, total/this-month/tokens tiles, month-by-month table,
  per-run tokens and cost ("not priced", never $0), by-hand panel with editable
  assumptions labelled as such.
- Contract: RoiRun and GET /roi; optional GET /models and POST /runs/{id}/retry.
- SKILL.md: cost page wiring, model picker and retry-on-other-model as optional sections.
- Tests: test_pricing.py self-check, page_check_ai/test_ai_cost.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
VasanthandClaude Opus 5.5 committed 2026-10-16 15:23:52 +05:30
1 parent 8951ac05a5
commit 9dcdd8f572
11 files changed
+392 -6

No files matched your search

@@ -18,6 +18,7 @@ from pathlib import Path
from urllib.parse import urlparse
from bands import BANDS, field_band, needs_review
from pricing import PRICING_AS_OF, PRICING_SOURCE, cost_for_call
HERE = Path(__file__).resolve().parent
API = "/api" # same as API_BASE in index.html
@@ -34,6 +35,7 @@ SAMPLE_FIELDS = [
("Tax", "196.00", [], False),
]
RUNS, ROWS = {}, {}
ROI_RUNS = [] # [{job_id, document, created_at, model, input_tokens, output_tokens, thinking_tokens}]
_next_row = [1]
@@ -56,6 +58,16 @@ def reset():
ROWS.clear()
put_run("job-1002", "invoice-1002.pdf", SAMPLE_FIELDS, status="running", pages_done=1, pages_total=3)
put_run("job-1001", "invoice-1001.pdf", SAMPLE_FIELDS, pages_total=2, pages_done=2)
ROI_RUNS.clear()
now = time.time()
for i, (model, tokens) in enumerate([("gemini-2.5-flash", (21_000, 1_800, 900)),
("gemini-2.5-flash", (18_500, 1_600, 0)),
("gemini-2.5-pro", (240_000, 3_000, 2_500)),
("gemini-2.5-flash-lite", (22_000, 1_700, 0)),
("gemini-2.5-flash", (None, None, None))]): # usage not recorded
ROI_RUNS.append({"job_id": f"job-{900 + i}", "document": f"invoice-{900 + i}.pdf",
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(now - i * 12 * 864e2)),
"model": model, **dict(zip(("input_tokens", "output_tokens", "thinking_tokens"), tokens))})
def run_view(job_id):
@@ -104,6 +116,18 @@ def approve(job_id, body):
return 200, {"ok": True}
def roi(body):
"""Cost per run. total_cost is null when the run recorded no token usage: unpriced, never zero.
An unknown model is an error (500 naming the known models), never a cost of zero."""
try:
runs = [{**r, "total_cost": None if r["input_tokens"] is None else
cost_for_call(r["model"], r["input_tokens"], r["output_tokens"], r["thinking_tokens"] or 0)}
for r in ROI_RUNS]
except ValueError as e:
return 500, {"detail": str(e)}
return 200, {"runs": runs, "pricing": {"source": PRICING_SOURCE, "as_of": PRICING_AS_OF}}
def document(job_id, body):
"""A stand-in for the source document. A real backend returns the PDF or page image."""
lines = "".join(f'<text x="48" y="{150 + i * 46}" font-size="20">{escape(f)}: {escape(v)}</text>'
@@ -122,6 +146,7 @@ ROUTES = [
("PATCH", r"/review/(\d+)", patch_row),
("POST", r"/runs/([\w-]+)/verify-remaining", verify_remaining),
("POST", r"/runs/([\w-]+)/approve", approve),
("GET", r"/roi", roi),
("POST", r"/set-dark", lambda body: (200, {})),
]