7e706793fa
- Add AGENTS.md and project-specific Zed skills (odoo-ocr-pipeline, odoo-xml-import, local-vlm-client). - Implement Pydantic schemas for documents, invoices, review results, and VLM responses. - Add unified BaseVLMClient with Ollama implementation and llama.cpp stub. - Build pipeline stages: loader, classifier, digital_pdf/scanned_print/handwritten/mixed_unknown branches, extractor, reviewer, xml_builder. - Add CLI entry point with sidecar JSON and confidence-gated XML output. - Include prompts for classifier, OCR, extraction, and review models. - Add tests with FakeVLMClient; pytest, ruff, and mypy all pass.
4.5 KiB
4.5 KiB
name, description
| name | description |
|---|---|
| odoo-ocr-pipeline | Use this skill when implementing the document classifier, OCR branches, structured extraction, or review stages of the odoo_ocr invoice processing pipeline. |
Odoo OCR Pipeline Implementation Skill
Use this skill whenever you are asked to write or modify code for the invoice OCR pipeline in odoo_ocr.
Pipeline Stages
The pipeline has these stages, each as a separate module under src/odoo_ocr/pipeline/:
- Loader (
pipeline/loader.py): converts PDFs and images to normalizedPIL.Image.Imageornumpyarrays. Handle multi-page PDFs by yielding one page at a time. - Classifier (
pipeline/classifier.py): determines the invoice type. Return aDocumentClassenum with values:digital_pdf— native PDF with embedded text.scanned_print— clean printed scan/photo.handwritten— handwritten invoice.mixed_unknown— ambiguous or low-quality input. Implementation should first try fast heuristics (file extension, text layer presence, image features), then fall back to a small VLM classifier if confidence is low.
- Branch routers:
pipeline/digital_pdf.py: extract text and layout withpymupdf/pdfplumber. Preserve bounding boxes for table reconstruction.pipeline/scanned_print.py: preprocess image and call the OCR VLM.pipeline/handwritten.py: preprocess image (binarization, slant correction, line segmentation if needed) and call GLM-OCR / handwriting OCR model.pipeline/mixed_unknown.py: run multiple OCR backends and merge/confidence-rank outputs.
- Extractor (
pipeline/extractor.py): take raw OCR output (text + image) and call a structured-extraction VLM to produce anExtractedInvoicePydantic object. Force JSON output only. - Reviewer (
pipeline/reviewer.py): call a review VLM with the original image and the extracted JSON. Return aReviewResultwithvalid,confidence, andissues.
Branch Routing Logic
# src/odoo_ocr/schemas/document.py
from enum import Enum
class DocumentClass(str, Enum):
digital_pdf = "digital_pdf"
scanned_print = "scanned_print"
handwritten = "handwritten"
mixed_unknown = "mixed_unknown"
Routing decision tree:
- If file is PDF and
pdfplumberextracts meaningful text →digital_pdf. - Else if image and classifier confidence > 0.85:
- handwriting features dominate →
handwritten - otherwise →
scanned_print
- handwriting features dominate →
- Else →
mixed_unknown.
Preprocessing Rules
For scanned/photo inputs:
- Convert to grayscale.
- Resize to a standard DPI (300 DPI preferred; 200 DPI minimum).
- Deskew if skew angle > 0.5°.
- Apply adaptive contrast/clarity; avoid over-blurring.
- For handwriting branch, also:
- Binarize with Otsu or Sauvola.
- Detect and correct slant.
- Optionally segment lines.
Structured Extraction Output
The final extraction must always fit the ExtractedInvoice Pydantic schema defined in src/odoo_ocr/schemas/invoice.py. Required fields include:
vendor_namevendor_address(optional)invoice_numberinvoice_datedue_date(optional)currency(default to settings)payment_terms(optional)line_items: list ofInvoiceLineItemwithdescription,quantity,unit_price,total_price,tax_rate.subtotaltax_totaltotalbank_details/iban(optional)raw_ocr_text(for debugging)
Review Output
The review stage must return:
class ReviewIssue(BaseModel):
field: str
severity: Literal["error", "warning"]
message: str
suggested_value: Any | None
class ReviewResult(BaseModel):
valid: bool
confidence: float # 0.0–1.0
issues: list[ReviewIssue]
corrected_invoice: ExtractedInvoice | None
Confidence thresholds (from config):
confidence >= 0.90: accept and generate XML.0.75 <= confidence < 0.90: generate XML withhuman_review_required=trueflag.confidence < 0.75: do not generate final XML; emit draft sidecar only and flag for human review.
Implementation Guidelines
- Keep each stage stateless; pass a
ProcessingContextPydantic object that holds settings, original path, classified type, and intermediate results. - Every model call goes through the unified model client from
src/odoo_ocr/models/base_vlm.py. - Log each stage with timing and input/output file hashes.
- Add unit tests for each branch with small fixture images/PDFs; mock model calls by default.
- Do not commit large model weights or binary fixtures.