refactor(ai): enhance AI trace integration and OCR normalization
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m54s
Test Suite / flutter-quality (push) Failing after 1m29s

- Add FlyerTraceSupplement type for AI trace metadata
- Implement getFlyerTraceSupplements method to fetch trace supplements
- Update AiTraceService to include prompt/rawOutput and counters in flyer traces
- Add persistFlyerTrace method to FlyerImportService for trace persistence
- Enhance AiFlyerParserService to return structured trace data with prompts and retries
- Update FlyerNormalizerService with OCR typo fixes for cheese variants and spröd bakad firre
- Improve Flutter admin panel with selectable text, warnings display, and tooltips
- Add comprehensive tests for AI trace supplements and normalization rules
This commit is contained in:
Nils-Johan Gynther
2026-05-21 19:11:54 +02:00
parent 67a7590525
commit 026323b72a
9 changed files with 681 additions and 67 deletions
@@ -120,7 +120,7 @@ describe('FlyerNormalizerService', () => {
const result = service.normalize(items);
expect(result).toHaveLength(3);
expect(result.map((item) => item.rawName)).toEqual(['Prastost', 'Herrgardsost', 'Greveost']);
expect(result.map((item) => item.rawName)).toEqual(['Prästost', 'Herrgårdsost', 'Greveost']);
expect(result.every((item) => item.brand === 'Arla Ko')).toBe(true);
expect(result.every((item) => item.categoryHint === 'Hårdost')).toBe(true);
expect(result[0].parseReasons).toContain('split_cheese_variants');
@@ -141,5 +141,48 @@ describe('FlyerNormalizerService', () => {
expect(result[0].brand).toBe('Arla Ko');
expect(result[0].categoryHint).toBe('Hårdost');
});
it('fixes known OCR typo for spröd', () => {
const items = [
{
rawName: 'Pröd Bakad Firre',
brand: 'Findus',
},
];
const result = service.normalize(items);
expect(result).toHaveLength(1);
expect(result[0].rawName).toBe('Spröd Bakad Firre');
expect(result[0].normalizedName).toBe('spröd bakad firre');
});
it('does not apply spröd typo fix outside known fish context', () => {
const items = [
{
rawName: 'Pröd tvättmedel',
brand: 'Test',
},
];
const result = service.normalize(items);
expect(result).toHaveLength(1);
expect(result[0].rawName).toBe('Pröd tvättmedel');
});
it('fixes herggårdsost only in cheese context', () => {
const items = [
{
rawName: 'Herggårdsost 31%',
brand: 'Arla Ko',
},
];
const result = service.normalize(items);
expect(result).toHaveLength(1);
expect(result[0].rawName).toContain('Herrgårdsost');
});
});
});