UI : moteur fourni par l'image, et journal du moteur repliable

Le panneau « Moteur » proposait d'installer llama.cpp alors que l'image le
fournit déjà (/app/llama-server) : cliquer lançait une compilation qui
échouait sur « outils manquants : cmake », l'image n'embarquant ni cmake ni
compilateur.
- engineProvided() : vrai en conteneur quand BIN désigne un exécutable
  existant HORS de LOKI_HOME (donc ni dépôt cloné, ni précompilé, ni backend
  custom, qui sont gérés par Loki) ; exposé en clé 'provided' de /api/llamacpp
- les routes install / install-custom / update refusent en 409 avec un
  message explicite au lieu de partir en build
- l'UI affiche l'état réel (chemin du binaire) et masque les trois modes

Le journal du moteur n'était repliable que par un second clic sur la pastille
d'état — sans chevron ni titre cliquable, contrairement à toutes les autres
sections de la barre latérale. Il devient un <details><summary> comme les
autres ; la pastille reste un raccourci et le chargement suit l'événement
toggle, quel que soit le moyen d'ouverture.

Vérifié dans l'image : provided=true, config_bin=/app/llama-server,
POST /api/llamacpp/install → 409 explicite, <details id="svc-log-box"> servi.
This commit is contained in:
Loki committed 2026-08-15 07:49:46 +00:00
1 parent 1e232a7ff3
commit 5b434a4a59
5 files changed
+166 -56

No files matched your search

+15 -5
View File
@@ -40,11 +40,12 @@
<!-- Avertissement de lancement (App Translocation macOS) — masqué par défaut. -->
<div id="app-warn" class="muted" style="display:none;font-size:11px;line-height:1.5;margin:0 0 10px;padding:8px 10px;border-radius:8px;color:var(--warn);border:1px solid color-mix(in srgb,var(--warn) 45%,transparent);background:color-mix(in srgb,var(--warn) 12%,transparent)"></div>
<div id="model-err" style="display:none;font-size:11px;line-height:1.5;margin:0 0 10px;padding:8px 10px;border-radius:8px;color:var(--err);border:1px solid color-mix(in srgb,var(--err) 45%,transparent);background:color-mix(in srgb,var(--err) 12%,transparent)"></div>
<!-- Journal du moteur : replié par défaut, ouvert au clic sur la pastille d'état.
<!-- Journal du moteur : replié par défaut, ouvert au clic sur la pastille d'état
OU par son propre chevron — c'est une section comme les autres, sinon on ne
trouvait pas comment la refermer.
C'est le seul endroit où l'on voit pourquoi un modèle refuse de charger. -->
<div id="svc-log-box" style="display:none;margin:0 0 10px">
<div class="row" style="justify-content:space-between;align-items:center">
<span class="io-cap">Journal du moteur</span>
<details id="svc-log-box" ontoggle="if(this.open){loadSvcLog();showPaths()}"><summary><span class="stitle">Journal du moteur</span></summary>
<div class="row" style="justify-content:flex-end;align-items:center">
<button onclick="loadSvcLog()" title="rafraîchir">↻</button>
</div>
<pre id="svc-log" class="muted" style="font-size:10px;line-height:1.45;max-height:220px;overflow:auto;white-space:pre-wrap;word-break:break-word;margin:6px 0 0"></pre>
@@ -54,7 +55,7 @@
<span class="io-cap">Emplacements</span>
</div>
<div id="paths-msg" class="muted" style="font-size:10px;line-height:1.5"></div>
</div>
</details>
<!-- GPU et RAM décrivent la même chose (les ressources de la machine) :
une seule section, deux blocs de statistique dans la même carte. -->
<details open><summary><span class="stitle">Machine</span></summary>
@@ -249,6 +250,15 @@
même sujet. Les trois modes sont des LIGNES de cette carte (elles
étaient des cartes dans une carte). -->
<div class="slist">
<!-- Moteur fourni par l'image Docker : aucun des trois modes ne s'applique
(rien à installer, et l'image n'embarque ni cmake ni compilateur). -->
<div id="lc-provided" class="rowbody" style="display:none">
<div style="font-size:12px;line-height:1.5">Moteur <b>fourni par l'image Docker</b> — llama.cpp officiel, déjà compilé.
<div class="muted" style="margin-top:4px">Rien à installer ni à mettre à jour ici : le moteur suit l'image.
Pour en changer (version épinglée, CPU, Vulkan), reconstruis l'image avec le build-arg <code>LLAMACPP_IMAGE</code>.</div>
<div class="muted" style="margin-top:4px"><code id="lc-provided-bin" style="word-break:break-all"></code></div>
</div>
</div>
<!-- La pastille « conseillé » n'est PAS écrite ici : le bon choix dépend de
la machine (voir lcRenderReco). Sur Linux + NVIDIA le précompilé ne
donne que du Vulkan — llama.cpp ne publie aucun binaire CUDA Linux —
+4 -4
View File
@@ -36,13 +36,13 @@ async function loadStatus(){
else { me.style.display='none'; }
}
}
// Journal du moteur — replié par défaut, on l'ouvre en cliquant la pastille.
// Journal du moteur — section repliable comme les autres. La pastille d'état
// reste un raccourci ; le chargement du journal est déclenché par l'événement
// `toggle` du <details>, qu'on l'ouvre par la pastille ou par son chevron.
function toggleSvcLog(){
const box=document.getElementById('svc-log-box');
if(!box) return;
const show = box.style.display==='none';
box.style.display = show ? '' : 'none';
if(show){ loadSvcLog(); showPaths(); }
box.open = !box.open;
}
async function loadSvcLog(){
const el=document.getElementById('svc-log');
+41 -19
View File
@@ -15,27 +15,34 @@ async function loadLlamacpp(){
const fastInstalled = !!pb.bin;
const optInstalled = !!s.bin;
lcRenderReco(s.reco);
lcRenderMode('fast', fastInstalled);
lcRenderMode('opt', optInstalled);
lcRenderCustomCard();
// Moteur fourni par l'image Docker : les trois modes d'installation n'ont
// aucun sens (rien à installer, aucun compilateur dans l'image). On affiche
// l'état réel à la place — les boutons du service, eux, restent utiles.
lcRenderProvided(s);
// Job en cours (page rechargée pendant une install) → on raccroche l'affichage.
if(s.job && s.job.exists && s.job.running && !lcPoll){
document.getElementById('lc-details').open = true;
lcStartPolling();
if(!s.provided){
lcRenderReco(s.reco);
lcRenderMode('fast', fastInstalled);
lcRenderMode('opt', optInstalled);
lcRenderCustomCard();
// Job en cours (page rechargée pendant une install) → on raccroche l'affichage.
if(s.job && s.job.exists && s.job.running && !lcPoll){
document.getElementById('lc-details').open = true;
lcStartPolling();
}
// Job terminé/interrompu qu'on n'a pas encore montré (rechargement APRÈS coup,
// typiquement quand le service a redémarré) : on l'affiche sans polling.
else if(s.job && s.job.exists && !s.job.running && !lcPoll && s.job.error && !lcEndShown){
document.getElementById('lc-details').open = true;
document.getElementById('lc-job').style.display = '';
lcLogNext = 0;
document.getElementById('lc-log').textContent = '';
lcEndShown = true;
lcPollJob(true); // quiet : simple rattrapage, pas de toast ni de rechargement
}
lcChipSync(s.job);
}
// Job terminé/interrompu qu'on n'a pas encore montré (rechargement APRÈS coup,
// typiquement quand le service a redémarré) : on l'affiche sans polling.
else if(s.job && s.job.exists && !s.job.running && !lcPoll && s.job.error && !lcEndShown){
document.getElementById('lc-details').open = true;
document.getElementById('lc-job').style.display = '';
lcLogNext = 0;
document.getElementById('lc-log').textContent = '';
lcEndShown = true;
lcPollJob(true); // quiet : simple rattrapage, pas de toast ni de rechargement
}
lcChipSync(s.job);
// Préchauffe la liste des cartes du moteur actif : interroger le moteur prend
// 1 à 3 s (init CUDA/Vulkan), et sans ça l'encart « cartes graphiques » de
// l'éditeur apparaissait après coup, une fois le reste déjà affiché.
@@ -90,6 +97,21 @@ function lcRenderReco(reco){
if(desc && reco && reco.why) desc.textContent = reco.why;
}
// Bascule entre « moteur de l'image » (une ligne d'état) et les trois modes
// d'installation gérés par Loki.
function lcRenderProvided(s){
const box = document.getElementById('lc-provided');
const modes = document.querySelector('.lc-modes');
if(!box || !modes) return;
box.style.display = s.provided ? '' : 'none';
modes.style.display = s.provided ? 'none' : '';
if(!s.provided) return;
const bin = document.getElementById('lc-provided-bin');
if(bin) bin.textContent = s.config_bin || '';
const job = document.getElementById('lc-job');
if(job) job.style.display = 'none';
}
function lcRenderMode(mode, installed){
const card = document.getElementById(mode==='fast' ? 'lc-mode-fast' : 'lc-mode-opt');
const state = document.getElementById(mode==='fast' ? 'lc-fast-state' : 'lc-opt-state');