feat: Replace Nebius with Pixtral AI for bottle scanning

This commit is contained in:
2025-12-19 21:53:18 +01:00
parent 06891a3619
commit 25b1378794
10 changed files with 184 additions and 81 deletions

125
.aiideas
View File

@@ -1,46 +1,123 @@
3. Timeline & "Flight Recorder" (Reihenfolge-Logik)
Hier ist der Code, um Pixtral Large (das europäische Flaggschiff-Modell von Mistral) direkt in deine Next.js App zu integrieren.
Ziel: Die Geschichte des Abends rekonstruieren. Analyse des Konsumverhaltens.
Feature: Die Timeline-Ansicht
Damit kannst du einen direkten "A/B-Test" gegen Gemini 3 Flash fahren.
1. Vorbereitung
Statt einer einfachen Liste, eine vertikale Zeitstrahl-Ansicht.
Du brauchst das Mistral SDK und einen API Key von console.mistral.ai.
Bash
14:00 Uhr: Start der Session "Whisky Herbst".
npm install @mistralai/mistralai
14:15 Uhr: Glenfiddich 12 (Mild, Start).
Füge deinen Key in die .env ein (nicht NEXT_PUBLIC_!): MISTRAL_API_KEY=dein_key_hier
2. Der Code (Server Action)
15:30 Uhr: Laphroaig Cask Strength (Der Gaumen-Killer).
Erstelle eine neue Datei, z.B. app/actions/scan-mistral.ts.
TypeScript
16:00 Uhr: Auchentoshan (Schmeckt nach nichts mehr, weil Laphroaig davor war).
'use server'
Analyse & Warnungen (Smart Features):
import { Mistral } from '@mistralai/mistralai';
Der "Palette-Checker":
const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });
Wenn der User einen extrem rauchigen Whisky (80ppm) loggt und 10 Minuten später einen milden Lowlander eintragen will.
export async function scanWithPixtral(base64Image: string, mimeType: string) {
// Pixtral braucht das Bild als Data-URL
const dataUrl = `data:${mimeType};base64,${base64Image}`;
Warnung (lustig): "Achtung! Du hast gerade eine Torfbombe getrunken. Warte lieber noch 10 Min oder trink Wasser, sonst schmeckst du den nächsten nicht!"
try {
const chatResponse = await client.chat.complete({
model: 'pixtral-large-latest', // Das beste Modell (Stand Dez 2025)
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: `Du bist ein Whisky-Experte und OCR-Spezialist.
Analysiere dieses Etikett präzise.
Antworte AUSSCHLIESSLICH mit gültigem JSON (kein Markdown, kein Text davor/danach):
{
"distillery": "Name der Brennerei (z.B. Lagavulin)",
"name": "Exakter Name/Edition (z.B. 16 Year Old)",
"vintage": "Jahrgang oder null",
"age": "Alter oder null (z.B. 16)",
"abv": "Alkoholgehalt (z.B. 43%)",
"search_query": "site:whiskybase.com [Brennerei] [Name] [Alter]"
}`
},
{
type: 'image_url',
imageUrl: dataUrl
}
]
}
],
responseFormat: { type: 'json_object' }, // Erzwingt JSON (wichtig!)
temperature: 0.1 // Niedrig = präziser, weniger Halluzinationen
});
ABV-Kurve:
const rawContent = chatResponse.choices?.[0].message.content;
if (!rawContent) throw new Error("Keine Antwort von Pixtral");
Ein Liniendiagramm am Ende der Session: Wie hat sich der Alkoholgehalt entwickelt?
// JSON parsen
return JSON.parse(rawContent as string);
Ideal: Langsamer Anstieg.
} catch (error) {
console.error("Pixtral Error:", error);
return null; // Fallback auslösen
}
}
Gefährlich: Zick-Zack.
3. Der "A/B-Switcher" (So nutzt du beides)
Time-Stamping:
In deiner Haupt-Logik (app/actions/scan.ts) kannst du jetzt einfach umschalten oder Pixtral als Fallback nutzen, wenn Gemini zickt (oder andersrum).
TypeScript
Nutze nicht nur created_at (Upload Zeit), sondern speichere explizit tasted_at.
'use server'
import { scanWithGemini } from './scan-gemini'; // Deine bestehende Funktion
import { scanWithPixtral } from './scan-mistral';
Warum? Wenn du 3 Stunden offline warst und dann online gehst, haben alle 5 Whiskys das gleiche created_at (Upload-Zeitpunkt). Du brauchst den Zeitpunkt, an dem der Button gedrückt wurde (lokale Handy-Zeit).
export async function scanBottle(formData: FormData) {
// ... Bild zu Base64 konvertieren ...
const base64 = "...";
const mime = "image/jpeg";
Zusammenfassung für die Session-Logik:
// STRATEGIE A: Der "Qualitäts-Check"
// Wir nutzen standardmäßig Gemini, aber Pixtral als EU-Option
let result;
const useEuModel = process.env.USE_EU_MODEL === 'true'; // Schalter in .env
Das Datenmodell muss wissen:
if (useEuModel) {
console.log("🇪🇺 Nutze Pixtral (Mistral AI)...");
result = await scanWithPixtral(base64, mime);
} else {
console.log("🇺🇸 Nutze Gemini 3 Flash...");
result = await scanWithGemini(base64, mime);
}
session_start (Zeitstempel)
// Wenn das erste Modell versagt (null zurückgibt), versuche das andere
if (!result) {
console.log("⚠️ Erster Versuch fehlgeschlagen, starte Fallback...");
result = useEuModel
? await scanWithGemini(base64, mime)
: await scanWithPixtral(base64, mime);
}
session_end (Zeitstempel)
// ... weiter mit Supabase Caching & Brave Search ...
return result;
}
Innerhalb der Session: Relative Zeit ("Dram Nr. 3, +45min nach Start").
Pixtral vs. Gemini 3 Flash (Dein Check)
Achte beim Testen auf diese Feinheiten:
Helle Schrift auf dunklem Grund: Hier ist Gemini oft aggressiver und liest besser. Pixtral ist manchmal vorsichtiger.
Schreibschrift (Signatory Vintage Abfüllungen): Pixtral Large ist hier erstaunlich gut, fast besser als Gemini, da es Handschrift extrem gut kann.
JSON-Struktur: Dank responseFormat: { type: 'json_object' } sollten beide Modelle sehr sauberen Code liefern.
Wenn Pixtral Large für dich ähnlich gut funktioniert wie Gemini 3 Flash, hast du den großen Vorteil: Daten bleiben in Europa (Server in Frankreich/EU). Das ist ein starkes Marketing-Argument ("We love Whisky & Privacy").