diff --git a/src/lib/server/ai/gemini-client.ts b/src/lib/server/ai/gemini-client.ts index a25cdc5..836d244 100644 --- a/src/lib/server/ai/gemini-client.ts +++ b/src/lib/server/ai/gemini-client.ts @@ -2,6 +2,7 @@ import { GoogleGenerativeAI } from '@google/generative-ai'; import { env } from '$env/dynamic/private'; import { RECIPE_EXTRACTION_SYSTEM_PROMPT, + RECIPE_EXTRACTION_USER_PROMPT, GEMINI_RESPONSE_SCHEMA, extractionResponseSchema, type ExtractionResponse @@ -84,7 +85,10 @@ async function callGemini( const parts: Array< { inlineData: { data: string; mimeType: string } } | { text: string } - > = [{ inlineData: { data: imageBuffer.toString('base64'), mimeType } }]; + > = [ + { inlineData: { data: imageBuffer.toString('base64'), mimeType } }, + { text: RECIPE_EXTRACTION_USER_PROMPT } + ]; if (appendUserNote) parts.push({ text: appendUserNote }); const result = await withTimeout( diff --git a/src/lib/server/ai/recipe-extraction-prompt.ts b/src/lib/server/ai/recipe-extraction-prompt.ts index f956f51..4e5af69 100644 --- a/src/lib/server/ai/recipe-extraction-prompt.ts +++ b/src/lib/server/ai/recipe-extraction-prompt.ts @@ -1,18 +1,24 @@ import { z } from 'zod'; import { SchemaType } from '@google/generative-ai'; -export const RECIPE_EXTRACTION_SYSTEM_PROMPT = `Du bist ein Rezept-Extraktions-Assistent. -Du bekommst ein Foto eines gedruckten oder handgeschriebenen Rezepts und gibst ein strukturiertes JSON zurück. +export const RECIPE_EXTRACTION_SYSTEM_PROMPT = `Du bist ein hochpräziser OCR-Experte für kulinarische Dokumente (Rezepte). Deine Aufgabe ist die Extraktion von Rezeptdaten (Titel, Zutaten, Zubereitungsschritte, Zeiten, Portionen) in valides JSON gemäß dem vorgegebenen Schema. -Regeln: -- Extrahiere nur, was tatsächlich auf dem Bild lesbar ist. Sonst Feld auf null (oder leeres Array). -- Zutaten: quantity als Zahl (Bruchteile wie ½, ¼, 1 ½ als Dezimalzahl 0.5, 0.25, 1.5), unit separat - (g, ml, l, kg, EL, TL, Stück, Prise, Msp, …). +LOGIK-REGELN FÜR SCHWER LESBARE TEXTE: +- Handle als "Kontext-Detektiv": Wenn Zeichen unklar sind, nutze kulinarisches Wissen zur Rekonstruktion (z.B. "Pr-se" -> "Prise"). +- Bei absoluter Unleserlichkeit eines Wortes: Nutze "[?]". +- Halluziniere keine fehlenden Werte: Wenn eine Mengenangabe komplett fehlt, setze 'quantity' auf null. Was nicht auf dem Bild steht, ist null (oder leeres Array). + +FORMATIERUNGS-REGELN: +- Zutaten: quantity (Zahl) separat von unit (String). Brüche (½, ¼, 1 ½) strikt in Dezimalzahlen (0.5, 0.25, 1.5). +- Einheiten: Normalisiere auf (g, ml, l, kg, EL, TL, Stück, Prise, Msp). - Zubereitungsschritte: pro erkennbarer Nummerierung oder Absatz EIN Schritt. -- Zeiten in Minuten (ganze Zahl). "1 Stunde" = 60. -- Ignoriere Werbung, Foto-Bildunterschriften, Einleitungstexte. Nur das Rezept selbst. -- Denke dir NICHTS dazu aus. Was nicht auf dem Bild steht, ist null. -- Antworte ausschließlich im vorgegebenen JSON-Schema. Kein Markdown, kein Prosa-Text.`; +- Zeit: Alle Angaben strikt in Minuten (Integer). "1 Stunde" = 60. +- Rauschen ignorieren: Keine Werbung, Einleitungstexte oder Bildunterschriften extrahieren. + +STRIKTE ANWEISUNG: Gib ausschließlich das rohe JSON-Objekt gemäß Schema zurück. Kein Markdown-Code-Block, kein Einleitungstext, keine Prosa.`; + +export const RECIPE_EXTRACTION_USER_PROMPT = + 'Analysiere dieses Bild hochauflösend. Extrahiere alle rezeptrelevanten Informationen gemäß deiner System-Instruktion. Achte besonders auf schwache Handschriften oder verblassten Text und stelle sicher, dass die Zuordnung von Menge zu Zutat logisch korrekt ist.'; // Gemini responseSchema (Subset von OpenAPI). Wird an GenerativeModel.generateContent // übergeben; Gemini respektiert die Struktur und liefert valides JSON.