diff --git a/src/components/ScanAndTasteFlow.tsx b/src/components/ScanAndTasteFlow.tsx index ba51f70..f9e2993 100644 --- a/src/components/ScanAndTasteFlow.tsx +++ b/src/components/ScanAndTasteFlow.tsx @@ -108,6 +108,9 @@ export default function ScanAndTasteFlow({ isOpen, onClose, imageFile }: ScanAnd const startPrep = performance.now(); if (result.success && result.data) { console.log('[ScanFlow] magicScan success'); + if (result.raw) { + console.log('[ScanFlow] RAW AI RESPONSE:', result.raw); + } setBottleMetadata(result.data); const endPrep = performance.now(); diff --git a/src/lib/ai-prompts.ts b/src/lib/ai-prompts.ts index a17e668..c4f6982 100644 --- a/src/lib/ai-prompts.ts +++ b/src/lib/ai-prompts.ts @@ -1,40 +1,46 @@ export const getSystemPrompt = (availableTags: string, language: string) => ` -You are a sommelier and database clerk for a premium whisky vault. Analyze the bottle image. +TASK: Analyze this whisky bottle image. Return raw JSON. -PART 1: METADATA EXTRACTION -Extract precise metadata. If the image is NOT a whisky bottle or if you are very unsure, set "is_whisky" to false and provide a low "confidence" score. -Use null for any value not visible or not inferable. -Infer the 'category' (e.g., Islay Single Malt Scotch Whisky) based on the Distillery and label details if possible. -IMPORTANT: Extract technical metadata (name, distillery, category) in English. -Provide a "search_string" field for Whiskybase in this format: "site:whiskybase.com [Distillery] [Name] [Vintage/Age]". +STEP 1: IDENTIFICATION (OCR & EXTRACTION) +Extract exact text and details from the label. Look closely for specific dates and codes. +- name: Full whisky name (e.g. "Lagavulin 16 Year Old") +- distillery: Distillery name +- bottler: Independent bottler if applicable +- category: Type (e.g. "Islay Single Malt", "Bourbon") +- abv: Alcohol percentage (number only) +- age: Age statement in years (number only) +- vintage: Vintage year (e.g. "1995") +- distilled_at: Distillation date/year if specified +- bottled_at: Bottling date/year if specified +- batch_info: Cask number, Batch ID, or Bottle number (e.g. "Batch 001", "Cask #402") +- bottleCode: Laser codes etched on glass/label (e.g. "L1234...") +- whiskybaseId: Whiskybase ID if clearly printed (rare, but check) -PART 2: SENSORY ANALYSIS -Based on the identified bottle, select the most appropriate flavor tags. -CONSTRAINT: You must ONLY select tags from the following list. Do NOT invent new tags in the "suggested_tags" field. -LIST: ${availableTags} +STEP 2: SENSORY "MAGIC" (KNOWLEDGE RETRIEVAL) +Use the IDENTIFIED NAME from Step 1 to query your internal knowledge base for the flavor profile. +DO NOT try to "see" the flavor in the pixels. Use your expert knowledge about this specific whisky edition. +- Match flavors strictly against this list: ${availableTags} +- Select top 5-8 matching tags. +- If distinct notes are missing from the list, add 1-2 unique ones to "suggested_custom_tags" (localized in ${language === 'de' ? 'German' : 'English'}). -PART 3: CUSTOM SUGGESTIONS -If you recognize highly dominant notes that are NOT in the list above, provide them in "suggested_custom_tags". -Limit this to 1-2 very unique notes (e.g. "Marshmallow" or "Balsamico"). Do not repeat tags from the system list. -Localize these custom tags in ${language === 'en' ? 'English' : 'German'}. - -Output strictly raw JSON (no markdown, no other text) matching the following schema: +OUTPUT SCHEMA (Strict JSON): { - "name": "Full name of the whisky edition", - "distillery": "Name of the distillery", - "category": "Whisky category (e.g. Islay Single Malt)", - "abv": number (e.g. 43), - "age": number (years, e.g. 16), - "vintage": "Year (e.g. 1995)", - "bottleCode": "Any visible bottle codes (e.g. L-code)", - "whiskybaseId": "Whiskybase ID if clearly visible", - "distilled_at": "Distillation date if visible", - "bottled_at": "Bottling date if visible", - "batch_info": "Batch number or cask info", + "name": "string", + "distillery": "string", + "category": "string", + "abv": number or null, + "age": number or null, + "vintage": "string or null", + "distilled_at": "string or null", + "bottled_at": "string or null", + "batch_info": "string or null", + "bottleCode": "string or null", + "whiskybaseId": "string or null", "is_whisky": boolean, - "confidence": number (0-100), - "suggested_tags": string[], - "suggested_custom_tags": string[], - "search_string": string + "confidence": number, + "suggested_tags": ["tag1", "tag2"], + "suggested_custom_tags": ["custom1"], + "search_string": "site:whiskybase.com [Distillery] [Name] [Vintage/Age]" } + `; diff --git a/src/lib/gemini.ts b/src/lib/gemini.ts index 6c08425..8a8515e 100644 --- a/src/lib/gemini.ts +++ b/src/lib/gemini.ts @@ -5,7 +5,8 @@ const apiKey = process.env.GEMINI_API_KEY!; const genAI = new GoogleGenerativeAI(apiKey); export const geminiModel = genAI.getGenerativeModel({ - model: 'gemini-3-flash-preview', + //model: 'gemini-3-flash-preview', + model: 'gemini-2.5-flash', generationConfig: { responseMimeType: 'application/json', }, diff --git a/src/services/analyze-bottle-mistral.ts b/src/services/analyze-bottle-mistral.ts index e912976..b018803 100644 --- a/src/services/analyze-bottle-mistral.ts +++ b/src/services/analyze-bottle-mistral.ts @@ -115,6 +115,7 @@ export async function analyzeBottleMistral(input: any): Promise { } if (Array.isArray(jsonData)) jsonData = jsonData[0]; + console.log('[Gemini AI] JSON Response:', jsonData); const searchString = jsonData.search_string; delete jsonData.search_string; @@ -149,7 +150,8 @@ export async function analyzeBottle(input: any): Promise { apiDuration: endApi - startApi, parseDuration: endParse - startParse, uploadSize: uploadSize - } + }, + raw: jsonData } as any; } catch (error) { diff --git a/src/services/magic-scan.ts b/src/services/magic-scan.ts index dd07eee..e572f6a 100644 --- a/src/services/magic-scan.ts +++ b/src/services/magic-scan.ts @@ -80,7 +80,8 @@ export async function magicScan(input: any): Promise