From e7863d732c2214c575249392ba05fc60dc65672d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 11:09:39 +0000 Subject: [PATCH 1/3] README : remettre la documentation en accord avec l'application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le README décrivait encore l'état d'AJEAN sur plusieurs points devenus faux, et passait sous silence ce que ce fork a ajouté. Corrections de fond : - L'accès distant chiffré ajean.link était annoncé en tête et dans les fonctionnalités. Sa section d'interface et son module JS ont été retirés ; le code serveur reste en place mais inerte, et c'est ce que le README dit maintenant plutôt que de promettre une fonctionnalité absente. - Le catalogue de modèles distant est documenté comme retiré, avec la raison. - Le titre de discussion vient du premier message, pas d'un modèle : dire « généré automatiquement » aurait laissé croire à un appel d'inférence. - L'outil de capture d'écran est toujours proposé au modèle ; c'est sa DESCRIPTION qui suit la vision réelle du moteur. La première rédaction disait que l'outil était retiré — c'est faux. - Les captures vivent sous workspace/captures//, pas directement sous /data. Ajouts : - Section « Installer un modèle » : recherche Hugging Face, verdict mémoire et son caractère estimatif assumé, projecteur vision du même dépôt, repères sur les quants Dynamic d'unsloth et sur ggml-org, mode expert par lien direct. - Section « Données et persistance » : ce que contient chaque chemin sous /data, la commande docker inspect pour vérifier le montage, et les deux pièges rencontrés sur Unraid — /mnt/user contre /mnt/cache, et les conteneurs relancés avant que le pilote Nvidia soit chargé (ERROR init result=11). - Discussions multiples, identité (prénom + avatars), regroupement des Paramètres, HF_TOKEN dans le tableau des variables. - Le tableau des différences avec l'amont gagne quatre lignes : panneau Moteur, choix du modèle, historique de tchat, accès distant. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VueWA9xcYadaYq65tBisix --- README.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5f77c3a..cec5650 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ > **un conteneur Docker GPU autonome**, là où l'amont s'installe en binaire + > systemd sur la machine hôte. -Loki fait tourner un modèle de langage **100 % en local** : tchat, mémoire -persistante, accès internet, outils MCP, agent (shell, fichiers), accès distant -chiffré — serveur d'inférence llama.cpp compris, dans une seule image. +Loki fait tourner un modèle de langage **100 % en local** : discussions +multiples, mémoire persistante, accès internet, captures de pages web, outils +MCP, agent (shell, fichiers) — serveur d'inférence llama.cpp compris, dans une +seule image. ## Architecture @@ -19,7 +20,9 @@ chiffré — serveur d'inférence llama.cpp compris, dans une seule image. │ │ pilote (fichier PID — pas de systemd) │ │ loki serve ──exec──► llama-server (CUDA, :8080) │ │ ▲ modèles .gguf │ -│ /data (config, bbolt, mémoire, workspace) │ +│ chromium (Playwright) — captures de pages web │ +│ /data (config, bbolt, presets, mémoire, │ +│ workspace + captures, modèles installés) │ │ /models (GGUF déposés à la main) │ └────────────────────────────────────────────────────┘ ``` @@ -102,36 +105,107 @@ redémarrages (volume `/data`). Variables d'environnement du conteneur : | `LOKI_NGL` | couches GPU initiales | `999` (tout) | | `LOKI_HOME` | données (volume) | `/data` | | `LOKI_MODEL_DIRS` | dossiers .gguf additionnels | `/models` | +| `HF_TOKEN` | jeton Hugging Face, pour les dépôts à accès restreint | — | En CLI dans le conteneur : `docker exec -it loki loki status` (aussi : `logs`, `restart`, `config`, `bench`, `test`…). -## Fonctionnalités (héritées d'AJEAN) +### Données et persistance -- **Tchat** avec streaming, raisonnement visible, pièces jointes, vision - (selon modèle), export de conversations. +**Tout** l'état vit sous `/data` (= `LOKI_HOME`). Si ce chemin n'est pas un +volume monté sur l'hôte, il disparaît au premier `docker compose down` — presets +et modèles compris. + +| Chemin | Contenu | +|---|---| +| `/data/loki.db` | base bbolt : préférences, discussions, mémoire des réglages | +| `/data/presets/` | un `.env` par preset (modèle, contexte, NGL, vision…) | +| `/data/models/` | modèles téléchargés depuis l'interface | +| `/data/memory/` | pages de mémoire persistante (`.md`) | +| `/data/workspace/` | dossier de travail de l'agent | +| `/data/workspace/captures//` | captures d'écran, un dossier par discussion | +| `/data/loki-engine.log` | journal de `llama-server` (aussi via `loki logs`) | +| `/models` | GGUF déposés à la main depuis l'hôte (volume séparé, `LOKI_MODEL_DIRS`) | + +Vérifier que le volume est bien là : + +```bash +docker inspect loki --format '{{range .Mounts}}{{.Source}} → {{.Destination}}{{println}}{{end}}' +``` + +Sur Unraid, garde le **même** chemin hôte d'un lancement à l'autre : `/mnt/user/…` +(partage, via FUSE) et `/mnt/cache/…` (disque de cache) désignent des +emplacements différents dès que le partage n'est pas en cache-only ou que le +*mover* est passé. Le compose fourni utilise `/mnt/user/appdata/loki/…`. + +Autre piège au redémarrage du serveur : Docker relance les conteneurs +`restart: unless-stopped` **avant** que le plugin Nvidia Driver ait chargé ses +modules. Le journal montre alors des `ERROR: init … result=11` et le moteur +démarre sans GPU. Un `docker restart loki` une fois le pilote prêt suffit. + +## Fonctionnalités + +Héritées d'AJEAN : + +- **Tchat** avec streaming, raisonnement visible, pièces jointes, export de + conversations. La **vision** demande un modèle multimodal *et* son projecteur + `mmproj` — voir [Installer un modèle](#installer-un-modèle). - **Mémoire persistante** (`memory off|ondemand|always`). - **Accès internet** : recherche + lecture de pages, moteur Go intégré ou [Crawl4AI](https://github.com/unclecode/crawl4ai) pour les pages JS. - **Agent** : shell, fichiers, workspace (`agent on`). - **Serveurs MCP** : Node.js est inclus dans l'image pour les serveurs `npx`. - **Presets** de configuration par modèle, bench, auto-détection GPU. -- **Accès distant chiffré** via le relais [ajean.link](https://ajean.link) - (service opéré par l'auteur de l'amont). - **API OpenAI-compatible** exposable (`network on`, protégée par clé). +Ajoutées par ce fork : + +- **Discussions multiples** : historique complet dans la barre latérale, titre + repris du premier message (renommable), suppression. Supprimer une discussion + efface aussi ses captures d'écran (`workspace/captures//`), pour que le + disque ne se remplisse pas en silence. +- **Recherche Hugging Face** intégrée avec verdict mémoire et installation liée + du projecteur vision (voir [Installer un modèle](#installer-un-modèle)). +- **Captures de pages web** : l'agent dispose de l'outil `web_screenshot` + (Chromium via Playwright, inclus dans l'image). Les captures partent en JPEG + et sont plafonnées à 20 fichiers / 40 Mo par discussion. La description de + l'outil suit la capacité **réelle** du moteur, sondée sur `/props` : sans + vision effective, elle dit au modèle « tu ne vois pas l'image » plutôt que de + lui promettre des yeux qu'il n'a pas — il peut toujours prendre la capture et + la montrer, sans prétendre la décrire. L'image relayée au moteur reste + éphémère : la persister gonflait le contexte jusqu'à le faire déborder. +- **Identité** : ton prénom et un avatar emoji pour toi et pour Loki, affichés + dans le fil. +- **Paramètres** : les réglages d'application (identité, apparence, accès + OpenAI, actions) sont regroupés à part des réglages d'IA. + +Retirées par ce fork : + +- **Accès distant via [ajean.link](https://ajean.link)** : la section de + l'interface et son module JS sont supprimés — un conteneur derrière son + propre réseau n'en a pas l'usage. Le code serveur du relais reste en place + mais **inerte** (aucun jeton, aucune section pour en fournir un) : le retirer + créerait un conflit à chaque reprise de l'amont. +- **Catalogue de modèles distant** : il interrogeait `ajean.link/models.json`, + sa route n'avait aucun consommateur et son repli embarqué datait de 2024. La + recherche Hugging Face le remplace. + ## Différences avec l'amont | | AJEAN (amont) | Loki (ce fork) | |---|---|---| | Installation | binaire + `sudo ajean install` (systemd) | `docker compose up` | | Moteur llama.cpp | compilé sur la machine (`ajean llamacpp install`) | image officielle llama.cpp (`server-cuda`), précompilée | +| Panneau « Moteur » | propose d'installer/compiler | annonce le moteur fourni par l'image, sans proposer d'install | | Supervision moteur | systemd / launchd / PID (Windows) | fichier PID (`LOKI_CONTAINER=1`) | | Configuration initiale | `ajean edit` ($EDITOR) | entrypoint + `loki config set` | +| Choix du modèle | lien Hugging Face collé à la main | recherche intégrée + verdict VRAM + projecteur lié | +| Historique de tchat | conversation unique | discussions multiples, titrées et persistées | +| Accès distant | relais chiffré ajean.link | retiré de l'interface | | Mise à jour | `ajean update` (binaire GitHub) | `docker compose pull` | -Le reste — UI, mémoire, outils, protocole — est celui d'AJEAN. Pour récupérer -les évolutions de l'amont : +Le reste — mémoire, outils, protocole, moteur d'inférence — est celui d'AJEAN. +Pour récupérer les évolutions de l'amont : ```bash git fetch upstream && git merge upstream/main # conflits de renommage à arbitrer From 133b30421ab4bfad1a2d8c7a87edb19376a279be Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 11:32:12 +0000 Subject: [PATCH 2/3] =?UTF-8?q?Postes=20distants=20:=20retirer=20le=20bout?= =?UTF-8?q?on=20oubli=C3=A9=20du=20composeur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le retrait de l'accès distant s'était arrêté à mi-chemin. La section « Accès distant » de la barre latérale et son module 13-remote.js ont bien disparu au commit 3740b88, mais l'autre moitié du même sujet est restée : le bouton « Postes distants » du composeur, l'icône écran/wifi sous « send ». Ce sont deux fonctionnalités distinctes dans le code (relay_* pour le tunnel ajean.link, node_* pour les postes appairés), mais à l'usage c'est la même chose : faire agir Loki ailleurs que sur le serveur qui l'héberge. Sur une installation en conteneur, ni l'une ni l'autre n'a d'objet, et ce bouton occupait une place dans une ligne d'outils déjà dense pour ouvrir une modale d'appairage qui ne servira jamais. Partent avec lui : les deux modales (celle d'appairage servait aussi d'écran d'édition d'un poste, via un remplacement de pied), le module 15-node.js entier, et les règles CSS #node-btn et .node-*. Le code serveur ne bouge pas — mêmes raisons qu'en 3740b88 : les routes /api/node/* et le serveur WebSocket d'appairage restent en place mais deviennent inatteignables, plus rien ne pouvant générer de code d'appairage. Les retirer créerait un conflit à chaque reprise de l'amont pour un gain nul. Un piège mérite d'être signalé, et il est maintenant commenté dans le code : loadAll() enchaîne ses chargements dans un Promise.allSettled, qui AVALE une ReferenceError. Oublier de retirer loadNode() de cette ligne n'aurait rien cassé visiblement — la page se serait simplement chargée à moitié. Le test navigateur écoute donc pageerror et échoue au moindre message. Vérifié en 1400×900, thèmes clair et sombre : aucune erreur JS au chargement, bouton et modales absents du DOM, openNodeHub indéfini, et le pied de carte garde son décompte de contexte, « compacter » et le trombone, tous atteignables au clic. Le composeur perd 26 px de large ; comme sa hauteur est mesurée depuis le correctif précédent, la réserve sous le fil suit toute seule — overlap.js repasse au vert de 380 à 2000 px. Deux échecs relevés au premier passage venaient du banc d'essai, pas du code : /api/backends/devices répond 400 parce que le BIN du fixture n'est pas un vrai llama-server, et « compacter » est masqué tant que le contexte est sous 50 %. Les assertions ont été corrigées, pas le code. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VueWA9xcYadaYq65tBisix --- README.md | 4 + internal/loki/ui/index.html | 287 +------------------------ internal/loki/ui/src/index.tmpl.html | 60 ------ internal/loki/ui/src/js/06-settings.js | 11 +- internal/loki/ui/src/js/15-node.js | 187 ---------------- internal/loki/ui/src/styles.css | 29 --- 6 files changed, 18 insertions(+), 560 deletions(-) delete mode 100644 internal/loki/ui/src/js/15-node.js diff --git a/README.md b/README.md index cec5650..3b195b6 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,10 @@ Retirées par ce fork : propre réseau n'en a pas l'usage. Le code serveur du relais reste en place mais **inerte** (aucun jeton, aucune section pour en fournir un) : le retirer créerait un conflit à chaque reprise de l'amont. +- **Postes distants** (faire agir l'agent sur un autre PC appairé) : bouton du + composeur, modales d'appairage et module JS supprimés. Même traitement que + ci-dessus — les routes `/api/node/*` subsistent mais plus rien ne peut + générer de code d'appairage, donc aucun poste ne peut se connecter. - **Catalogue de modèles distant** : il interrogeait `ajean.link/models.json`, sa route n'avait aucun consommateur et son repli embarqué datait de 2024. La recherche Hugging Face le remplace. diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html index ccfc238..ef47c27 100644 --- a/internal/loki/ui/index.html +++ b/internal/loki/ui/index.html @@ -1140,20 +1140,6 @@ html[data-hide-stats="1"] #chat .msg .statline{display:none} #attach:hover{background:var(--row-bg-on);border-color:transparent;color:var(--text)} #attach:active{transform:scale(.9)} #attach svg{display:block} -/* Bouton « Postes distants » du composeur : même gabarit que le trombone. L'icône - dépend de la cible active — écran (local) par défaut, ondes wifi quand un poste - distant est sélectionné, alors teinté en accent pour signaler « l'IA agit - ailleurs ». Les deux SVG coexistent ; on n'en montre qu'un via la classe. */ -#node-btn{width:18px;height:18px;min-height:18px;flex:none;margin:0;padding:0; - display:flex;align-items:center;justify-content:center;border-radius:999px; - background:transparent;border:1px solid transparent;color:var(--dim)} -#node-btn:hover{background:var(--row-bg-on);border-color:transparent;color:var(--text)} -#node-btn:active{transform:scale(.9)} -#node-btn svg{display:block} -#node-btn .ic-remote{display:none} -#node-btn.remote{color:var(--accent)} -#node-btn.remote .ic-here{display:none} -#node-btn.remote .ic-remote{display:block} /* Fichiers en attente : de petites bulles POSÉES AU-DESSUS de la carte. Le composeur réserve déjà 34 px transparents au-dessus de lui (la bande de dégradé qui fond vers le fil) : les pastilles s'y logent, et on retire d'autant @@ -1269,21 +1255,6 @@ button:hover{border-color:var(--dim);color:var(--text)} .xempty{display:flex;flex-direction:column;align-items:center;gap:6px;padding:44px 20px;text-align:center;color:var(--text);font-size:14px} .xempty-i{font-size:30px;line-height:1;color:var(--dim);opacity:.7} -/* Postes distants : cases de capacités, code d'appairage, commande. */ -.node-caps{display:flex;flex-wrap:wrap;gap:6px} -.node-cap{display:inline-flex;align-items:center;gap:5px;padding:5px 9px;border:1px solid var(--border);border-radius:7px;font-size:12px;cursor:pointer;user-select:none} -.node-cap input{margin:0} -.node-code{font-family:var(--mono,monospace);font-size:22px;font-weight:700;letter-spacing:3px;color:var(--accent);padding:8px 0} -.node-cmd{white-space:pre-wrap;word-break:break-all;background:var(--code-bg);border:1px solid var(--border);border-radius:7px;padding:8px;margin:6px 0;font-size:12px} - -/* Poste distant : sélecteur de cible d'exécution. */ -.node-target{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:8px} -.node-target-l{font-size:12px;color:var(--dim);margin-right:2px;width:100%} -.node-target-opt{display:inline-flex;align-items:center;gap:5px;padding:5px 9px;border:1px solid var(--border);border-radius:7px;font-size:12px;cursor:pointer} -.node-target-opt.on{border-color:var(--accent);color:var(--accent)} -.node-target-opt.off{opacity:.6} -.node-target-opt input{margin:0} - /* ===== Direction « Terre » : surfaces en cartes ======================= La charte pose des CARTES arrondies séparées par une gouttière, au lieu d'une colonne à filets. Bloc en fin de feuille pour prendre le pas sur les @@ -1756,13 +1727,6 @@ button:hover{border-color:var(--dim);color:var(--text)} 0 / 0 - - + + + +
+
+
+