187d0283a5
- Add support for PNG, JPEG, and WebP image formats in flyer import - Replace external importer service with internal AI-based parsing pipeline - Add new services: TextExtractorService, AiFlyerParserService, FlyerNormalizerService - Integrate Mistral AI, pdf-parse, and tesseract.js dependencies - Add quality confidence indicators and warning panels in Flutter UI - Update package.json with new dependencies and transform ignore patterns - Add documentation for flyer importer system - Add Kilo AI planning file for Happy Island project BREAKING CHANGE: Flyer import now uses internal AI parsing instead of external importer service
126 lines
6.8 KiB
Markdown
126 lines
6.8 KiB
Markdown
# 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.
|