From bd1549449cc2f2dea1c813ed153086eb2abb033b Mon Sep 17 00:00:00 2001 From: nadja Date: Wed, 26 Aug 2026 20:23:52 +0200 Subject: [PATCH] fix ki modell --- .env.example | 9 +++++-- backend/routes/recipes.js | 20 +++++++-------- backend/services/ai.js | 51 ++++++++++++++++++++++++++------------- docker-compose.yml | 15 ++---------- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/.env.example b/.env.example index a6aa602..6b4d85f 100644 --- a/.env.example +++ b/.env.example @@ -7,5 +7,10 @@ JWT_SECRET=super-langes-geheimnis-hier-aendern ADMIN_USER=admin ADMIN_PASS=DEIN-SICHERES-PASSWORT -# Ollama KI-Modell (optional, Standard: mistral) -# OLLAMA_MODEL=mistral +# Groq API (kostenloser Cloud-Zugang zu LLMs) +# 1. Account erstellen: https://console.groq.com +# 2. API Key erstellen und hier eintragen +GROQ_API_KEY=gsk_dein-api-key-hier + +# Modell (optional, Standard: llama-3.1-8b-instant) +# GROQ_MODEL=llama-3.1-8b-instant diff --git a/backend/routes/recipes.js b/backend/routes/recipes.js index ab659c8..d170e5b 100644 --- a/backend/routes/recipes.js +++ b/backend/routes/recipes.js @@ -4,7 +4,7 @@ import { updateRecipe, deleteRecipe, getCategories, getDb } from '../db.js'; import { getTikTokData } from '../services/tiktok.js'; -import { isOllamaAvailable, extractRecipe } from '../services/ai.js'; +import { isAiAvailable, extractRecipe } from '../services/ai.js'; import { trackEvent } from '../analytics-db.js'; const router = Router(); @@ -34,8 +34,8 @@ router.get('/authors', (req, res) => { }); router.get('/ai-status', async (req, res) => { - const available = await isOllamaAvailable(); - res.json({ ollama: available }); + const available = await isAiAvailable(); + res.json({ ai: available }); }); router.get('/:id', (req, res) => { @@ -78,29 +78,29 @@ router.post('/from-tiktok', async (req, res) => { const tiktokData = await getTikTokData(url); - const ollamaAvailable = await isOllamaAvailable(); + const aiAvailable = await isAiAvailable(); let extracted = null; - let aiStatus = ollamaAvailable ? 'available' : 'unavailable'; + let aiStatus = aiAvailable ? 'available' : 'unavailable'; let aiMessage = ''; - if (ollamaAvailable && tiktokData.title) { + if (aiAvailable && tiktokData.title) { try { extracted = await extractRecipe(tiktokData.title); aiStatus = 'success'; } catch (e) { - console.error('Ollama Extraktion fehlgeschlagen:', e.message); + console.error('KI Extraktion fehlgeschlagen:', e.message); aiStatus = 'error'; aiMessage = e.message; } - } else if (!ollamaAvailable) { - aiMessage = 'Ollama-Server nicht erreichbar'; + } else if (!aiAvailable) { + aiMessage = 'KI-Service nicht erreichbar'; } else if (!tiktokData.title) { aiMessage = 'Kein Titel im Video gefunden'; } res.json({ tiktok: tiktokData, - ollama: ollamaAvailable, + ai: aiAvailable, aiStatus, aiMessage, extracted, diff --git a/backend/services/ai.js b/backend/services/ai.js index 826d1f9..bdfc09a 100644 --- a/backend/services/ai.js +++ b/backend/services/ai.js @@ -1,11 +1,25 @@ import { SYSTEM_PROMPT } from '../prompts/extractRecipe.js'; -const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434'; -const MODEL = process.env.OLLAMA_MODEL || 'mistral'; +const GROQ_API_KEY = process.env.GROQ_API_KEY; +const GROQ_MODEL = process.env.GROQ_MODEL || 'llama-3.1-8b-instant'; +const GROQ_URL = 'https://api.groq.com/openai/v1/chat/completions'; -export async function isOllamaAvailable() { +export async function isAiAvailable() { + if (!GROQ_API_KEY) return false; try { - const res = await fetch(`${OLLAMA_URL}/api/tags`, { signal: AbortSignal.timeout(3000) }); + const res = await fetch(GROQ_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${GROQ_API_KEY}`, + }, + body: JSON.stringify({ + model: GROQ_MODEL, + messages: [{ role: 'user', content: 'ping' }], + max_tokens: 1, + }), + signal: AbortSignal.timeout(10000), + }); return res.ok; } catch { return false; @@ -13,28 +27,31 @@ export async function isOllamaAvailable() { } export async function extractRecipe(description) { - const response = await fetch(`${OLLAMA_URL}/api/generate`, { + const response = await fetch(GROQ_URL, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${GROQ_API_KEY}`, + }, body: JSON.stringify({ - model: MODEL, - system: SYSTEM_PROMPT, - prompt: `Extrahiere ein Rezept aus dieser TikTok-Videobeschreibung:\n\n${description}`, - stream: false, - options: { - num_ctx: 2048, - temperature: 0.1, - }, + model: GROQ_MODEL, + messages: [ + { role: 'system', content: SYSTEM_PROMPT }, + { role: 'user', content: `Extrahiere ein Rezept aus dieser TikTok-Videobeschreibung:\n\n${description}` }, + ], + temperature: 0.1, + max_tokens: 2048, }), - signal: AbortSignal.timeout(240000), + signal: AbortSignal.timeout(30000), }); if (!response.ok) { - throw new Error(`Ollama Fehler: ${response.status}`); + const err = await response.text(); + throw new Error(`Groq Fehler: ${response.status} - ${err}`); } const data = await response.json(); - let text = data.response.trim(); + let text = data.choices[0].message.content.trim(); if (text.startsWith('```')) { text = text.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, ''); diff --git a/docker-compose.yml b/docker-compose.yml index a844d74..c15256b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,22 +13,11 @@ services: - PORT=3001 - DB_PATH=/app/data/data.json - ANALYTICS_PATH=/app/data/analytics.json - - OLLAMA_URL=http://ollama:11434 - - OLLAMA_MODEL=${OLLAMA_MODEL:-mistral} + - GROQ_API_KEY=${GROQ_API_KEY} + - GROQ_MODEL=${GROQ_MODEL:-llama-3.1-8b-instant} - JWT_SECRET=${JWT_SECRET} - ADMIN_USER=${ADMIN_USER:-admin} - ADMIN_PASS=${ADMIN_PASS} volumes: - ./data:/app/data - depends_on: - - ollama restart: unless-stopped - - ollama: - image: ollama/ollama - volumes: - - ollama_data:/root/.ollama - restart: unless-stopped - -volumes: - ollama_data: