fix ki modell

This commit is contained in:
nadja
2026-08-26 20:23:52 +02:00
parent 39b6a16598
commit bd1549449c
4 changed files with 53 additions and 42 deletions

View File

@@ -7,5 +7,10 @@ JWT_SECRET=super-langes-geheimnis-hier-aendern
ADMIN_USER=admin ADMIN_USER=admin
ADMIN_PASS=DEIN-SICHERES-PASSWORT ADMIN_PASS=DEIN-SICHERES-PASSWORT
# Ollama KI-Modell (optional, Standard: mistral) # Groq API (kostenloser Cloud-Zugang zu LLMs)
# OLLAMA_MODEL=mistral # 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

View File

@@ -4,7 +4,7 @@ import {
updateRecipe, deleteRecipe, getCategories, getDb updateRecipe, deleteRecipe, getCategories, getDb
} from '../db.js'; } from '../db.js';
import { getTikTokData } from '../services/tiktok.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'; import { trackEvent } from '../analytics-db.js';
const router = Router(); const router = Router();
@@ -34,8 +34,8 @@ router.get('/authors', (req, res) => {
}); });
router.get('/ai-status', async (req, res) => { router.get('/ai-status', async (req, res) => {
const available = await isOllamaAvailable(); const available = await isAiAvailable();
res.json({ ollama: available }); res.json({ ai: available });
}); });
router.get('/:id', (req, res) => { router.get('/:id', (req, res) => {
@@ -78,29 +78,29 @@ router.post('/from-tiktok', async (req, res) => {
const tiktokData = await getTikTokData(url); const tiktokData = await getTikTokData(url);
const ollamaAvailable = await isOllamaAvailable(); const aiAvailable = await isAiAvailable();
let extracted = null; let extracted = null;
let aiStatus = ollamaAvailable ? 'available' : 'unavailable'; let aiStatus = aiAvailable ? 'available' : 'unavailable';
let aiMessage = ''; let aiMessage = '';
if (ollamaAvailable && tiktokData.title) { if (aiAvailable && tiktokData.title) {
try { try {
extracted = await extractRecipe(tiktokData.title); extracted = await extractRecipe(tiktokData.title);
aiStatus = 'success'; aiStatus = 'success';
} catch (e) { } catch (e) {
console.error('Ollama Extraktion fehlgeschlagen:', e.message); console.error('KI Extraktion fehlgeschlagen:', e.message);
aiStatus = 'error'; aiStatus = 'error';
aiMessage = e.message; aiMessage = e.message;
} }
} else if (!ollamaAvailable) { } else if (!aiAvailable) {
aiMessage = 'Ollama-Server nicht erreichbar'; aiMessage = 'KI-Service nicht erreichbar';
} else if (!tiktokData.title) { } else if (!tiktokData.title) {
aiMessage = 'Kein Titel im Video gefunden'; aiMessage = 'Kein Titel im Video gefunden';
} }
res.json({ res.json({
tiktok: tiktokData, tiktok: tiktokData,
ollama: ollamaAvailable, ai: aiAvailable,
aiStatus, aiStatus,
aiMessage, aiMessage,
extracted, extracted,

View File

@@ -1,11 +1,25 @@
import { SYSTEM_PROMPT } from '../prompts/extractRecipe.js'; import { SYSTEM_PROMPT } from '../prompts/extractRecipe.js';
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434'; const GROQ_API_KEY = process.env.GROQ_API_KEY;
const MODEL = process.env.OLLAMA_MODEL || 'mistral'; 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 { 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; return res.ok;
} catch { } catch {
return false; return false;
@@ -13,28 +27,31 @@ export async function isOllamaAvailable() {
} }
export async function extractRecipe(description) { export async function extractRecipe(description) {
const response = await fetch(`${OLLAMA_URL}/api/generate`, { const response = await fetch(GROQ_URL, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: {
body: JSON.stringify({ 'Content-Type': 'application/json',
model: MODEL, 'Authorization': `Bearer ${GROQ_API_KEY}`,
system: SYSTEM_PROMPT,
prompt: `Extrahiere ein Rezept aus dieser TikTok-Videobeschreibung:\n\n${description}`,
stream: false,
options: {
num_ctx: 2048,
temperature: 0.1,
}, },
body: JSON.stringify({
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) { 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(); const data = await response.json();
let text = data.response.trim(); let text = data.choices[0].message.content.trim();
if (text.startsWith('```')) { if (text.startsWith('```')) {
text = text.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, ''); text = text.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '');

View File

@@ -13,22 +13,11 @@ services:
- PORT=3001 - PORT=3001
- DB_PATH=/app/data/data.json - DB_PATH=/app/data/data.json
- ANALYTICS_PATH=/app/data/analytics.json - ANALYTICS_PATH=/app/data/analytics.json
- OLLAMA_URL=http://ollama:11434 - GROQ_API_KEY=${GROQ_API_KEY}
- OLLAMA_MODEL=${OLLAMA_MODEL:-mistral} - GROQ_MODEL=${GROQ_MODEL:-llama-3.1-8b-instant}
- JWT_SECRET=${JWT_SECRET} - JWT_SECRET=${JWT_SECRET}
- ADMIN_USER=${ADMIN_USER:-admin} - ADMIN_USER=${ADMIN_USER:-admin}
- ADMIN_PASS=${ADMIN_PASS} - ADMIN_PASS=${ADMIN_PASS}
volumes: volumes:
- ./data:/app/data - ./data:/app/data
depends_on:
- ollama
restart: unless-stopped restart: unless-stopped
ollama:
image: ollama/ollama
volumes:
- ollama_data:/root/.ollama
restart: unless-stopped
volumes:
ollama_data: