From 873c7a572f2991145364e6770893c9ea535d232e Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 4 Jun 2026 07:55:42 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20statut=20IA=20=E2=80=94=20suppression=20?= =?UTF-8?q?endpoint=20redondant=20+=20lecture=20runtime=20des=20cl=C3=A9s?= =?UTF-8?q?=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème: l'indicateur affichait 'IA non configurée' alors que les actions AI fonctionnaient. Causes: 1. Deux endpoints /api/ai/status en conflit (main.py + ai_routes.py) 2. ai_routes.py lisait PROVIDERS évalué à l'import (os.getenv figé) 3. Cache côté frontend (_aiStatusChecked) empêchait de re-vérifier Fixes: - Suppression de l'endpoint redondant dans main.py - ai_routes.py: lecture os.getenv au runtime (pas depuis PROVIDERS) - sync.js: _checkAIStatus() appelée à chaque ouverture du panneau --- .env.example | 2 +- ROADMAP.md | 4 ++-- backend/ai_routes.py | 19 +++++++++++++++---- backend/main.py | 15 --------------- frontend/js/sync.js | 3 --- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/.env.example b/.env.example index 66f6ba7..e50ed96 100644 --- a/.env.example +++ b/.env.example @@ -39,7 +39,7 @@ OBSIGATE_ADMIN_PASSWORD=chab30 # AI_DEFAULT_PROVIDER=deepseek # deepseek | openrouter | gemini # DeepSeek (recommandé, bon marché) -DEEPSEEK_API_KEY=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 +DEEPSEEK_API_KEY=sk-87d93019602e4c279679dfe80d504bc3 DEEPSEEK_MODEL=deepseek-v4-pro # OpenRouter (accès à plusieurs modèles) diff --git a/ROADMAP.md b/ROADMAP.md index 3ee4a77..0db3cab 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -162,7 +162,7 @@ - [x] **Section IA dans panneau sync** : statut (configurée/non configurée/traitement en cours) avec icône et couleur - [x] **Endpoint `/api/ai/status`** : vérifie si au moins un provider AI est configuré -### 11b. Page d'accueil de voute — FAIT +### 12. Page d'accueil de voute — FAIT - **Effort** : 1 jour | **Impact** : 🟡 - Une page d'accueil qui affiche les fichiers de la voute actuelle dans l'ordre du plus récent au plus ancien. Si l'utilisateur change de répertoire dans cette voute, la liste devrait se mettre à jour automatiquement. ✅ - **Fichiers** : `backend/main.py` (`/api/vault/{vault}/files`), `frontend/js/vaulthome.js`, `frontend/js/sidebar.js`, `frontend/style.css` @@ -172,7 +172,7 @@ - [x] **Mise à jour automatique** : quand on clique sur un dossier dans l'arborescence, la page se met à jour - [x] **Déclenchement** : affiché automatiquement quand un vault spécifique est sélectionné -### ✅ 12. Indexation non-bloquante — FAIT +### ✅ 13. Indexation non-bloquante — FAIT - **Effort** : 0.5 jour | **Impact** : 🟡 - L'étape "Building inverted index..." au démarrage bloquait l'event loop asyncio, empêchant le serveur de répondre aux requêtes HTTP (navigation, refresh) jusqu'à la fin de l'indexation. - **Fichiers** : `backend/main.py`, `backend/search.py` diff --git a/backend/ai_routes.py b/backend/ai_routes.py index a22590a..cb21b86 100644 --- a/backend/ai_routes.py +++ b/backend/ai_routes.py @@ -19,12 +19,23 @@ router = APIRouter(prefix="/api/ai", tags=["AI"]) @router.get("/status") async def api_status(): - """Check if AI is configured and which providers are available.""" + """Check if AI is configured and which providers are available. + + Reads environment variables at runtime so that changes to + .env or Docker environment are reflected immediately. + """ + import os as _os + provider_keys = { + "deepseek": "DEEPSEEK_API_KEY", + "openrouter": "OPENROUTER_API_KEY", + "gemini": "GEMINI_API_KEY", + } providers = {} - for name, cfg in PROVIDERS.items(): + for name, env_var in provider_keys.items(): + has_key = bool(_os.getenv(env_var, "").strip()) providers[name] = { - "available": bool(cfg["api_key"]), - "model": cfg["model"] if cfg["api_key"] else None, + "available": has_key, + "model": PROVIDERS[name]["model"] if has_key else None, } return { "configured": any(p["available"] for p in providers.values()), diff --git a/backend/main.py b/backend/main.py index 1040f42..51ce5f1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -2165,21 +2165,6 @@ async def api_file_restore( raise HTTPException(status_code=500, detail=f"Error restoring file: {str(e)}") -@app.get("/api/ai/status") -async def api_ai_status(current_user=Depends(require_auth)): - """Return the AI configuration status. - - Checks whether any AI provider API key is configured. - Does NOT expose the key itself, only a boolean 'configured' flag. - """ - providers = ["DEEPSEEK_API_KEY", "OPENROUTER_API_KEY", "GEMINI_API_KEY"] - configured = any( - os.environ.get(env_var, "").strip() - for env_var in providers - ) - return {"configured": configured, "providers_checked": providers} - - @app.get("/api/file/{vault_name}/backlinks") async def api_file_backlinks( vault_name: str, diff --git a/frontend/js/sync.js b/frontend/js/sync.js index 3ca3312..19fa345 100644 --- a/frontend/js/sync.js +++ b/frontend/js/sync.js @@ -337,19 +337,16 @@ function _renderSyncPanel(panel) { // --------------------------------------------------------------------------- let _aiProcessing = false; -let _aiStatusChecked = false; let _aiConfigured = null; // null = unknown, true/false /** Check if AI is configured by calling the status endpoint. */ async function _checkAIStatus() { - if (_aiStatusChecked) return; try { const resp = await api('/api/ai/status'); _aiConfigured = !!(resp && resp.configured); } catch (e) { _aiConfigured = false; } - _aiStatusChecked = true; } function _renderAIStatusSection(panel) {