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:
1 parent
8951ac05a5
commit
9dcdd8f572
11 files changed
+392
-6
No files matched your search
@@ -13,11 +13,16 @@ Run { job_id, document, status: pending|queued|running|done|failed, model,
|
||||
approved, error }
|
||||
ReviewRow { row_id, field, value, band: green|amber|red|blank|unscored,
|
||||
score, reason, verified, corrected_value }
|
||||
RoiRun { job_id, document, created_at, model, input_tokens, output_tokens,
|
||||
thinking_tokens, total_cost }
|
||||
```
|
||||
|
||||
- `summary` counts the run's rows per band. It is `null` until the run is `done`.
|
||||
- `score` is the field's worst word score, or `null` when it has none.
|
||||
- `corrected_value` is `null` until a reviewer saves a value different from `value`.
|
||||
- `created_at` is ISO 8601 in UTC. Months on the cost page are UTC months.
|
||||
- `RoiRun` token counts and `total_cost` (USD) are `null` when the run recorded no token
|
||||
usage. The page shows it as "not priced" and counts it in the coverage pill, never as $0.
|
||||
|
||||
## Endpoints (web: `{API_BASE}` + path, JSON in and out)
|
||||
|
||||
@@ -29,8 +34,16 @@ ReviewRow { row_id, field, value, band: green|amber|red|blank|unscored,
|
||||
| `GET /runs/{job_id}/document` | The source document (PDF or image) shown beside the fields. |
|
||||
| `PATCH /review/{row_id}` | Body `{corrected_value?, verified}`. Patches one row, returns it. |
|
||||
| `POST /runs/{job_id}/verify-remaining` | Verifies every flagged row not yet verified. Returns `{verified: n}`. |
|
||||
| `GET /roi` | **Fetch ROI.** `{runs: [RoiRun], pricing: {source, as_of}}`, every run, newest first. The page sums spend, groups months and computes the by-hand panel itself. |
|
||||
| `POST /runs/{job_id}/approve` | **The approve gate.** `409 {detail}` while the run isn't done or any flagged row is unverified, else `200 {ok: true}`. |
|
||||
|
||||
Optional, only when the user chose those sections:
|
||||
|
||||
| Method and path | What it does |
|
||||
|---|---|
|
||||
| `GET /models` | `{models: [name]}`: the keys of the pricing table, so only priced models can be picked. |
|
||||
| `POST /runs/{job_id}/retry` | Body `{model}`. Re-queues a `failed` run on that model, returns the `Run`. |
|
||||
|
||||
Errors answer with a non-2xx status and `{detail: "<why>"}`. The page shows `detail`.
|
||||
|
||||
## Rules
|
||||
@@ -45,6 +58,12 @@ Errors answer with a non-2xx status and `{detail: "<why>"}`. The page shows `det
|
||||
edited row (with `verified: true`). Leaving with unsaved edits asks first.
|
||||
- **Polling:** while a run is `queued` or `running`, the page polls `GET /runs/{job_id}`
|
||||
every 1.5s and shows "page X of Y". It stops when the run settles.
|
||||
- **Cost** comes from `templates/web/pricing.py` and nowhere else: per million tokens,
|
||||
input × input rate + (output + thinking) × output rate, long-context rates above the
|
||||
model's threshold. An unknown model is an error naming the known models, never $0.
|
||||
The table records its source URL and as-of date; `GET /roi` returns both.
|
||||
- **Assumptions** (minutes per document, hourly rate) live only on the page, typed by the
|
||||
reader and labelled as assumptions. The backend never stores or guesses them.
|
||||
- **Caveat:** the legend always shows that colour reflects print clarity, not
|
||||
correctness.
|
||||
|
||||
|
||||
Reference in new issue
Block a user