feat: Add Fuse.js distillery name normalization

New: src/lib/distillery-matcher.ts
- normalizeDistillery(): Fuzzy matches AI responses against distilleries.json
- cleanBottleName(): Removes distillery from bottle name to avoid duplication
- normalizeWhiskyData(): Combined helper for both operations

Example transformations:
- 'ARDNAHOE DISTILLERY CO LTD' → 'Ardnahoe'
- 'Laphroaig 10 Year Old' → '10 Year Old' (with distillery in separate field)

Integration:
- gemini-vision.ts now normalizes results after AI response
- Enables consistent distillery names for enrichment cache
This commit is contained in:
2025-12-26 22:20:31 +01:00
parent daf6c86633
commit 883b2b61b4
3 changed files with 188 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ import { createClient } from '@/lib/supabase/server';
import { trackApiUsage } from '@/services/track-api-usage';
import { checkCreditBalance, deductCredits } from '@/services/credit-service';
import { getAIProvider, getOpenRouterClient, OPENROUTER_VISION_MODEL, OPENROUTER_PROVIDER_PREFERENCES } from '@/lib/openrouter';
import { normalizeWhiskyData } from '@/lib/distillery-matcher';
// Schema for Gemini Vision extraction
const visionSchema = {
@@ -253,6 +254,23 @@ export async function analyzeLabelWithGemini(imageBase64: string): Promise<Gemin
// Validate with Zod schema
const validatedData = BottleMetadataSchema.parse(result.data);
// ========================================
// NORMALIZE DISTILLERY NAME
// ========================================
const normalized = normalizeWhiskyData(
validatedData.name || '',
validatedData.distillery || ''
);
// Apply normalized values
const finalData = {
...validatedData,
name: normalized.name || validatedData.name,
distillery: normalized.distillery || validatedData.distillery,
};
console.log(`[Vision] Normalized: distillery="${normalized.distillery}", name="${normalized.name}"`);
// Track usage and deduct credits
await trackApiUsage({
userId: user.id,
@@ -264,7 +282,7 @@ export async function analyzeLabelWithGemini(imageBase64: string): Promise<Gemin
return {
success: true,
data: validatedData,
data: finalData,
provider,
perf: {
apiCall: result.apiTime,