# Plan: Omgjord flyerimport (pdf-parse + tesseract + Mistral Tiny) ## Mål Ersätta nuvarande flyerimportflöde (som idag delegerar till `importer-api`) med en robust pipeline som: 1) extraherar text från flyer-PDF (primärt `pdf-parse`, fallback OCR via Tesseract), 2) skickar normaliserad text till Mistral Tiny, 3) returnerar strikt strukturerad JSON, 4) behåller befintlig matchning/planeringsflöde i backend + Flutter men förbättrar UX kring importresultat och fel. ## Nulägesanalys (projektanpassad) - Backend endpoint finns: `POST /flyer-import/parse` i `backend/src/flyer-import/flyer-import.controller.ts`. - Nuvarande backendlogik i `backend/src/flyer-import/flyer-import.service.ts` anropar extern tjänst via `IMPORTER_SERVICE_URL` (`/api/flyer/parse`). - Flutter har redan komplett flyerflik i `flutter/lib/features/import/presentation/flyer_import_tab.dart`: - filval, importknapp, preview, radrendering med checkboxar, bulk-planering. - Datamodell för sessions/items finns redan i Prisma (`FlyerSession`, `FlyerItem`, `FlyerSelection`) och stödjer parse+match-metadata. - `flyerimporter.md` beskriver rätt riktning men är generisk; projektet behöver NestJS-integration och kompatibilitet med befintliga DTO/Flutter-modeller. ## Föreslagen arkitektur (ersättning av dagens lösning) ### 1) Ny intern parser i recipe-api (NestJS) - Ersätt `parseViaImporter(...)` i `FlyerImportService` med lokal pipeline: - `extractFlyerText(file)` - PDF/text-extraktion via `pdf-parse`. - Fallback OCR via Tesseract för sidor/underlag utan användbar text. - `parseFlyerWithMistral(text)` - Mistral Tiny-anrop med strikt JSON-schema-prompt. - `normalizeFlyerItems(aiJson)` - validering, typkonvertering, enhetsnormalisering, confidence/reasonCodes. - Behåll resten av tjänsten intakt (matchning, sessionpersistens, selections-kompatibilitet). ### 2) AI-kontrakt (strikt JSON) - Introducera explicit schema för AI-svar (intern typ + runtime-validering): - `rawName`, `normalizedName`, `category`, `price`, `priceUnit`, `comparisonPrice`, `comparisonUnit`, `offerText`, `confidence`, `reasonCodes`. - Promptdesign: - svensk flyer-kontext, - tydlig enhets- och prisnormalisering, - "returnera ENDAST JSON" + exempel, - fallback vid saknade fält (`null`, tomma listor). - Robust parsing av modelloutput: - ta bort ev. markdown fences, - fail-fast med tydligt felmeddelande om ogiltigt JSON. ### 3) OCR-strategi - Primärväg: `pdf-parse` (snabb, billig). - OCR-fallback: bara när extraherad text är tom/under tröskel. - Preprocess för OCR (vid behov): sidvis rasterisering + språk `swe` (ev. `swe+eng`). - Timeout/guardrails per steg för att undvika låsta importer. ### 4) API/infra-anpassning - Controller (`flyer-import.controller.ts`): - uppdatera tillåtna MIME-typer så de matchar Flutter-filtyper (PDF + bilder om vi ska stödja bildflyers). - `compose.yml`/env: - gör `IMPORTER_SERVICE_URL` optional eller avveckla för flyerflödet. - säkerställ `MISTRAL_API_KEY` används av `recipe-api` för flyer. - Dokumentation: - uppdatera teknisk beskrivning så flyerimport inte längre kräver extern flyer-parser. ## UX-analys Flutter (nuvarande) och planerade förbättringar ### Nuvarande UX (bra att bygga vidare på) - Enkel 3-stegsinteraktion: välj fil -> importera -> markera/planera. - Förhandsvisning finns och passar arbetsflödet. - Offer-badge + pris/jämförpris + matchvisning ger snabb scanning. ### UX-gap att täppa till i denna implementation - Ingen tydlig visning av parserwarnings från backend (fältet `warnings` finns i modellen). - Ingen kvalitetssignal i UI trots att `parseConfidence/matchConfidence` finns. - Felmeddelanden är relativt råa; saknar råd per feltyp (timeout, ogiltig fil, AI-svar oformaterat). ### Föreslagna UX-förbättringar (inkrementella, kompatibla) 1. Visa `warnings` över resultatlistan i en kompakt varningspanel. 2. Lägg till "kvalitetsindikator" per rad (t.ex. låg/medel/hög) baserat på `parseConfidence` + `matchConfidence`. 3. Lägg till filterchips: `Endast erbjudanden`, `Saknar matchning`, `Låg kvalitet`. 4. Förbättra loading-state med stegnära text ("Extraherar text", "Tolkar med AI", "Matchar produkter"). 5. Felmappning till användarvänliga meddelanden i `showErrorDialog` (teknisk detalj i kopierbar sekundärtext). ## Implementationsplan (ordning) ### Fas A - Backend kärna 1. Lägg till dependencies i `backend/package.json` för PDF/OCR/Mistral-klient. 2. Skapa intern flyer-parser service i `backend/src/flyer-import/` (text extraction + AI parse). 3. Byt `parseViaImporter` till intern implementation i `FlyerImportService`. 4. Lägg till runtime-validering och normalisering av AI-svar. ### Fas B - Kontrakt och robusthet 5. Säkerställ att response-format fortsatt matchar `FlyerImportResponse` (ingen breaking change mot Flutter). 6. Förbättra controller MIME-regler så de stämmer med faktiska stödda format. 7. Lägg till tydliga felkoder/meddelanden för: - tom/oläsbar flyer, - AI-parsefel, - timeout/service unavailable. ### Fas C - Flutter UX på befintlig skärm 8. Visa backend `warnings` i `flyer_import_tab.dart`. 9. Lägg till kvalitetsindikator + minimala filterchips. 10. Förfina loading/feltexter utan att ändra grundlayouten. ### Fas D - Verifiering 11. Backendtester för intern flyer-parser (happy path + fallback + felbanor). 12. Uppdatera/addera Flutter widgettester för warnings/indikator/filter. 13. Manuell E2E: PDF med text, PDF med skannade sidor, bildflyer, trasig fil. ## Filer som sannolikt berörs vid implementation - `backend/src/flyer-import/flyer-import.service.ts` - `backend/src/flyer-import/flyer-import.controller.ts` - `backend/src/flyer-import/dto/flyer-import.response.ts` (endast om extra metadata behövs) - `backend/package.json` - `flutter/lib/features/import/presentation/flyer_import_tab.dart` - Ev. `flutter/lib/features/import/domain/flyer_import_item.dart` (om ny UI-metadata exponeras) - Dokumentation: `TEKNISK_BESKRIVNING.md` (kort uppdatering av arkitektur) ## Risker och mitigering - OCR-prestanda/latens: använd fallback-only och timeout. - Mistral kan ge semistrukturerat svar: strikt schema + robust JSON-sanitizing + validering. - Kostnad/kvot på AI-anrop: minimera promptstorlek, trunkera brus, återanvänd normalisering. - Driftöverraskningar: behåll endpoint-kontrakt oförändrat mot Flutter. ## Acceptance criteria - Flyerimport fungerar utan beroende av extern `/api/flyer/parse` i importer-api. - Minst en PDF med inbäddad text och en skannad PDF importeras framgångsrikt. - Backend returnerar valid `FlyerImportResponse` och befintlig planeringsfunktion fortsätter fungera. - Flutter visar warnings och gör det tydligare vilka rader som behöver manuell granskning. ## Fastställt beslut - Första leveransen ska stödja **PDF + bildfiler** (`png/jpg/webp`) fullt ut.