Skip to content

Repository files navigation

DocProcessor

Extracts structured data (vendor, totals, line items) from invoices, bills, and receipts — PDFs or scanned images — using local LLMs. No cloud API keys: every model runs through Ollama on your own machine/GPU.

Two front ends share the same extraction core:

  • app.py — a Streamlit desktop-style UI. Single process, synchronous, good for quick local use.
  • manage.py / config/ / processor/ — a Django + Channels web app with a background job queue and live progress over WebSockets. Good for longer-running or multi-page jobs where you want to watch progress and cancel mid-run.

Pipeline

file (PDF/image)
   │
   ▼
OpenCV preprocessing   (extractor.py)
   — deskew/denoise scanned images, CLAHE contrast for clean scans
   ▼
Docling text extraction (extractor.py)
   — digital-PDF fast path (no OCR) first
   — falls back to OCR if the page is a scan or came back empty
   — falls back again to rendering the page as a bitmap if Docling
     swallowed a table as an embedded image
   ▼
LLM extraction — one of two pipelines below (Ollama)
   ▼
JSON repair + amount sanitisation
   — 3-tier JSON repair: direct parse → json_repair → bracket-closing
   — comma/period locale correction and ×100 OCR-error correction
   ▼
structured invoice JSON (vendor, totals, line_items[])

Standard pipeline (llm.py)

Single-pass extraction, tuned for a ~40s/page budget:

  • One prompt per page asks for the full schema (header, totals, line items).
  • Multi-page documents split the work: a fast 3B model (FAST_MODEL) pulls the header and totals once, while each page's line items are extracted concurrently.
  • If a page returns no line items (e.g. a dense table the LLM under-extracted), a regex-based fallback (extract_line_items_fallback) detects table rows by pattern and re-asks the LLM in batches of 60 rows.
  • Model chaining: PRIMARY_MODEL (qwen2.5:7b) is tried first; any exception (timeout, bad output, OOM) triggers one automatic fallback to FALLBACK_MODEL (llama3.1:8b) for the rest of the job.

High-accuracy pipeline (llmplus.py)

Used when the user selects "High Accuracy" mode. Trades speed for reliability:

  1. Runs the same extraction 3 times with a larger 40k-char context window and slightly increasing temperature (0.05 → 0.15) for diversity between passes.
  2. Votes across the three candidates — numeric fields take the median, string fields take the majority value, nested objects (vendor/bill-to) take the most complete candidate, and line items take the longest list.
  3. Validates the math: sums line-item totals against tax/shipping/discount and the stated grand total. If they don't reconcile, appends a labelled "reconciliation" line item so the table always ties out, instead of silently showing a wrong total.

Model roster

Model Role
qwen2.5:7b Primary — best structured-output accuracy
qwen2.5:3b Fast — header/totals only
llama3.1:8b Automatic fallback if the primary fails

All models run locally via Ollama; nothing is sent to an external API.

Setup

  1. Install Ollama and pull the models:
    ollama pull qwen2.5:7b
    ollama pull qwen2.5:3b
    ollama pull llama3.1:8b
    
  2. Install Python dependencies:
    pip install -r requirements.txt
    
  3. Sanity-check your environment:
    python verify_env.py
    

Run the Streamlit UI

streamlit run app.py

Run the Django web app

python manage.py migrate
python manage.py runserver

(Uses Daphne/Channels for ASGI + WebSockets, so runserver serves both HTTP and the ws/jobs/ live-progress socket.)

Notes

  • GPU is optional. device can be forced to cpu, cuda, or left on auto detection; tune PAGE_TOKENS_PER_SECOND in llm.py to your own GPU's measured throughput.
  • config/settings.py ships with DEBUG = True and a placeholder SECRET_KEY for local development — set a real secret key and DEBUG = False before deploying anywhere public.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages