from __future__ import annotations import json import httpx from app.config import Settings from app.extract import extract_receipt from backends.gemma import GemmaLLM from backends.ollama import OllamaLLM from tests.conftest import tiny_jpeg EXTRACT = { "doc_kind": "receipt", "category": "dining", "vendor": "Cafe", "date": "2026-08-21", "tax": 0.5, "total": 8.0, "currency": "USD", "line_items": [], } def _chat_handler(sink: list[dict]): def handler(request: httpx.Request) -> httpx.Response: payload = json.loads(request.content) sink.append(payload) return httpx.Response( 200, json={"choices": [{"message": {"content": json.dumps(EXTRACT)}}]}, ) return handler def test_gemma_sends_image_url(settings: Settings) -> None: sink: list[dict] = [] client = httpx.Client( transport=httpx.MockTransport(_chat_handler(sink)), base_url="http://llm.test/v1", ) llm = GemmaLLM(settings, client=client) extract = extract_receipt( llm, settings=settings, image_jpeg=tiny_jpeg(), ocr_text=None ) assert extract.vendor == "Cafe" content = sink[0]["messages"][1]["content"] assert isinstance(content, list) kinds = {part["type"] for part in content} assert "image_url" in kinds url = next(part["image_url"]["url"] for part in content if part["type"] == "image_url") assert url.startswith("data:image/jpeg;base64,") def test_lightning_never_sends_image(settings: Settings) -> None: settings = settings.model_copy( update={ "llm_backend": "ollama", "llm_model": "nemotron-3.5-lightning", "llm_accepts_images": False, } ) sink: list[dict] = [] client = httpx.Client( transport=httpx.MockTransport(_chat_handler(sink)), base_url="http://llm.test/v1", ) llm = OllamaLLM(settings, client=client) assert llm.accepts_images is False extract = extract_receipt( llm, settings=settings, image_jpeg=tiny_jpeg(), ocr_text="Cafe 8.00" ) assert extract.total is not None content = sink[0]["messages"][1]["content"] assert isinstance(content, str) dumped = json.dumps(sink[0]) assert "image_url" not in dumped assert "data:image" not in dumped