mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-21 05:58:13 +02:00
Retire la pile Python/React : Loki repart de la base Go d'AJEAN
L'ancien Loki (FastAPI + React + Ollama) est remplacé par le fork d'AJEAN. Tout reste accessible dans l'historique (git show bb4fb1b:backend/app/agent.py).
This commit is contained in:
87 files changed
-18730
No files matched your search
@@ -1,135 +0,0 @@
|
||||
# Loki — MCP intégré + Skills automatiques + Boucle code vérifiée
|
||||
|
||||
**Date** : 2026-07-18
|
||||
**Objectif** : augmenter l'intelligence effective des modèles locaux (~17 Go,
|
||||
répartis sur 5060 Ti + 3060 12 Go) sans changer de modèle, en améliorant le
|
||||
harnais : outils professionnels via MCP, méthodes expertes injectées (skills),
|
||||
et vérification réelle du code produit.
|
||||
|
||||
**Choix utilisateur validés** :
|
||||
- Cas d'usage : tout (code + raisonnement + multi-étapes).
|
||||
- Compromis : équilibré — passes supplémentaires uniquement pour le code
|
||||
(1 passe de correction max).
|
||||
- Un seul gros modèle ; pas d'architecture deux-modèles.
|
||||
- MCP : catalogue préconfiguré des meilleurs serveurs, toggles on/off,
|
||||
désactivé = zéro coût (aucun outil dans le prompt, aucun process lancé).
|
||||
- Skills : déclenchement automatique (routeur lexical), invisible, désactivable.
|
||||
|
||||
---
|
||||
|
||||
## 1. Client MCP intégré
|
||||
|
||||
### Backend — `backend/app/mcp_client.py`
|
||||
|
||||
- SDK officiel Python `mcp` (ajout à `requirements.txt`).
|
||||
- Transports : stdio (commande locale, ex. `npx …`) et streamable-http (URL).
|
||||
- **Gestionnaire** (singleton, même pattern que `ollama_client`) :
|
||||
- Catalogue embarqué (dict Python) + état activé/désactivé et paramètres
|
||||
(clé API, URL) persistés en DB (`db.get/set_config_value`, clé `mcp`).
|
||||
- Connexion **lazy** : un serveur activé n'est démarré qu'au premier message
|
||||
qui suit ; la session MCP (process + handshake) est réutilisée ensuite.
|
||||
- Serveur désactivé : process jamais lancé, outils jamais exposés.
|
||||
- `list_tools()` par serveur → conversion schéma MCP → schéma
|
||||
function-calling Ollama ; préfixe `mcp_<serveur>_<outil>`.
|
||||
- `call_tool(name, args)` avec timeout 30 s par appel.
|
||||
- Panne (crash process, handshake KO, timeout répété) : le serveur est
|
||||
marqué en erreur, ses outils retirés, notice SSE dans le fil ; nouvelle
|
||||
tentative au message suivant. Un MCP cassé ne bloque jamais le chat.
|
||||
- `aclose()` au shutdown (lifespan `main.py`).
|
||||
|
||||
### Catalogue préconfiguré (désactivés par défaut)
|
||||
|
||||
| ID | Serveur | Commande / transport | Apport |
|
||||
|---|---|---|---|
|
||||
| `playwright` | Playwright MCP | `npx @playwright/mcp@latest --headless` | navigateur réel : naviguer, cliquer, lire la console, screenshot |
|
||||
| `context7` | Context7 | `npx -y @upstash/context7-mcp` | doc à jour de toute librairie/framework |
|
||||
| `fetch` | Fetch | `python -m mcp_server_fetch` | lecture propre d'URL (markdown) |
|
||||
| `searxng` | SearxNG | `npx -y mcp-searxng` + `SEARXNG_URL` | vraie recherche web (nécessite instance SearxNG) |
|
||||
| `custom` | Personnalisé | commande ou URL saisie par l'utilisateur | n'importe quel serveur MCP |
|
||||
|
||||
- Filtre d'outils par serveur (liste `expose` optionnelle dans le catalogue)
|
||||
pour limiter le nombre d'outils injectés (ex. Playwright : navigate, click,
|
||||
type, snapshot, console, screenshot — pas les 25+ outils complets).
|
||||
|
||||
### Intégration boucle agent — `backend/app/agent.py` + `tools.py`
|
||||
|
||||
- Les outils MCP actifs sont ajoutés à la liste d'outils envoyée au modèle
|
||||
(après les outils natifs) et dispatchés vers `mcp_client.call_tool`.
|
||||
- Résultats tronqués à une taille max (~8 000 caractères) avant retour au
|
||||
modèle (protège le contexte).
|
||||
- Rendu frontend : ToolCards existantes (aucun composant nouveau requis).
|
||||
|
||||
### UI — Configuration → onglet « MCP » (`SettingsView.tsx`)
|
||||
|
||||
- Une carte par serveur du catalogue : nom, description, toggle, statut
|
||||
(● connecté / ○ inactif / ⚠ erreur + message), nombre d'outils exposés,
|
||||
champs paramètres (URL/clé) si requis.
|
||||
- Carte « Personnalisé » : champ commande ou URL + toggle.
|
||||
- Routes : `GET /api/mcp` (état), `PUT /api/mcp/{id}` (toggle + params),
|
||||
`POST /api/mcp/{id}/test` (connexion d'essai, renvoie outils découverts).
|
||||
|
||||
### Dockerfile
|
||||
|
||||
- Ajout Node.js LTS (requis npx) + `pip install mcp mcp-server-fetch`.
|
||||
- Image : les serveurs npx sont téléchargés au premier lancement (cache
|
||||
volume npm optionnel).
|
||||
|
||||
## 2. Skills automatiques
|
||||
|
||||
### Bibliothèque — `backend/skills/*.md`
|
||||
|
||||
Fichiers markdown en français, livrés avec l'app :
|
||||
- `debogage-systematique.md` — reproduire → isoler → hypothèse → corriger → vérifier.
|
||||
- `creation-web.md` — structure sémantique → style → interactivité → vérification (liens, console, responsive).
|
||||
- `refactor-sur.md` — comprendre → tests/garde-fous → petits pas → vérifier à chaque pas.
|
||||
- `analyse-donnees.md` — examiner le format → valider → transformer → présenter.
|
||||
- `redaction-structuree.md` — plan → rédaction → relecture ciblée.
|
||||
|
||||
Format : frontmatter (`name`, `description`, `keywords`) + corps injecté tel quel.
|
||||
|
||||
### Routeur de skills — `backend/app/skills.py`
|
||||
|
||||
- Sélection **lexicale instantanée** (regex/mots-clés, même style que
|
||||
`router.py`) : au plus UNE skill par message ; aucun appel LLM.
|
||||
- Injection : message système supplémentaire
|
||||
(`"Méthode à suivre pour cette tâche :\n<corps>"`) inséré après l'invite
|
||||
système, pour ce tour uniquement (non persisté dans l'historique).
|
||||
- Événement SSE `skill` `{name, title}` → badge dans le fil
|
||||
(« 📘 Skill : Débogage systématique »).
|
||||
- Toggle global `skills_enabled` (défaut : activé) dans la config agent
|
||||
(Configuration → Intelligence).
|
||||
|
||||
## 3. Boucle code vérifiée
|
||||
|
||||
- Nouvel outil natif `run_check` (`tools.py`) : détection par extension —
|
||||
`.py` → `python -m py_compile` (analyse seule, JAMAIS d'exécution — toute
|
||||
exécution passe par `run_shell` et sa validation utilisateur) ;
|
||||
`.js/.ts` → `node --check` ; `.html` → `check_html` existant ; JSON → parse.
|
||||
Sortie = erreurs compactées.
|
||||
- Boucle agent : après un `write_file`/`edit_file` sur du code, le harnais
|
||||
exécute `run_check` automatiquement et renvoie les erreurs au modèle dans le
|
||||
même tour — **1 passe de correction maximum** (choix « équilibré »).
|
||||
- Si le serveur MCP Playwright est actif et que la tâche a produit du HTML :
|
||||
la skill `creation-web` oriente le modèle vers une vérification réelle dans
|
||||
le navigateur (console + rendu) au lieu du check statique seul.
|
||||
|
||||
## 4. Hors périmètre
|
||||
|
||||
- Architecture deux-modèles (architecte/codeur) — rejetée par l'utilisateur.
|
||||
- Best-of-N / juge — trop coûteux.
|
||||
- Marketplace/édition de skills dans l'UI (v2 possible ; v1 = fichiers livrés).
|
||||
|
||||
## 5. Vérification
|
||||
|
||||
1. **MCP** : activer Fetch → demander « résume cette page <url> » → ToolCard
|
||||
`mcp_fetch_fetch` + résumé correct. Désactiver → l'outil disparaît du
|
||||
prompt (vérifiable via logs). Serveur cassé (commande invalide) → notice,
|
||||
chat fonctionnel.
|
||||
2. **Playwright** : « crée une page X puis vérifie-la dans le navigateur » →
|
||||
navigation + console lue + correction éventuelle.
|
||||
3. **Skills** : message « mon script plante avec TypeError » → badge skill
|
||||
débogage ; message banal → aucun badge.
|
||||
4. **run_check** : demander un script Python avec bug volontaire induit →
|
||||
l'erreur est détectée et corrigée dans le même tour (1 passe).
|
||||
5. **Perf** : aucun serveur MCP actif → latence premier token inchangée
|
||||
(mesure avant/après).
|
||||
@@ -1,99 +0,0 @@
|
||||
# Loki — Projets (répertoires de travail) + aperçu réductible
|
||||
|
||||
**Date** : 2026-07-19
|
||||
**Objectif** : permettre à l'agent de travailler dans des projets entiers —
|
||||
un projet = un répertoire choisi depuis la carte du chat — et pouvoir replier
|
||||
le panneau d'aperçu.
|
||||
|
||||
**Choix validés** : projet = sous-dossier de premier niveau du workspace
|
||||
(pas de chemin arbitraire) ; portée par session ; édition de la mémoire
|
||||
écartée du périmètre.
|
||||
|
||||
---
|
||||
|
||||
## 1. Projets — backend
|
||||
|
||||
### Modèle
|
||||
- Un projet = un sous-dossier de premier niveau de `WORKSPACE_DIR`
|
||||
(ex. `workspace/jeu-snake/`). Nom validé : `^[a-z0-9][a-z0-9_-]{0,40}$`.
|
||||
- Session sans projet (`NULL`) = racine du workspace — compatibilité totale
|
||||
avec les sessions existantes.
|
||||
- Colonne `project TEXT` ajoutée à `sessions` (migration douce
|
||||
`ALTER TABLE`, comme `summary`/`meta`).
|
||||
|
||||
### Racine active par requête (contextvar)
|
||||
- `tools.py` : `_ACTIVE_ROOT: ContextVar[str | None]` + fonctions
|
||||
`set_project(name | None)` / lecture dans `_workspace_root()`. `_safe_path`
|
||||
inchangé dans sa logique de confinement — simplement re-raciné sur
|
||||
`workspace/<projet>` quand un projet est actif.
|
||||
- `routes/chat.py` : au début de `chat()`, `tools.set_project(session["project"])`
|
||||
(après validation : dossier existant, sinon retombe sur la racine + notice
|
||||
SSE « projet introuvable, retour au workspace »).
|
||||
- Les aides de contexte suivent la même racine : `_workspace_listing`,
|
||||
`_mentioned_files`, `_session_code_context` (fichiers vérifiés sous la
|
||||
racine active).
|
||||
- `run_shell` : cwd = racine active (déjà `_workspace_root()`).
|
||||
|
||||
### Moteur code (Aider) par projet
|
||||
- `coder.run_code_task` reçoit la racine active (cwd du process Aider).
|
||||
- `coder.ensure_git(dir)` appelé à la création d'un projet → un dépôt git
|
||||
par projet, historique/revert propres. L'onglet Git du panneau droit opère
|
||||
sur le projet de la session courante (routes git prennent `project`).
|
||||
|
||||
### Routes
|
||||
- `GET /api/projects` → `{projects: [{name, files: int}], root_files: int}`
|
||||
(sous-dossiers de premier niveau, dotfiles exclus).
|
||||
- `POST /api/projects {name}` → mkdir + `ensure_git` ; 400 si nom invalide
|
||||
ou existant.
|
||||
- `PATCH /api/sessions/{sid}` accepte `project: str | null` (en plus de
|
||||
`title`).
|
||||
- `GET /api/files`, `/api/files/content`, `/api/files/download`,
|
||||
`DELETE /api/files` : paramètre optionnel `project` — même re-racinage,
|
||||
même confinement.
|
||||
- Routes git (`/api/git/*`) : paramètre optionnel `project`.
|
||||
|
||||
## 2. Sélecteur projet — frontend (carte du chat)
|
||||
|
||||
- **Chip « 📁 <projet> »** dans le composer, à côté du sélecteur de mode
|
||||
Plan/Build/Yolo. Affiche `workspace` si aucun projet.
|
||||
- Clic → menu (même style que les menus existants) : liste des projets,
|
||||
entrée active cochée, **« + Nouveau projet »** avec input inline
|
||||
(Enter crée + sélectionne, Escape annule).
|
||||
- Sélection → `PATCH /api/sessions/{sid} {project}` → store met à jour la
|
||||
session courante → `refreshFiles()` re-racine LeftPanel / FilesView /
|
||||
PreviewPanel (le store passe `project` de la session courante aux appels
|
||||
fichiers).
|
||||
- Nouvelle session : hérite du projet de la session courante (envoyé au
|
||||
`POST /api/sessions`).
|
||||
- Session sans projet : comportement actuel inchangé.
|
||||
|
||||
## 3. Aperçu réductible — frontend
|
||||
|
||||
- Bouton de repli dans l'en-tête du `PreviewPanel` (à côté des onglets) :
|
||||
replie le panneau en **barre verticale fine (36 px)** ne contenant qu'un
|
||||
bouton de réouverture (icône ⇤ pivotée) et l'indicateur d'onglet actif.
|
||||
- État replié persisté en `localStorage` (`loki.preview.collapsed`), la
|
||||
largeur l'est déjà (`loki.preview.width`).
|
||||
- Replié, le panneau ne rend pas son contenu (pas d'iframe HTML vivante).
|
||||
|
||||
## 4. Hors périmètre
|
||||
- Édition de la mémoire (RAG/résumés) — écartée par l'utilisateur.
|
||||
- Chemins hors workspace, suppression/renommage de projets depuis l'UI
|
||||
(suppression possible via la corbeille de l'arborescence).
|
||||
- Projet imbriqué (sous-sous-dossier comme projet).
|
||||
|
||||
## 5. Vérification
|
||||
1. `POST /api/projects {"name":"demo"}` → dossier + `.git` créés ;
|
||||
nom invalide → 400.
|
||||
2. Session A projet `demo`, session B sans projet : fichiers créés par
|
||||
l'agent de A atterrissent dans `workspace/demo/`, ceux de B à la racine ;
|
||||
l'arborescence gauche suit la session ouverte.
|
||||
3. `curl "…/api/files?project=demo"` → arbre du projet seul ;
|
||||
`?project=../x` → 400.
|
||||
4. Reprise : « corrige les bugs » dans une session projet → Aider travaille
|
||||
dans `workspace/demo/`, commit dans le git du projet, onglet Git montre
|
||||
l'historique du projet.
|
||||
5. Aperçu : bouton replie → barre fine, contenu démonté ; réouverture
|
||||
restaure l'onglet et la largeur ; état conservé après rechargement.
|
||||
6. pytest : contextvar (re-racinage + confinement), routes projects,
|
||||
migration colonne, héritage projet à la création de session.
|
||||
Reference in new issue
Block a user