Spaces:
Sleeping
Sleeping
File size: 8,361 Bytes
e804596 5d3256d c65024b 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 b6beb2d 5d3256d e804596 5d3256d b6beb2d 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d e804596 5d3256d 1b33ac9 462604d 1b33ac9 5d3256d e804596 5d3256d e804596 5d3256d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | # PROGRESS_FUTURE.md -- Post-Ship Backlog
The project is shipped: full pipeline, unattended watcher, measured eval
(SROIE n=100), live demo on Hugging Face Spaces. Everything in this file is
optional. It exists so any item can be picked up cold -- by you or a future
Claude Code session -- with the same task format as the other ledgers.
**Priority rule:** Tier 1 closes gaps in claims the README already makes.
Tier 2 makes an existing claim demonstrably true. Tier 3 is genuine
improvement with diminishing portfolio return. Work top-down; stopping after
any tier leaves a coherent project.
**Protocol:** same as PROGRESS_TOMORROW.md -- interactive, one task at a time,
review the diff, verify on real documents where a model is touched, one commit
per task, tick the box. Specs in `docs/` remain the source of truth; do not
edit them casually. If an item needs design beyond what the docs cover, write
the design *when the item is next*, not before.
---
## Tier 1 -- Close the measurement gap (do these first)
The README claims three critical fields (`total`, `tax`, `invoice_number`) but
only `total` is measured -- SROIE doesn't label the other two. These finish the
evidence story. The harness, cache, and sweep already exist; each task is one
adapter + one eval run.
- [ ] **F1 -- Wire the CORD adapter** (measures `tax` + line items)
Implement the scaffolded `eval/datasets/cord.py` against
`naver-clova-ix/cord-v2`: parse the `ground_truth` JSON string, map
`gt_parse` per the mapping documented in the scaffold (subtotal, tax, total,
line_items, vendor where present). Labeled fields = only what CORD labels.
Note: CORD receipts are Indonesian -- expect lower text-field accuracy; that
is signal about multilingual behaviour, not a harness bug. Run predict on a
SMALL slice (20) first, then 100. Costs Gemini quota -- run deliberately.
Check: `uv run python -m eval.run_eval predict --dataset cord --limit 20`
then `score --dataset cord` produces tables; tests still offline-green.
Commit: `eval: wire CORD adapter (tax + line-item coverage)`
- [ ] **F2 -- Wire the invoice-JSON adapter** (measures `invoice_number`)
Implement `eval/datasets/invoice_json.py` against
`mychen76/invoices-and-receipts_ocr_v1` (or `GokulRajaR/invoice-ocr-json`
if the shape is cleaner -- probe both, pick one, document why). Map invoice
number, dates, totals, tax per the scaffold. Same small-slice-first rule.
Check: predict (20) + score produce tables including `invoice_number`.
Commit: `eval: wire invoice-JSON adapter (invoice_number coverage)`
- [ ] **F3 -- Update the README results section**
Extend the results table to all three datasets; update the framing to state
which critical fields are measured where; refresh the auto-accept precision
claim if the numbers move it. Keep the honest caveats (confidence ceiling,
slice sizes).
Check: README table covers total/tax/invoice_number with dataset provenance.
Commit: `docs: eval results across SROIE + CORD + invoices`
## Tier 2 -- Make the offline claim true (T4 + T6, deferred from launch)
The swappable-backend design currently has one real backend. These make
"runs fully free, offline, and private" demonstrable rather than aspirational.
- [ ] **F4 -- OCR path** (build plan 2.3; ledger T4)
`src/docfield/parsing/ocr.py` behind the payload interface; wire into
`acquire` for `IMAGE_STRATEGY=ocr_then_text`.
DECISION: try `uv add paddleocr`; if it won't resolve on 3.11, fall back to
`uv add pytesseract` + the Tesseract binary, and record the choice here.
Check: a sample receipt image yields text; `process_document` runs in
`ocr_then_text` mode with the stub backend (no model needed to test the path).
Commit: `phase 2.3: OCR acquire path (ocr_then_text)`
- [ ] **F5 -- Ollama backend** (build plan 2.6; ledger T6)
Requires a local Ollama server + pulled model (e.g. `qwen2.5:7b`).
`src/docfield/backends/ollama.py`: JSON-schema/grammar-constrained
decoding, text-in (pairs with F4), registered in the factory, model id from
config. Mocked unit tests + one manual smoke against the live server.
Check: `EXTRACTION_BACKEND=ollama` + `IMAGE_STRATEGY=ocr_then_text` returns
schema-valid data on a real receipt, fully offline.
Commit: `phase 2.6: ollama backend (local/offline path)`
- [ ] **F6 -- Offline eval comparison** (small, high-signal)
Run the SROIE 20-slice through the Ollama path and add a one-row comparison
to the README (Gemini vs local 7B on the same slice). This is the concrete
payoff of the swappable design: same harness, two backends, honest numbers.
Check: comparison row in README with slice size stated.
Commit: `eval: gemini vs ollama comparison (SROIE-20)`
## Tier 3 -- Genuine improvements, diminishing portfolio returns
Defensible engineering; none changes how the project reads to a reviewer.
Pick by interest, not obligation.
- [ ] **F7 -- Real confidence signal.** Surface a usable model signal
(logprobs where the API exposes them, or k-sample self-consistency voting)
so `CONFIDENCE_THRESHOLD` becomes a live dial; re-run the sweep and update
the README (this would retire the "confidence ceiling" caveat). Design
needed before building: self-consistency multiplies per-document cost by k.
- [ ] **F8 -- Review-queue UI.** A minimal local page over `review/`: show the
document, the extraction, the validation failures; accept-with-edits writes
to the store. Keeps the "not a product" scope -- single user, no auth.
- [ ] **F9 -- Watcher hardening.** Bounded retries with backoff for transient
backend failures, a dead-letter state distinct from review, and a startup
reconciliation pass over files that arrived while the watcher was down.
- [ ] **F10 -- Second document domain.** One new document type (e.g. utility
bills or purchase orders): schema fields, validation rules, a small labeled
eval slice. Proves the architecture generalizes beyond receipts/invoices.
- [ ] **F11 -- Settle the FC-1 monetary-tolerance questions.** One held-out
document (`X51005806696`) was auto-accepted with a `total` that disagrees
with gold, clearing every rule; it is the difference between 100% and 98.4%
auto-accept precision. Written up in full, with the mechanism and a
correction to the first hypothesis about it, in `eval/FINDINGS.md`. Do not
tune a constant against it -- that is a sample of one, and the constant most
people would reach for is not the one that admitted it. Gather evidence
first: (a) how many held-out documents fall inside the *relative* tolerance
but outside the absolute floor, (b) how many SROIE `total` labels record a
pre-tax subtotal rather than the grand total (if common, the fix belongs in
the adapter, not in validation), (c) whether `money_close` should compare in
`Decimal` -- at this residual the float verdict differs from the exact one,
which is a correctness question separate from the tolerance value.
- [x] **F12 -- Backfill the 44 unextracted held-out documents.** Done: all 44
re-predicted via `--retry-errors`, 0 errors, the 317 successful predictions
left byte-identical. The run that
expanded SROIE to the full 361-document test split hit a Gemini monthly
spend cap; 44 held-out documents returned 429 and have no extraction. They
are cached as errors, and predict is idempotent, so a plain re-run **skips
them** -- they need `--overwrite`, scoped to those ids, once quota resets.
Until then held-out is 217 usable of 261 and every held-out figure carries
that hole. The report names it explicitly (Document outcomes block).
## Not doing, and why
Explicit non-goals -- declining these is a design decision, not an omission:
- **Fine-tuning a model.** The project's thesis is engineering *around*
off-the-shelf models; fine-tuning is a different project and would compete
on the one axis (benchmark F1) where purpose-built models win.
- **Multi-tenant / production deployment.** Auth, queues, horizontal scale,
SLAs -- out of scope per requirements section 4; the Space is a demo, not a service.
- **A full review application.** F8 stays a single-user local page; workflow
tooling, audit trails, and roles are product work, not portfolio work.
- **Chasing SROIE/CORD leaderboards.** The datasets are the measuring
instrument, not the objective (see README framing).
|