Le correctif précédent ne pouvait pas s'appliquer. Il ne posait « -ngl auto »
que si la clé NGL était ABSENTE de config.env — or defaultConfig() y sème
NGL=999 sur chaque installation neuve. La quasi-totalité des configurations la
portent donc sans que personne ne l'ait choisie, le code la lisait comme un
choix délibéré, et l'abandon revenait intact :
W common_fit_params: failed to fit params to free device memory:
n_gpu_layers already set by user to 999, abort
999 n'a jamais été un nombre de couches : c'est la sentinelle historique
« toutes ». Sur un moteur qui sait mesurer la VRAM libre, elle devient donc
« -ngl auto », et Loki le dit sur stderr plutôt que de le faire en douce. Tout
autre nombre reste intouché — c'est un vrai choix. NGL=all force l'ancien
comportement, NGL=auto n'envoie toujours aucun drapeau, et un moteur ancien
reçoit toujours 999 (l'omettre le ferait tourner 100 % CPU).
- La décision sort dans nglArgs(), fonction pure : la seule question qui
demande le moteur arrive déjà tranchée, donc elle se teste sans lancer
llama-server. 10 cas couverts.
- binFitsLayersItself() double la lecture fine de l'aide par la présence de
--load-mode. Les deux sont arrivés dans la même vague ; une description
reformulée ou une colonne plus large ne doit pas faire conclure « moteur
incapable » et réimposer 999.
- L'aide de la clé et le sous-titre du champ disent la nouvelle règle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les llama-server récents ont fusionné --mlock, --mmap et --no-mmap dans un
seul --load-mode, et refusent désormais d'ajuster les couches GPU dès qu'on
leur impose un nombre : « n_gpu_layers already set by user to 999, abort ».
Le moteur pousse alors tout sur le GPU et meurt en cudaMalloc, ou se replie
à moitié sur le CPU — débit effondré, GPU à 100 %.
- Traduction des drapeaux dépréciés AU LANCEMENT, pas dans le preset :
--mlock → --load-mode mlock, --no-mmap → --load-mode none, --mmap →
--load-mode mmap. Les interrupteurs de l'interface continuent d'écrire
l'ancienne forme, et un moteur antérieur la reçoit telle quelle. Un
--load-mode écrit à la main dans EXTRA_ARGS gagne sur tout.
- -ngl auto par défaut quand le moteur propose la valeur, 999 sinon (l'omettre
sur un moteur ancien le ferait tourner 100 % CPU). NGL=<nombre> dans le
preset reste souverain.
- --parallel et -ngl ne sont plus ajoutés en double quand EXTRA_ARGS les porte
déjà : le moteur râlait (« specified multiple times ») et, pire, c'est la
dernière occurrence qui gagne — le réglage explicite du preset était donc
silencieusement écrasé par le défaut de Loki.
- Les capacités se lisent dans « <bin> --help », une seule fois par binaire
(binHelp met en cache) au lieu d'un lancement par question.
Dictée : quand le téléchargement du modèle whisper échoue (conteneur sans
réseau, Hugging Face injoignable), le serveur renvoyait bien la raison mais
l'interface affichait quand même « téléchargement (0 %) ». L'erreur cachée
laissait cliquer indéfiniment sur un micro qui n'écrira jamais rien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les llama-server récents ouvrent 4 slots par défaut : des tampons de
calcul GPU multipliés par quatre, pour un serveur qui ne sert qu'un
utilisateur. Résultat vécu après une mise à jour du moteur : une config
27B qui tournait très bien meurt en « cudaMalloc failed: out of
memory » en pleine génération, sans qu'aucun réglage n'ait changé. On
repasse explicitement à un slot — le comportement historique — et
PARALLEL=n reste disponible dans le preset pour qui veut du parallèle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'endpoint compatible OpenAI n'était pas servi par Loki : le panneau annonçait
l'adresse de llama-server lui-même, http://<ip>:8080/v1. Dans le déploiement de
référence de ce fork, cette adresse ne peut joindre personne — le port 8080
n'est pas publié par le conteneur, l'entrypoint sème HOST=127.0.0.1, et l'IP
annoncée est celle du bridge Docker. L'autre voie proposée, « exposer en public
(ajean.link) », exigeait un jeton de relais que ce fork ne permet plus
d'obtenir : l'interrupteur ne pouvait que renvoyer vers un panneau supprimé.
Désormais, Loki sert /v1/* SUR SON PROPRE PORT et relaie vers le moteur. L'API
est donc joignable partout où l'interface l'est — IP du réseau local, nom de
domaine, reverse proxy — sans publier de second port ni ouvrir le moteur.
Serveur
- mountOAI (llm_oai.go) monte /v1/ sur le mux, et RIEN d'autre : ni /metrics,
ni /props, ni /slots, qui divulgueraient le modèle chargé et l'état des slots.
Le filtre interne d'oaiHandler reste en seconde barrière.
- requireCompletionKey (web_auth.go) garde cette surface avec la clé des
COMPLÉTIONS, pas celle de pilotage : un client OpenAI n'a qu'un en-tête
Authorization, et on veut pouvoir lui donner l'accès au modèle sans le droit
de redémarrer la machine. Erreurs au format d'OpenAI (body.error.message), que
les SDK savent présenter. Le préflight CORS passe sans clé — il n'en porte
jamais, et le refuser casserait tout client tiers de navigateur.
- effectiveAPIKeyErr (backend_config.go) devient la source unique de la clé
exigée : base d'abord, config.env en repli, exactement comme le moteur. Sans
ce miroir, un API_KEY résiduel donnait un endpoint « ouvert » côté Loki et un
401 côté moteur, sans rien pour l'expliquer. Lecture ratée = refus, jamais
ouverture (même raisonnement que readWebKeyErr).
- oaiHandler passe à ReverseProxy.Rewrite : le port du moteur est relu à chaque
requête au lieu d'être figé à la construction — il visait l'ancien port dès
qu'on changeait PORT, jusqu'au redémarrage de Loki.
- withLocalAuth (relay_link.go) n'injecte plus la clé de pilotage sur /v1 : elle
aurait été refusée par la garde, et surtout relayée au moteur. Le trafic du
tunnel est marqué (en-tête effacé avant d'être posé, sinon un client le forge)
et la surface y reste fermée tant que oai_public est faux — la promesse du
tunnel est tenue.
Adresse affichée
- web_public_url.go : normalisation d'une adresse publique saisie à la main
(schéma ajouté, /v1 recopié toléré, chemin refusé), origine de la requête via
Host + X-Forwarded-Proto, et la règle de priorité entre les deux.
- Le calcul quitte le navigateur pour le serveur : c'est la concaténation côté
client qui produisait l'adresse fantôme.
Interface
- Le panneau perd l'interrupteur ajean.link et l'interrupteur d'écoute LAN — ce
dernier n'a plus d'objet, et deux interrupteurs pour « rendre l'IA joignable »
était la confusion à lever. La route /api/network et `loki network` restent
pour qui veut exposer le moteur en direct.
- Il gagne un champ « adresse publique » (facultatif, pour le reverse proxy) et
un avertissement rouge tant qu'aucune clé n'est définie — l'endpoint est
maintenant ouvert PARTOUT où l'interface l'est, ça ne se dit pas à voix basse.
Le démarrage de `loki web` le crie aussi.
Vérifié bout en bout sur le serveur réel : liste des modèles à travers Loki avec
la clé (200), sans la clé (401), et complétion en streaming dont les tokens
arrivent espacés de 120 ms — le flux traverse bien le double proxy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
Loki passait TOUJOURS -ngl, avec 999 comme repli quand le champ est vide :
vider « Couches sur GPU » ne changeait donc rien. Or llama.cpp sait ajuster
lui-même le nombre de couches à la mémoire libre (common_fit_params), mais y
renonce dès que la valeur est imposée — « n_gpu_layers already set by user to
999, abort ». D'où, sur une carte trop juste pour le contexte demandé, des
cudaMalloc en échec, un repli partiel sur le CPU, et un débit effondré alors
que le GPU tourne à 100 %.
NGL=auto n'envoie plus -ngl du tout. Le défaut reste 999 : omettre le drapeau
sur un llama.cpp antérieur à cet ajustement le ferait tourner 100 % CPU.
Le champ de l'UI passe en texte pour accepter « auto ».
Vérifié : NGL=auto → aucun -ngl dans la ligne de commande ; NGL=999 → -ngl 999.
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki)
- LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ;
units loki-engine / loki-ui ; binaire et aide CLI
- updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les
binaires AJEAN amont)
Conservé à l'identique : le domaine ajean.link (service de tunnel amont),
les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et
LICENSE (historique et licence de l'amont).
go build/vet/test : verts.