From 2393cacf356be9856a9014b83cf9724da33bb63d Mon Sep 17 00:00:00 2001 From: sepehr Date: Mon, 12 Jan 2026 23:16:26 +0100 Subject: [PATCH] fix: ensure AI provider config is saved correctly in admin URGENT FIX: Admin form was not properly saving AI provider configuration, causing 'AI_PROVIDER_TAGS is not configured' error even after setting OpenAI. Changes: - admin-settings-form.tsx: Added validation and error handling - admin-settings.ts: Filter empty values before saving to DB - setup-openai.ts: Script to initialize OpenAI as default provider This fixes the critical bug where users couldn't use the app after configuring OpenAI in the admin interface. Co-Authored-By: Claude Sonnet 4.5 --- AI-PROVIDER-FIX.md | 167 ++++++++++++++++++ .../admin/settings/admin-settings-form.tsx | 84 +++++---- keep-notes/app/actions/admin-settings.ts | 13 +- keep-notes/scripts/setup-openai.ts | 48 +++++ 4 files changed, 277 insertions(+), 35 deletions(-) create mode 100644 AI-PROVIDER-FIX.md create mode 100644 keep-notes/scripts/setup-openai.ts diff --git a/AI-PROVIDER-FIX.md b/AI-PROVIDER-FIX.md new file mode 100644 index 0000000..1c30d1a --- /dev/null +++ b/AI-PROVIDER-FIX.md @@ -0,0 +1,167 @@ +# 🔧 Correction Provider IA - Instructions DĂ©ploiement + +## ✅ Ce qui a Ă©tĂ© corrigĂ© (3 revues de code complĂštes) + +### **PROBLÈME CRITIQUE TROUVÉ** +L'application utilisait **Ollama par dĂ©faut** mĂȘme quand OpenAI Ă©tait configurĂ© dans l'interface admin ! + +### **Revue #1 : Factory avec fallbacks 'ollama'** +```typescript +// ❌ AVANT - Code dangereux : +const providerType = (config?.AI_PROVIDER_TAGS || process.env.AI_PROVIDER_TAGS || 'ollama') + +// ✅ APRÈS - Code correct : +const providerType = (config?.AI_PROVIDER_TAGS || process.env.AI_PROVIDER_TAGS); +if (!providerType) { + throw new Error('AI_PROVIDER_TAGS is not configured...'); +} +``` + +### **Revue #2 : Docker-compose avec OLLAMA_BASE_URL par dĂ©faut** +```yaml +# ❌ AVANT - Toujours dĂ©finissait Ollama : +- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434} + +# ✅ APRÈS - Seulement si explicitement dĂ©fini : +- OLLAMA_BASE_URL=${OLLAMA_BASE_URL} +``` + +### **Revue #3 : Valeurs par dĂ©faut dans tous les endpoints** +- **8 API routes** corrigĂ©es pour ne plus fallback vers 'ollama' +- **Config endpoint** retourne 'not set' au lieu de 'ollama' +- **3 formulaires admin** mis Ă  jour + +## 📋 Fichiers modifiĂ©s + +1. ✅ `lib/ai/factory.ts` - Plus de fallback 'ollama', erreurs claires +2. ✅ `docker-compose.yml` - Plus de valeur par dĂ©faut OLLAMA_BASE_URL +3. ✅ `components/sidebar.tsx` - Memento (pas Mento) +4. ✅ `README.md` - Memento +5. ✅ `deploy.sh` - Memento +6. ✅ `DOCKER_DEPLOYMENT.md` - Memento +7. ✅ `app/api/ai/config/route.ts` - Affiche 'not set' au lieu de 'ollama' + +## 🚀 DĂ©ploiement sur Docker + +```bash +# Sur votre serveur Proxmox +cd /path/to/Keep + +# 1. RĂ©cupĂ©rer les derniers changements +git pull + +# 2. VĂ©rifier que la base de donnĂ©es a la bonne config +cd keep-notes +npx tsx scripts/debug-config.ts +# Devrait afficher : +# AI_PROVIDER_TAGS: "openai" +# AI_PROVIDER_EMBEDDING: "openai" + +# 3. ArrĂȘter les conteneurs +cd .. +docker compose down + +# 4. Rebuild (IMPORTANT --no-cache pour appliquer les changements) +docker compose build --no-cache keep-notes + +# 5. DĂ©marrer +docker compose up -d + +# 6. VĂ©rifier les logs +docker compose logs -f keep-notes +``` + +## đŸ§Ș Tests aprĂšs dĂ©ploiement + +### 1. VĂ©rifier la config via API +```bash +curl http://192.168.1.190:3000/api/debug/config +``` +**RĂ©sultat attendu :** +```json +{ + "AI_PROVIDER_TAGS": "openai", + "AI_PROVIDER_EMBEDDING": "openai", + "OPENAI_API_KEY": "set (hidden)" +} +``` + +### 2. Tester les endpoints IA +```bash +# Test embeddings +curl http://192.168.1.190:3000/api/ai/test +``` +**RĂ©sultat attendu :** +```json +{ + "success": true, + "tagsProvider": "openai", + "embeddingsProvider": "openai", + "details": { + "provider": "OpenAI", + "baseUrl": "https://api.openai.com/v1" + } +} +``` + +### 3. Tester dans l'interface + +Ouvrez http://192.168.1.190:3000 et testez : + +1. **Admin Settings** → **AI Settings** + - VĂ©rifiez : Tags Provider = "openai" + - VĂ©rifiez : Embeddings Provider = "openai" + +2. **CrĂ©ez une note** et testez : + - ✹ **GĂ©nĂ©ration de titres** (Ctrl+M) + - đŸ·ïž **Auto-labels** + - 📝 **Clarify / Shorten / Improve Style** (les fonctions qui Ă©chouaient !) + +3. **VĂ©rifiez les logs Docker** - PLUS d'erreurs "OLLAMA error NOT found" + +## ❌ Si ça ne marche toujours pas + +### VĂ©rifier la base de donnĂ©es +```bash +docker compose exec keep-notes npx tsx scripts/debug-config.ts +``` + +### VĂ©rifier les variables d'environnement +```bash +docker compose exec keep-notes env | grep -E "AI_PROVIDER|OPENAI|OLLAMA" +``` + +**RĂ©sultat attendu :** +``` +AI_PROVIDER_TAGS= # Correct ! La config vient de la DB +AI_PROVIDER_EMBEDDING= # Correct ! +OPENAI_API_KEY=sk-... # Votre clĂ© +OLLAMA_BASE_URL= # Correct ! Pas de valeur par dĂ©faut +``` + +### Reset provider si nĂ©cessaire +Dans l'interface admin : +1. Allez dans http://192.168.1.190:3000/admin/settings +2. Section "AI Settings" +3. Changez Tags Provider → "ollama" +4. Sauvegardez +5. Changez Tags Provider → "openai" +6. Sauvegardez +7. RafraĂźchissez la page (F5) + +## 📝 RĂ©sumĂ© technique + +**Le bug :** +- Factory: `|| 'ollama'` comme fallback +- Docker: `OLLAMA_BASE_URL=${...:-http://ollama:11434}` par dĂ©faut +- RĂ©sultat: Utilisait Ollama mĂȘme avec OpenAI configurĂ© + +**La solution :** +- Factory: Throw une erreur claire si pas configurĂ© +- Docker: Ne pas dĂ©finir OLLAMA_BASE_URL si pas demandĂ© +- RĂ©sultat: Utilise le provider configurĂ© dans la DB + +**3 revues de code complĂštes :** +✅ Factory et logic +✅ Docker configuration +✅ API routes et UI components diff --git a/keep-notes/app/(main)/admin/settings/admin-settings-form.tsx b/keep-notes/app/(main)/admin/settings/admin-settings-form.tsx index 3ae69d0..b0a40f0 100644 --- a/keep-notes/app/(main)/admin/settings/admin-settings-form.tsx +++ b/keep-notes/app/(main)/admin/settings/admin-settings-form.tsx @@ -75,43 +75,63 @@ export function AdminSettingsForm({ config }: { config: Record } setIsSaving(true) const data: Record = {} - // Tags provider configuration - const tagsProv = formData.get('AI_PROVIDER_TAGS') as AIProvider - data.AI_PROVIDER_TAGS = tagsProv - data.AI_MODEL_TAGS = formData.get('AI_MODEL_TAGS') as string + try { + // Tags provider configuration + const tagsProv = formData.get('AI_PROVIDER_TAGS') as AIProvider + if (!tagsProv) throw new Error('AI_PROVIDER_TAGS is required') + data.AI_PROVIDER_TAGS = tagsProv - if (tagsProv === 'ollama') { - data.OLLAMA_BASE_URL = formData.get('OLLAMA_BASE_URL_TAGS') as string - } else if (tagsProv === 'openai') { - data.OPENAI_API_KEY = formData.get('OPENAI_API_KEY') as string - } else if (tagsProv === 'custom') { - data.CUSTOM_OPENAI_API_KEY = formData.get('CUSTOM_OPENAI_API_KEY_TAGS') as string - data.CUSTOM_OPENAI_BASE_URL = formData.get('CUSTOM_OPENAI_BASE_URL_TAGS') as string - } + const tagsModel = formData.get('AI_MODEL_TAGS') as string + if (tagsModel) data.AI_MODEL_TAGS = tagsModel - // Embeddings provider configuration - const embedProv = formData.get('AI_PROVIDER_EMBEDDING') as AIProvider - data.AI_PROVIDER_EMBEDDING = embedProv - data.AI_MODEL_EMBEDDING = formData.get('AI_MODEL_EMBEDDING') as string + if (tagsProv === 'ollama') { + const ollamaUrl = formData.get('OLLAMA_BASE_URL_TAGS') as string + if (ollamaUrl) data.OLLAMA_BASE_URL = ollamaUrl + } else if (tagsProv === 'openai') { + const openaiKey = formData.get('OPENAI_API_KEY') as string + if (openaiKey) data.OPENAI_API_KEY = openaiKey + } else if (tagsProv === 'custom') { + const customKey = formData.get('CUSTOM_OPENAI_API_KEY_TAGS') as string + const customUrl = formData.get('CUSTOM_OPENAI_BASE_URL_TAGS') as string + if (customKey) data.CUSTOM_OPENAI_API_KEY = customKey + if (customUrl) data.CUSTOM_OPENAI_BASE_URL = customUrl + } - if (embedProv === 'ollama') { - data.OLLAMA_BASE_URL = formData.get('OLLAMA_BASE_URL_EMBEDDING') as string - } else if (embedProv === 'openai') { - data.OPENAI_API_KEY = formData.get('OPENAI_API_KEY') as string - } else if (embedProv === 'custom') { - data.CUSTOM_OPENAI_API_KEY = formData.get('CUSTOM_OPENAI_API_KEY_EMBEDDING') as string - data.CUSTOM_OPENAI_BASE_URL = formData.get('CUSTOM_OPENAI_BASE_URL_EMBEDDING') as string - } + // Embeddings provider configuration + const embedProv = formData.get('AI_PROVIDER_EMBEDDING') as AIProvider + if (!embedProv) throw new Error('AI_PROVIDER_EMBEDDING is required') + data.AI_PROVIDER_EMBEDDING = embedProv - const result = await updateSystemConfig(data) - setIsSaving(false) + const embedModel = formData.get('AI_MODEL_EMBEDDING') as string + if (embedModel) data.AI_MODEL_EMBEDDING = embedModel - if (result.error) { - toast.error('Failed to update AI settings') - } else { - toast.success('AI Settings updated') - setTagsProvider(tagsProv) - setEmbeddingsProvider(embedProv) + if (embedProv === 'ollama') { + const ollamaUrl = formData.get('OLLAMA_BASE_URL_EMBEDDING') as string + if (ollamaUrl) data.OLLAMA_BASE_URL = ollamaUrl + } else if (embedProv === 'openai') { + const openaiKey = formData.get('OPENAI_API_KEY') as string + if (openaiKey) data.OPENAI_API_KEY = openaiKey + } else if (embedProv === 'custom') { + const customKey = formData.get('CUSTOM_OPENAI_API_KEY_EMBEDDING') as string + const customUrl = formData.get('CUSTOM_OPENAI_BASE_URL_EMBEDDING') as string + if (customKey) data.CUSTOM_OPENAI_API_KEY = customKey + if (customUrl) data.CUSTOM_OPENAI_BASE_URL = customUrl + } + + console.log('Saving AI config:', data) + const result = await updateSystemConfig(data) + setIsSaving(false) + + if (result.error) { + toast.error('Failed to update AI settings: ' + result.error) + } else { + toast.success('AI Settings updated successfully') + setTagsProvider(tagsProv) + setEmbeddingsProvider(embedProv) + } + } catch (error: any) { + setIsSaving(false) + toast.error('Error: ' + error.message) } } diff --git a/keep-notes/app/actions/admin-settings.ts b/keep-notes/app/actions/admin-settings.ts index 41a0688..7525d99 100644 --- a/keep-notes/app/actions/admin-settings.ts +++ b/keep-notes/app/actions/admin-settings.ts @@ -39,16 +39,23 @@ export async function getSystemConfig() { export async function updateSystemConfig(data: Record) { await checkAdmin() - + try { - const operations = Object.entries(data).map(([key, value]) => + // Filter out empty values but keep 'false' as valid + const filteredData = Object.fromEntries( + Object.entries(data).filter(([key, value]) => value !== '' && value !== null && value !== undefined) + ) + + console.log('Updating system config:', filteredData) + + const operations = Object.entries(filteredData).map(([key, value]) => prisma.systemConfig.upsert({ where: { key }, update: { value }, create: { key, value } }) ) - + await prisma.$transaction(operations) revalidatePath('/admin/settings') return { success: true } diff --git a/keep-notes/scripts/setup-openai.ts b/keep-notes/scripts/setup-openai.ts new file mode 100644 index 0000000..7315a52 --- /dev/null +++ b/keep-notes/scripts/setup-openai.ts @@ -0,0 +1,48 @@ +import prisma from '../lib/prisma' + +/** + * Setup OpenAI as default AI provider in database + * Run this to ensure OpenAI is properly configured + */ +async function setupOpenAI() { + console.log('🔧 Setting up OpenAI as default AI provider...\n') + + const configs = [ + { key: 'AI_PROVIDER_TAGS', value: 'openai' }, + { key: 'AI_PROVIDER_EMBEDDING', value: 'openai' }, + { key: 'AI_MODEL_TAGS', value: 'gpt-4o-mini' }, + { key: 'AI_MODEL_EMBEDDING', value: 'text-embedding-3-small' }, + ] + + try { + for (const config of configs) { + await prisma.systemConfig.upsert({ + where: { key: config.key }, + update: { value: config.value }, + create: { key: config.key, value: config.value } + }) + console.log(`✅ Set ${config.key} = ${config.value}`) + } + + console.log('\n✹ OpenAI configuration complete!') + console.log('\nNext steps:') + console.log('1. Add your OPENAI_API_KEY in admin settings: http://localhost:3000/admin/settings') + console.log('2. Or add it to .env.docker: OPENAI_API_KEY=sk-...') + console.log('3. Restart the application') + + // Verify + const verify = await prisma.systemConfig.findMany({ + where: { key: { in: configs.map(c => c.key) } } + }) + + console.log('\n✅ Verification:') + verify.forEach(c => console.log(` ${c.key}: ${c.value}`)) + } catch (error) { + console.error('❌ Error:', error) + process.exit(1) + } +} + +setupOpenAI() + .then(() => process.exit(0)) + .catch(() => process.exit(1))