feat(ai): enhance AI trace warnings and reason codes system
- Added structured warning system with `AdminAiWarning` type in backend and Flutter - Implemented detailed reason descriptors with `FlyerReasonDescriptor` for parse and match operations - Added `legacyWarnings` field to maintain backward compatibility - Enhanced AI trace service to collect and format warnings with item-level context - Updated flyer import services to include detailed reason descriptions in responses - Added Swedish diacritic preservation for cheese variants (Prästost, Herrgårdsost, Grevéost) - Implemented UTF-8 content validation for AI responses - Added new reason code definitions in `reason-codes.ts` - Updated Flutter UI to display structured warnings with severity indicators - Added error report generation and copy functionality in admin panel - Added comprehensive test coverage for new warning system and cheese normalization BREAKING CHANGE: AI trace warnings are now structured objects instead of simple strings
This commit is contained in:
@@ -120,12 +120,28 @@ describe('FlyerNormalizerService', () => {
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result.map((item) => item.rawName)).toEqual(['Prästost', 'Herrgårdsost', 'Greveost']);
|
||||
expect(result.map((item) => item.rawName)).toEqual(['Prästost', 'Herrgårdsost', 'Grevéost']);
|
||||
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');
|
||||
});
|
||||
|
||||
it('normalizes PRAST token to Prästost', () => {
|
||||
const items = [{ rawName: 'PRAST, GREVE', brand: 'ARLA KO' }];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result.map((item) => item.rawName)).toContain('Prästost');
|
||||
});
|
||||
|
||||
it('normalizes GREVE token to Grevéost', () => {
|
||||
const items = [{ rawName: 'GREVE, PRAST', brand: 'ARLA KO' }];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result.map((item) => item.rawName)).toContain('Grevéost');
|
||||
});
|
||||
|
||||
it('keeps single cheese item unsplit but normalizes brand/category', () => {
|
||||
const items = [
|
||||
{
|
||||
@@ -184,5 +200,20 @@ describe('FlyerNormalizerService', () => {
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].rawName).toContain('Herrgårdsost');
|
||||
});
|
||||
|
||||
it('fixes greveost typo in cheese context and preserves é', () => {
|
||||
const items = [
|
||||
{
|
||||
rawName: 'Greveost skivad',
|
||||
brand: 'Arla Ko',
|
||||
},
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].rawName).toContain('Grevéost');
|
||||
expect(result[0].normalizedName).toContain('grevéost');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user