Initial scaffold: local invoice OCR pipeline with Ollama, classifier branches, structured extraction, review, and Odoo XML export
- 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.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""Shared pytest fixtures."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from odoo_ocr.clients.base import BaseVLMClient
|
||||
from odoo_ocr.config import Settings
|
||||
from odoo_ocr.schemas import VLMResponse
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings() -> Settings:
|
||||
"""Default test settings with cache disabled."""
|
||||
return Settings(
|
||||
ollama_base_url="http://test-ollama:11435",
|
||||
cache=Settings().model_dump()["cache"] | {"enabled": False},
|
||||
)
|
||||
|
||||
|
||||
class FakeVLMClient(BaseVLMClient):
|
||||
"""Test double that returns canned responses based on prompt content."""
|
||||
|
||||
def __init__(self, responses: dict[str, str] | None = None) -> None:
|
||||
super().__init__(Settings())
|
||||
self.responses = responses or {}
|
||||
self.calls: list[dict[str, Any]] = []
|
||||
|
||||
async def complete(
|
||||
self,
|
||||
system_prompt: str,
|
||||
user_prompt: str,
|
||||
images: list[Image.Image],
|
||||
response_format: type[Any] | None = None,
|
||||
temperature: float = 0.1,
|
||||
max_tokens: int = 4096,
|
||||
model: str | None = None,
|
||||
) -> VLMResponse:
|
||||
self.calls.append(
|
||||
{
|
||||
"system_prompt": system_prompt,
|
||||
"user_prompt": user_prompt,
|
||||
"image_count": len(images),
|
||||
"model": model,
|
||||
}
|
||||
)
|
||||
for key, value in self.responses.items():
|
||||
if key in system_prompt or key in user_prompt:
|
||||
return VLMResponse(content=json.dumps(value), model="fake")
|
||||
return VLMResponse(content=json.dumps({}), model="fake")
|
||||
Reference in New Issue
Block a user