perf: skip Mistral AI for PDF lines that lack numeric value (header/footer/junk)
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-03 22:21:44 +02:00
parent ea006e7fbe
commit 2dc8aa4fb4
@@ -258,6 +258,17 @@ function ruleBasedParseLine(line: string): ParsedReceiptItemRaw | null {
return null;
}
/**
* Avgör om en rad troligen är en produktrad (har namnliknande text OCH ett
* numeriskt värde som tyder på pris eller kvantitet). Rader utan siffror är
* troligen header/footer i PDF:en och behöver inte AI-tolkning.
*/
function looksLikeReceiptProductLine(line: string): boolean {
if (!isLikelyNameLikeText(line)) return false;
// Måste innehålla minst ett tal (pris, vikt, antal)
return /\d/.test(line);
}
@Injectable()
export class ReceiptParsingService {
private readonly logger = new Logger(ReceiptParsingService.name);
@@ -332,10 +343,18 @@ export class ReceiptParsingService {
}
}
this.logger.log(`PDF: ${resolved.length} rader lösta regelbaserat, ${needsAI.length} skickas till AI`);
// Filtrera bort PDF-skräp (butiksnamn, datum, header/footer) innan AI-anrop.
// En rad behöver AI bara om den ser ut som en produktrad (namntext + siffra).
const productLinesForAI = needsAI.filter(looksLikeReceiptProductLine);
const discarded = needsAI.length - productLinesForAI.length;
if (needsAI.length > 0) {
const aiItems = await this.callMistralText(needsAI, apiKey);
this.logger.log(
`PDF: ${resolved.length} regelbaserade, ${productLinesForAI.length} till AI` +
(discarded > 0 ? `, ${discarded} rader kasserade (ej produktrader)` : ''),
);
if (productLinesForAI.length > 0) {
const aiItems = await this.callMistralText(productLinesForAI, apiKey);
resolved.push(...aiItems);
}