# Plan för vidareutveckling av Microservice Importer ## Dokumentstatus (2026-05-03) Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. ### Senast avklarat i angränsande flöden - Regelbaserad kvittotolkning har stärkts för multipack/enheter och svårare radformat. - Bröd-/rostbrödklassning har utökade guardrails för att minska felaktig kategorisering. - Klientens granskningsflöde och sessionpersistens i Flutter är implementerat, vilket minskar avbrott mellan parse och spara. - Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis, och potatis samt justerad AI-guardrail. - Testinfrastruktur: parametriserade enhetstester för kvittoimport (18 testfall) och CI/CD-pipeline med automatiserad testkörning på push. - **PDF-parsning stabiliserad:** `pdf-parse` använder nu `require()` för CJS-kompatibilitet; `pdfjs-dist/legacy/build/pdf.js` används som fallback för att undvika `DOMMatrix`-fel i Node.js-miljö. - **Retry-logik förbättrad:** Mistral-anrop vid 429/503 väntar nu `3000 * attempt` ms (3s, 6s, 9s) i stället för fast 1s. - **Reproducerbart bygge:** `package-lock.json` är nu spårat i repot; Dockerfile använder `npm ci`. - **AI-optimering implementerad:** `looksLikeReceiptProductLine()` filtrerar bort PDF-rader utan siffra (header/footer/butiksinformation) innan Mistral-anrop. Minskar drastiskt antal onödiga AI-anrop vid kvittoimport. ## Status (2026-05-03) — Driftsatt och integrerad med recipe-app `microservice-importer` körs som intern tjänst (`importer-api`) i `recipe-app/compose.yml`. Alla importflöden är delegerade och driftsatta. | Endpoint | Funktion | Status | |---|---|---| | `POST /api/quick-import` | URL-skrapning (ICA, generisk), PDF, OCR-bild | ✅ Driftsatt | | `POST /api/recipes/parse-markdown` | Markdown → ingrediensstruktur (utan DB) | ✅ Driftsatt | | `POST /api/receipt-import/parse` | Kvittobild/PDF → `ParsedReceiptItem[]` via Mistral AI | ✅ Driftsatt | | `GET /api/health` | Hälsokontroll (används av Docker healthcheck) | ✅ Driftsatt | **Serverstruktur:** ``` /opt/containers/ microservice-importer/ ← klonas och pullas separat recipe-app/ compose.yml ← bygger importer-api från ../microservice-importer deploy.sh ``` **Deploy:** ```bash cd /opt/containers/microservice-importer && git pull cd /opt/containers/recipe-app && git pull && ./deploy.sh ``` --- ## Nästa steg ### Hög prioritet - **Kvittoimport Fas 6b** — Granskningssteg och bulk-spara i Flutter-klienten (backend-logiken är klar) ### Medel prioritet - **Fler webbplats-parsers** — Specifika parsers för t.ex. Tasteline, Köket.se, Arla - **Swagger/OpenAPI** — Automatisk API-dokumentation via `@nestjs/swagger` - **Testtäckning** — Utökad enhetstesttäckning för parsers och `receipt-parsing.service.ts` (18 testfall för kvittoimport) ### Låg prioritet / Framtida - **Caching** — Cacha skrapade sidor för att minska belastning på externa webbplatser - **Puppeteer** — Hantera JavaScript-renderade receptsidor - **Word-dokument** — Stöd för `.docx`-import --- ## AI-optimering: Mistral-modell och pipeline **Nuläge:** `mistral-small-2603` används för bildinput (vision). För PDF-flödet extraheras text först via `pdf-parse`/`pdfjs-dist`; sedan regelbaserad parsning; sedan AI enbart för återstående rader. ### Implementerad optimering: AI sist i pipeline (PDF) Kvittopipelinen för PDF ser nu ut: ``` PDF → pdf-parse / pdfjs-dist → preprocessPdfLines → isIgnoredReceiptLine → ruleBasedParseLine → looksLikeReceiptProductLine → AI ``` **`looksLikeReceiptProductLine(line)`** filtrerar bort rader som saknar siffra (butiksnamn, datum, välkomsttext m.m.) innan Mistral-anropet. Enbart rader med namnliknande text OCH minst ett tal skickas till AI. **Fördelar:** - Regelbaserad parsning hanterar standardfall gratis och snabbt (t.ex. "MJÖLK 1,5L", "BLANDFÄRS 997G") - AI anropas bara för rader som regelverket inte kan tolka entydigt - Möjlighet att använda en mindre/billigare modell för enklare tolkningsuppgifter ### Modellval för olika deluppgifter | Uppgift | Rekommenderad modell | Motivering | |---|---|---| | Kvittoparsning (hela bilden, nuläge) | `mistral-small-2603` | Vision-förmåga krävs för bild-input | | Tolka OCR-text (textbaserad input) | `mistral-small-latest` eller mindre | Enklare uppgift när text redan extraherats | | Kategorisering av enskild produktrad | `open-mistral-nemo` (7B) | Klassificering, ej vision — kan vara mycket liten | **OBS:** För bild-input (JPEG/PNG/HEIC/WebP) krävs alltid en vision-kapabel modell. Optimering med mindre modell är bara möjlig när Tesseract/pdf-parse redan har extraherat text. --- ## Framtida förbättringar ### Schemalagd Uppdatering av Kategorier - **Mål:** Implementera en schemalagd uppdatering av kategorierna en gång i veckan för att säkerställa att cachen alltid är uppdaterad. - **Metod:** Använda `cron` för att schemalägga ett anrop till `POST /receipt-import/refresh-categories` en gång i veckan. --- ## Nuvarande Implementering ### Manuell Uppdatering av Kategorier - **Mål:** Låta användaren manuellt uppdatera kategorierna via Flutter-UI. - **Implementering:** - En knapp i Flutter-UI:n som låter användaren trigga uppdateringen. - Anropa `POST /receipt-import/refresh-categories` från Flutter-UI:n när användaren klickar på knappen. ```dart // Exempel på hur du kan anropa endpointen från Flutter Future refreshCategories() async { final response = await http.post( Uri.parse('http://YOUR_API_URL/receipt-import/refresh-categories'), headers: {'Authorization': 'Bearer YOUR_JWT_TOKEN'}, ); if (response.statusCode == 200) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Kategorier har uppdaterats.')), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Misslyckades med att uppdatera kategorier.')), ); } } ``` --- ## Arkitektur-noteringar - Tjänsten är **helt stateless** — ingen databas, ingen session - Kommunicerar **aldrig direkt** med internet-klienter — exponeras bara på `recipe-internal`-nätverket - `MISTRAL_API_KEY` injiceras via env (samma nyckel som `recipe-api` använder) - Alpine Docker-image: systempaket `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng` installerade via `apk` - Host-port 3001 är upptagen av `wetty` på servern — `importer-api` exponeras aldrig till host