Spaces:
Running
A newer version of the Gradio SDK is available: 6.20.0
Build Plan β Ordered Tasks for Claude Code
Execute phases in order. Each task lists what to build and its acceptance criteria. Every phase ends in something runnable or testable. Keep the core decoupled from entry points and backends throughout (see CLAUDE.md).
Legend: [AC] = acceptance criteria.
Phase 0 β Scaffolding
0.1 Project skeleton. Create the repo layout from the setup doc and
initialize the project with uv (uv init). Pin the interpreter to 3.11
(uv python pin 3.11) and set requires-python = ">=3.11" in pyproject.toml.
Add dependencies via uv add (committing uv.lock); add .env.example and
.gitignore (ignore data/, .env, .venv/, caches β but commit
uv.lock and .python-version); add an empty README.md.
[AC] .python-version reads 3.11; uv sync succeeds and
uv run python -c "import sys, doc_agent; assert sys.version_info[:2]==(3,11)"
passes.
0.2 Config loader. Implement config.py using pydantic-settings: load env,
validate combinations (gemini requires key; vision_direct requires multimodal
backend), fail fast with clear messages.
[AC] Unit test: invalid combos raise; valid config parses.
Phase 1 β Contract & validation (pure, no model yet)
1.1 Schema. Implement schema/models.py (Document, LineItem) per the
data spec, with normalization helpers (money β float, date β ISO).
[AC] test_schema.py: valid dicts parse; malformed money/date handled.
1.2 Validation rules. Implement validation/rules.py: hard rules H1βH4,
soft rules S1βS4, monetary epsilon, returning a structured report.
[AC] test_validation.py: reconciling totals pass H2/H3; mismatches fail;
soft failures recorded without forcing review.
1.3 Confidence & routing. Implement routing/score.py: pure
score(data, report, model_signal) -> float and
route(score, report) -> decision, with hard-failure short-circuit.
[AC] test_routing.py: hard failure β review regardless of score;
threshold boundary behaves; missing required fields lower score.
These three modules are fully testable before any model exists.
Phase 2 β Parsing & backends
2.1 Modality detection. parsing/detect.py: map file β native_pdf |
image by extension/MIME.
[AC] Correctly classifies the supported extensions.
2.2 Docling parser. parsing/docling_parser.py: native PDF β text/layout
payload; optionally retain page image.
[AC] A sample text PDF yields non-empty structured text.
2.3 OCR path (optional strategy). parsing/ocr.py: image β text via
PaddleOCR/Tesseract, behind the same payload interface.
[AC] A sample receipt image yields text; absence of the OCR engine degrades
gracefully with a clear error.
2.4 Backend interface + factory. backends/base.py: the
ExtractionBackend protocol, BackendResult, and a factory that builds the
backend from config.
[AC] Factory returns the configured backend; unknown backend β clear error.
2.5 Gemini backend. backends/gemini.py: multimodal call to the free tier;
text for native PDFs, image for scans/photos; schema-constrained JSON output;
bounded retries + timeout; model id from config.
[AC] Given a sample document, returns schema-valid JSON; transient errors
retry then route to review (don't crash).
2.6 Ollama backend. backends/ollama.py: local call with JSON-schema/grammar
constrained decoding; text-in (pairs with OCR path).
[AC] With a local model present, returns schema-valid JSON for a text
payload. Skipped/marked if no local server (documented).
Phase 3 β Core pipeline
3.1 Assemble core. core.py: process_document(path) -> ExtractionResult
chaining detect β acquire β extract β validate β score β route. Pure of
side-effects (no file moves, no DB) β returns a result object.
[AC] test_core_smoke.py: runs end-to-end on a couple of sample files with
a stub backend; returns a populated result with a decision.
3.2 Idempotency. Content-hash helper so the same file isn't reprocessed. [AC] Same file hashed identically across runs.
Phase 4 β Entry points
4.1 Persistence. store/db.py (SQLite append of accepted records) and
store/export.py (CSV export).
[AC] Accepted record persists and appears in CSV; schema columns match.
4.2 Watcher / batch runner. ingest/watcher.py: watch (or poll) inbox/,
call core, persist accepted, move source to processed/ or review/,
per-document try/except with structured logging.
[AC] Dropping a batch (mixed PDFs/scans/photos) processes all; one
deliberately corrupt file routes to review with a logged reason and does not
stop the loop.
4.3 Web demo. web/app.py: Gradio single-upload UI rendering fields,
per-field confidence, validation report, and decision; explicit
"synthetic/public only" notice; stateless.
[AC] Local Gradio URL processes one upload of each modality and displays a
correct, validated result.
Phase 5 β Evaluation
5.1 Dataset loaders. eval/datasets/: scripts to fetch a held-out slice of
SROIE and the labelled invoice JSON set; map gold labels to the schema. No data
committed to git.
[AC] Loader yields (document, gold) pairs for a slice.
5.2 Metrics harness. eval/run_eval.py: run core over a slice; normalize;
compute per-field and per-critical-field precision/recall/F1; compute
auto-accept precision on critical fields; sweep threshold.
[AC] Prints a metrics table; produces the precision/recall trade-off across
thresholds; recommends a CONFIDENCE_THRESHOLD.
5.3 Tune & record. Set CONFIDENCE_THRESHOLD so auto-accept precision on
critical fields β₯ 0.98; record the resulting recall.
[AC] README contains a results table per dataset (precision/recall/F1 +
routing stats) β the portfolio evidence.
Phase 6 β Deploy & document
6.1 Hugging Face Space. Add Space app.py and a requirements.txt for the
Gradio builder, generated from uv (uv export --no-hashes --no-dev -o requirements.txt); set secrets (GEMINI_API_KEY, backend=gemini,
vision_direct). Deploy.
[AC] Public URL processes an uploaded document of each modality.
6.2 README. Quickstart (both modes), the swappable-backend explanation, the results table, the demo URL, and the free-tier/privacy caveats. [AC] A reader can run locally from scratch and understands the design and the evidence.
Build order rationale
Pure logic first (Phases 1) so the hard-to-test parts (validation, routing) are locked down before any model variance enters. Parsing and backends next (Phase 2), then the core that composes them (Phase 3). Entry points (Phase 4) are thin glue added only once the core is proven. Evaluation (Phase 5) sets the one tunable threshold with evidence. Deployment (Phase 6) is last and small because the core was kept host-independent from the start.