Files
Rpa_Skills/rpa-dashboard-ai/templates/contract.md
T
VasanthandClaude Opus 5.5 9dcdd8f572 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>
2026-10-16 15:23:52 +05:30

72 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dashboard data contract (AI)
Everything in the no-AI contract (`../rpa-dashboard-no-ai/templates/contract.md`)
applies unchanged: the `{event, data}` envelope, its events, its actions and its rules.
The AI dashboard adds extraction runs, review rows and the approve gate. These shapes
come from the Superbill prototype, trimmed.
## Shapes
```
Run { job_id, document, status: pending|queued|running|done|failed, model,
pages_done, pages_total, summary: { green, amber, red, blank, unscored } | null,
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)
| Method and path | What it does |
|---|---|
| `GET /runs` | `{runs: [Run]}`. The page opens the first one. |
| `GET /runs/{job_id}` | One `Run`. |
| `GET /runs/{job_id}/review` | `{rows: [ReviewRow]}`. Empty until the run is `done`. |
| `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
- **Bands** come from `templates/web/bands.py` and nowhere else: green ≥0.95, amber
0.90–0.95, red <0.90. A field takes its worst word score. A blank mandatory field
is `blank`, whatever its score. No scores is `unscored`.
- **Flagged** = every band but green. Each flagged row must be verified before approval.
- **The server enforces the gate.** The page disables nothing that matters for safety:
it asks, and shows the server's refusal.
- **Drafts:** the page sends nothing while a reviewer types. Save sends one PATCH per
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.
`templates/web/server_stub.py` implements all of this on sample data, standard library
only. A real backend serves the same endpoints with the same rules.