mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-20 21:48:47 +02:00
UI backend llama.cpp : vue simplifiee (2 modes rapide/optimisee, plus de badge, sans jargon)
Refonte de la section pour la rendre utilisable sans connaissances techniques : - suppression du badge a cote du titre et de tous les details (commit, retard git, arch CUDA, nombre de jobs, chemins) ; une seule ligne 'Moteur actif : …' - deux cartes claires : Version rapide (precompile, conseillee) / Version optimisee (compilee) ; un clic installe, active ou bascule - bascule instantanee entre deux versions deja installees (nouvel endpoint /api/llamacpp/use : repointe BIN + redemarre, sans recompiler) - carte active : '✓ Actif' + 'mettre a jour' ; progression et logs replies sous 'details'
This commit is contained in:
1 parent
b0c1503ed8
commit
ff045ba21e
6 files changed
+260
-244
No files matched your search
+113
-122
@@ -290,7 +290,21 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
|||||||
h2,details>summary{font-size:10px}
|
h2,details>summary{font-size:10px}
|
||||||
#menubtn{width:32px;height:32px;font-size:15px;top:calc(6px + env(safe-area-inset-top))}
|
#menubtn{width:32px;height:32px;font-size:15px;top:calc(6px + env(safe-area-inset-top))}
|
||||||
}
|
}
|
||||||
/* --- Backend llama.cpp (gestion install/update depuis l'UI) --------------- */
|
/* --- Backend llama.cpp (2 modes : rapide / optimisée) --------------------- */
|
||||||
|
.lc-head{font-size:12px;line-height:1.5;margin-bottom:10px}
|
||||||
|
.lc-modes{display:flex;flex-direction:column;gap:8px}
|
||||||
|
.lc-modes.busy{opacity:.5;pointer-events:none}
|
||||||
|
.lc-mode{border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;transition:border-color .12s,background .12s}
|
||||||
|
.lc-mode:hover{border-color:var(--accent)}
|
||||||
|
.lc-mode.active{border-color:var(--accent);background:var(--panel);cursor:default}
|
||||||
|
.lc-mode-t{font-size:13px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}
|
||||||
|
.lc-reco{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--accent);border:1px solid var(--accent);border-radius:8px;padding:1px 6px}
|
||||||
|
.lc-mode-d{font-size:11px;color:var(--dim);margin-top:3px}
|
||||||
|
.lc-mode-state{font-size:11px;margin-top:7px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
|
||||||
|
.lc-mode-go{color:var(--accent)}
|
||||||
|
.lc-mode-active-tag{color:var(--accent);font-weight:600}
|
||||||
|
.lc-update-link{color:var(--dim);text-decoration:underline;cursor:pointer}
|
||||||
|
.lc-update-link:hover{color:var(--accent)}
|
||||||
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em;font-weight:600;padding:6px 0;margin-top:4px}
|
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em;font-weight:600;padding:6px 0;margin-top:4px}
|
||||||
.lc-logbar #lc-log-caret{font-size:9px;transition:transform .12s;display:inline-block}
|
.lc-logbar #lc-log-caret{font-size:9px;transition:transform .12s;display:inline-block}
|
||||||
.lc-logbar.open #lc-log-caret{transform:rotate(90deg)}
|
.lc-logbar.open #lc-log-caret{transform:rotate(90deg)}
|
||||||
@@ -394,19 +408,23 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
|||||||
<button onclick="apiKeyAction('clear')">retirer</button>
|
<button onclick="apiKeyAction('clear')">retirer</button>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<details id="lc-details"><summary>Backend llama.cpp <span id="lc-badge" style="margin-left:auto;margin-right:12px;font-size:10px"></span></summary>
|
<details id="lc-details"><summary>Backend llama.cpp</summary>
|
||||||
<div id="lc-status" class="muted" style="font-size:12px;line-height:1.6">…</div>
|
<div id="lc-status" class="lc-head muted">…</div>
|
||||||
<div class="row" id="lc-actions" style="margin-top:8px;flex-wrap:wrap">
|
<div class="lc-modes">
|
||||||
<button id="lc-prebuilt" onclick="lcPrebuilt()" style="border-color:var(--accent);color:var(--accent)">⚡ binaires précompilés</button>
|
<div class="lc-mode" id="lc-mode-fast" onclick="lcPick('fast')">
|
||||||
<button id="lc-install" onclick="lcInstall()" style="display:none">🔧 compiler (sources)</button>
|
<div class="lc-mode-t">⚡ Version rapide <span class="lc-reco">conseillé</span></div>
|
||||||
<button id="lc-check" onclick="lcCheck()">↥ vérifier les maj</button>
|
<div class="lc-mode-d">prête à l'emploi, installée en ~2 min</div>
|
||||||
<button id="lc-update" onclick="lcUpdate(false)" style="display:none;border-color:var(--accent);color:var(--accent)">⬆ mettre à jour</button>
|
<div class="lc-mode-state" id="lc-fast-state"></div>
|
||||||
<button id="lc-rebuild" onclick="lcUpdate(true)" style="display:none">🔧 recompiler</button>
|
</div>
|
||||||
|
<div class="lc-mode" id="lc-mode-opt" onclick="lcPick('opt')">
|
||||||
|
<div class="lc-mode-t">🔧 Version optimisée</div>
|
||||||
|
<div class="lc-mode-d">compilée pour ta machine, plus longue</div>
|
||||||
|
<div class="lc-mode-state" id="lc-opt-state"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="lc-check-msg" class="muted" style="font-size:11px;margin-top:6px;line-height:1.5"></div>
|
<div id="lc-job" style="display:none;margin-top:10px">
|
||||||
<div id="lc-job" style="display:none;margin-top:8px">
|
|
||||||
<div id="lc-job-phase" style="font-size:12px;display:flex;align-items:center;gap:6px"></div>
|
<div id="lc-job-phase" style="font-size:12px;display:flex;align-items:center;gap:6px"></div>
|
||||||
<div class="lc-logbar" onclick="lcToggleLog()"><span id="lc-log-caret">▸</span> logs du build</div>
|
<div class="lc-logbar" onclick="lcToggleLog()"><span id="lc-log-caret">▸</span> détails</div>
|
||||||
<pre id="lc-log" hidden></pre>
|
<pre id="lc-log" hidden></pre>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
@@ -1592,135 +1610,111 @@ async function send(){
|
|||||||
loadAll();
|
loadAll();
|
||||||
setInterval(loadStatus, 5000);
|
setInterval(loadStatus, 5000);
|
||||||
setInterval(loadVram, 3000);
|
setInterval(loadVram, 3000);
|
||||||
// Backend llama.cpp : statut, vérification des maj, install / update / rebuild
|
// Backend llama.cpp — vue simple : deux modes au choix.
|
||||||
// en job asynchrone côté serveur (logs + progression pollés sur /api/llamacpp/job).
|
// ⚡ Version rapide = binaires officiels précompilés (aucune compilation)
|
||||||
|
// 🔧 Version optimisée = compilée pour la machine
|
||||||
|
// Un clic sur une carte l'installe (ou l'active si déjà installée). La carte
|
||||||
|
// active propose « mettre à jour ». Aucun détail technique affiché.
|
||||||
let lcState = null, lcPoll = null, lcLogNext = 0;
|
let lcState = null, lcPoll = null, lcLogNext = 0;
|
||||||
|
|
||||||
function lcBadge(html){ document.getElementById('lc-badge').innerHTML = html; }
|
|
||||||
|
|
||||||
async function loadLlamacpp(){
|
async function loadLlamacpp(){
|
||||||
let s;
|
let s;
|
||||||
try{ s = await jget('/api/llamacpp'); }catch(_){ return; }
|
try{ s = await jget('/api/llamacpp'); }catch(_){ return; }
|
||||||
lcState = s;
|
lcState = s;
|
||||||
const st = document.getElementById('lc-status');
|
|
||||||
const parts = [];
|
|
||||||
if(!s.installed){
|
|
||||||
parts.push('<span style="color:var(--dim)">llama.cpp n\'est pas encore installé sur cette machine.</span>');
|
|
||||||
parts.push('<span style="color:var(--dim)">L\'installation clone le dépôt, détecte le GPU et compile <code>llama-server</code> automatiquement.</span>');
|
|
||||||
lcBadge('<span class="tag" style="opacity:.6">absent</span>');
|
|
||||||
} else {
|
|
||||||
const esc = t=>String(t||'').replace(/[<>&]/g,c=>({'<':'<','>':'>','&':'&'}[c]));
|
|
||||||
parts.push('commit <b style="color:var(--text)">'+esc(s.commit)+'</b>'+(s.branch?' <span style="opacity:.7">('+esc(s.branch)+')</span>':''));
|
|
||||||
if(s.commit_date) parts.push('<span style="opacity:.8">'+esc(String(s.commit_date).slice(0,16))+'</span> — '+esc(s.commit_msg));
|
|
||||||
if(s.bin){
|
|
||||||
parts.push('binaire : <span style="color:var(--ok)">✓ compilé</span>'+(s.in_use?' · <span style="color:var(--accent)">utilisé par la config</span>':' · <span style="opacity:.7">non utilisé par la config (BIN pointe ailleurs)</span>'));
|
|
||||||
} else {
|
|
||||||
parts.push('binaire : <span style="color:var(--err)">absent (pas encore compilé)</span>');
|
|
||||||
}
|
|
||||||
if(s.behind > 0) lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
|
||||||
else if(s.bin) lcBadge('<span class="tag ok">ok</span>');
|
|
||||||
else lcBadge('<span class="tag err">à compiler</span>');
|
|
||||||
}
|
|
||||||
if(s.plan){
|
|
||||||
const p = s.plan;
|
|
||||||
parts.push('accélérateur détecté : <b style="color:var(--text)">'+p.backend.toUpperCase()+'</b>'+(p.arch?' <span style="opacity:.7">(arch '+p.arch+')</span>':'')+' · '+p.jobs+' jobs');
|
|
||||||
}
|
|
||||||
// Mode actif : binaires officiels précompilés vs build local.
|
|
||||||
const pb = s.prebuilt || {};
|
const pb = s.prebuilt || {};
|
||||||
if(pb.in_use){
|
const fastActive = !!pb.in_use;
|
||||||
parts.push('mode : <b style="color:var(--accent)">⚡ binaires précompilés officiels</b>'+(pb.tag?' <span style="opacity:.7">('+pb.tag+')</span>':''));
|
const optActive = s.installed && s.in_use && !pb.in_use;
|
||||||
lcBadge('<span class="tag ok">précompilé</span>');
|
|
||||||
} else if(s.installed && s.in_use){
|
const head = document.getElementById('lc-status');
|
||||||
parts.push('mode : <b style="color:var(--text)">🔧 compilé depuis les sources</b>'+(pb.tag?' <span style="opacity:.6">(précompilé '+pb.tag+' aussi installé)</span>':''));
|
if(!fastActive && !optActive){
|
||||||
|
head.innerHTML = 'Le moteur d\'IA n\'est pas encore installé. Choisis une version ci-dessous — la <b>rapide</b> convient à presque tout le monde.';
|
||||||
|
} else if(fastActive){
|
||||||
|
head.innerHTML = 'Moteur actif : <b style="color:var(--accent)">version rapide</b>.';
|
||||||
|
} else {
|
||||||
|
head.innerHTML = 'Moteur actif : <b style="color:var(--accent)">version optimisée</b>.';
|
||||||
}
|
}
|
||||||
st.innerHTML = parts.join('<br>');
|
lcRenderMode('fast', fastActive, !!pb.bin);
|
||||||
document.getElementById('lc-install').style.display = s.installed ? 'none' : '';
|
lcRenderMode('opt', optActive, !!s.bin);
|
||||||
document.getElementById('lc-check').style.display = (s.installed || pb.in_use) ? '' : 'none';
|
|
||||||
document.getElementById('lc-update').style.display = (s.installed && !pb.in_use && (s.behind>0 || !s.bin)) ? '' : 'none';
|
// Job en cours (page rechargée pendant une install) → on raccroche l'affichage.
|
||||||
document.getElementById('lc-rebuild').style.display = (s.installed && !pb.in_use) ? '' : 'none';
|
|
||||||
document.getElementById('lc-prebuilt').textContent = pb.in_use ? '⚡ maj binaires précompilés' : '⚡ binaires précompilés';
|
|
||||||
// Job en cours (page rechargée pendant un build) → on raccroche le polling.
|
|
||||||
if(s.job && s.job.exists && s.job.running && !lcPoll){
|
if(s.job && s.job.exists && s.job.running && !lcPoll){
|
||||||
document.getElementById('lc-details').open = true;
|
document.getElementById('lc-details').open = true;
|
||||||
lcStartPolling();
|
lcStartPolling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcCheck(){
|
function lcRenderMode(mode, active, installed){
|
||||||
const btn = document.getElementById('lc-check');
|
const card = document.getElementById(mode==='fast' ? 'lc-mode-fast' : 'lc-mode-opt');
|
||||||
const msg = document.getElementById('lc-check-msg');
|
const state = document.getElementById(mode==='fast' ? 'lc-fast-state' : 'lc-opt-state');
|
||||||
btn.disabled = true; btn.textContent = '⏳ vérification…';
|
card.classList.toggle('active', active);
|
||||||
// Mode précompilé : on compare à la dernière release officielle, pas au git.
|
if(active){
|
||||||
if(lcState && lcState.prebuilt && lcState.prebuilt.in_use){
|
state.innerHTML = '<span class="lc-mode-active-tag">✓ Actif</span>'
|
||||||
try{
|
+ '<span class="lc-update-link" onclick="event.stopPropagation();lcUpdateActive()">↻ mettre à jour</span>';
|
||||||
const r = await jpost('/api/llamacpp/prebuilt/check');
|
} else if(installed){
|
||||||
if(!r.ok){ msg.innerHTML = '<span style="color:var(--err)">'+(r.error||'erreur')+'</span>'; return; }
|
state.innerHTML = '<span class="lc-mode-go">→ cliquer pour l\'activer</span>';
|
||||||
if(r.update){
|
} else {
|
||||||
msg.innerHTML = 'nouvelle release officielle : <b style="color:var(--accent)">'+r.latest+'</b> (installée : '+(r.current||'aucune')+')<br><span style="opacity:.8">'+String(r.variant||'').replace(/[<>&]/g,'')+' · ~'+r.size_mb+' Mo</span>';
|
state.innerHTML = '<span class="lc-mode-go">→ cliquer pour installer</span>';
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
|
||||||
} else {
|
|
||||||
msg.innerHTML = '<span style="color:var(--ok)">✓ à jour</span> ('+r.current+')';
|
|
||||||
}
|
|
||||||
} finally { btn.disabled = false; btn.textContent = '↥ vérifier les maj'; }
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try{
|
}
|
||||||
const r = await jpost('/api/llamacpp/check');
|
|
||||||
if(!r.ok){ msg.innerHTML = '<span style="color:var(--err)">'+(r.error||'erreur')+'</span>'; return; }
|
// Clic sur une carte : activer / installer le mode correspondant.
|
||||||
if(r.behind > 0){
|
async function lcPick(mode){
|
||||||
msg.innerHTML = '<b style="color:var(--accent)">'+r.behind+' commit(s) de retard</b> sur origin/'+r.branch+'<br>'+
|
const s = lcState || {}, pb = s.prebuilt || {};
|
||||||
'dernier distant : <b>'+r.remote+'</b> — '+String(r.remote_msg||'').replace(/[<>&]/g,'');
|
if(mode === 'fast'){
|
||||||
document.getElementById('lc-update').style.display = '';
|
if(pb.in_use) return; // déjà actif
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
if(pb.bin){ // déjà téléchargée → simple bascule
|
||||||
} else if(!r.has_binary){
|
if(!await askConfirm('Basculer sur la version rapide (déjà installée) ? Le moteur va redémarrer.', {title:'Version rapide', okText:'Basculer'})) return;
|
||||||
msg.innerHTML = 'sources à jour mais <b>binaire absent</b> — lance une recompilation.';
|
return lcUse('fast');
|
||||||
} else {
|
|
||||||
msg.innerHTML = '<span style="color:var(--ok)">✓ à jour</span> ('+r.local+')';
|
|
||||||
}
|
}
|
||||||
} finally { btn.disabled = false; btn.textContent = '↥ vérifier les maj'; }
|
if(!await askConfirm('Installer la version rapide de llama.cpp : téléchargement prêt à l\'emploi (~2 min, aucune compilation). Le moteur va redémarrer à la fin.', {title:'Version rapide', okText:'Installer'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/prebuilt', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
lcStartPolling();
|
||||||
|
} else {
|
||||||
|
if(s.installed && s.in_use && !pb.in_use) return; // déjà actif
|
||||||
|
if(s.bin){ // déjà compilée → simple bascule
|
||||||
|
if(!await askConfirm('Basculer sur la version optimisée (déjà compilée) ? Le moteur va redémarrer.', {title:'Version optimisée', okText:'Basculer'})) return;
|
||||||
|
return lcUse('opt');
|
||||||
|
}
|
||||||
|
if(!await askConfirm('Compiler la version optimisée pour ta machine. Ça peut prendre de longues minutes (surtout avec une carte NVIDIA). Le moteur va redémarrer à la fin.', {title:'Version optimisée', okText:'Compiler'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/install', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
lcStartPolling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcInstall(){
|
// Bascule instantanée entre deux versions déjà installées (pas de recompilation).
|
||||||
if(!await askConfirm('Cloner llama.cpp puis compiler llama-server pour cette machine (détection GPU automatique). La compilation peut prendre de longues minutes (surtout en CUDA).', {title:'Installer llama.cpp ?', okText:'Installer'})) return;
|
async function lcUse(mode){
|
||||||
const r = await jpost('/api/llamacpp/install', {});
|
toast('bascule…');
|
||||||
|
const r = await jpost('/api/llamacpp/use', {mode});
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
toast(mode==='fast' ? 'version rapide activée' : 'version optimisée activée');
|
||||||
|
setTimeout(loadAll, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// « Mettre à jour » sur la carte active.
|
||||||
|
async function lcUpdateActive(){
|
||||||
|
const s = lcState || {}, pb = s.prebuilt || {};
|
||||||
|
if(pb.in_use){
|
||||||
|
if(!await askConfirm('Vérifier et installer la dernière version rapide. Le moteur va redémarrer.', {title:'Mettre à jour', okText:'Mettre à jour'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/prebuilt', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
} else {
|
||||||
|
if(!await askConfirm('Vérifier et installer la dernière version optimisée (recompilation si besoin). Le moteur va redémarrer.', {title:'Mettre à jour', okText:'Mettre à jour'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/update', {clean:false});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
}
|
||||||
lcStartPolling();
|
lcStartPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcPrebuilt(){
|
// --- Progression de l'installation (téléchargement / compilation) ----------
|
||||||
const inUse = lcState && lcState.prebuilt && lcState.prebuilt.in_use;
|
function lcBusy(on){ document.querySelector('.lc-modes').classList.toggle('busy', on); }
|
||||||
const msg = inUse
|
|
||||||
? 'Télécharger la dernière release officielle précompilée de llama.cpp (~2 min, aucune compilation). Le service sera arrêté pendant le remplacement puis redémarré.'
|
|
||||||
: 'Télécharger les binaires OFFICIELS précompilés de llama.cpp au lieu de compiler (~2 min). Le variant est choisi automatiquement (CUDA / Vulkan / Metal / CPU) et BIN pointera dessus. Le build local, s\'il existe, reste sur le disque — tu peux y revenir via l\'éditeur de preset. Note : builds génériques (pas de tuning natif) ; sous Linux il n\'existe pas de build CUDA officiel (variant Vulkan).';
|
|
||||||
if(!await askConfirm(msg, {title: inUse ? 'Mettre à jour les binaires précompilés ?' : 'Passer aux binaires précompilés ?', okText: inUse ? 'Mettre à jour' : 'Télécharger'})) return;
|
|
||||||
const r = await jpost('/api/llamacpp/prebuilt', {});
|
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
|
||||||
lcStartPolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function lcUpdate(clean){
|
|
||||||
const msg = clean
|
|
||||||
? 'Recompiler llama.cpp from scratch (build/ supprimé). Le service sera arrêté pendant le build puis redémarré.'
|
|
||||||
: 'Mettre à jour llama.cpp (git pull) et recompiler. Le service sera arrêté pendant le build puis redémarré.';
|
|
||||||
if(!await askConfirm(msg, {title: clean ? 'Recompiler llama.cpp ?' : 'Mettre à jour llama.cpp ?', okText: clean ? 'Recompiler' : 'Mettre à jour'})) return;
|
|
||||||
const r = await jpost('/api/llamacpp/update', {clean: !!clean});
|
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
|
||||||
lcStartPolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
function lcSetButtons(disabled){
|
|
||||||
['lc-install','lc-check','lc-update','lc-rebuild','lc-prebuilt'].forEach(id=>{
|
|
||||||
const b=document.getElementById(id); if(b) b.disabled = disabled;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function lcStartPolling(){
|
function lcStartPolling(){
|
||||||
const jobBox = document.getElementById('lc-job');
|
document.getElementById('lc-job').style.display = '';
|
||||||
jobBox.style.display = '';
|
|
||||||
document.getElementById('lc-log').textContent = '';
|
document.getElementById('lc-log').textContent = '';
|
||||||
lcLogNext = 0;
|
lcLogNext = 0;
|
||||||
lcSetButtons(true);
|
lcBusy(true);
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)"><span class="lc-spin">⏳</span> build</span>');
|
|
||||||
if(lcPoll) clearInterval(lcPoll);
|
if(lcPoll) clearInterval(lcPoll);
|
||||||
lcPoll = setInterval(lcPollJob, 1000);
|
lcPoll = setInterval(lcPollJob, 1000);
|
||||||
lcPollJob();
|
lcPollJob();
|
||||||
@@ -1731,7 +1725,6 @@ async function lcPollJob(){
|
|||||||
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
||||||
if(!j.exists) return;
|
if(!j.exists) return;
|
||||||
const phaseEl = document.getElementById('lc-job-phase');
|
const phaseEl = document.getElementById('lc-job-phase');
|
||||||
// Logs incrémentaux (offset absolu — le serveur tronque le début si besoin).
|
|
||||||
if(j.lines && j.lines.length){
|
if(j.lines && j.lines.length){
|
||||||
const pre = document.getElementById('lc-log');
|
const pre = document.getElementById('lc-log');
|
||||||
const stick = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 20;
|
const stick = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 20;
|
||||||
@@ -1743,21 +1736,19 @@ async function lcPollJob(){
|
|||||||
phaseEl.innerHTML = '<span class="lc-spin">⏳</span> <span>'+String(j.phase||'…').replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span class="lc-spin">⏳</span> <span>'+String(j.phase||'…').replace(/[<>&]/g,'')+'</span>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Terminé.
|
|
||||||
clearInterval(lcPoll); lcPoll = null;
|
clearInterval(lcPoll); lcPoll = null;
|
||||||
lcSetButtons(false);
|
lcBusy(false);
|
||||||
if(j.error){
|
if(j.error){
|
||||||
phaseEl.innerHTML = '<span style="color:var(--err)">✗ '+String(j.error).replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span style="color:var(--err)">✗ '+String(j.error).replace(/[<>&]/g,'')+'</span>';
|
||||||
// Ouvre les logs pour que l'erreur réelle soit visible tout de suite.
|
|
||||||
const pre = document.getElementById('lc-log');
|
const pre = document.getElementById('lc-log');
|
||||||
if(pre.hasAttribute('hidden')) lcToggleLog();
|
if(pre.hasAttribute('hidden')) lcToggleLog();
|
||||||
pre.scrollTop = pre.scrollHeight;
|
pre.scrollTop = pre.scrollHeight;
|
||||||
toast('build llama.cpp en échec');
|
toast('échec — voir les détails');
|
||||||
} else {
|
} else {
|
||||||
phaseEl.innerHTML = '<span style="color:var(--ok)">✓ '+String(j.phase||'terminé').replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span style="color:var(--ok)">✓ '+String(j.phase||'terminé').replace(/[<>&]/g,'')+'</span>';
|
||||||
toast('llama.cpp : '+(j.phase||'terminé'));
|
toast('c\'est prêt ✓');
|
||||||
}
|
}
|
||||||
loadAll(); // rafraîchit tout (config, statut service… et ce panneau)
|
loadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function lcToggleLog(){
|
function lcToggleLog(){
|
||||||
|
|||||||
@@ -116,19 +116,23 @@
|
|||||||
<button onclick="apiKeyAction('clear')">retirer</button>
|
<button onclick="apiKeyAction('clear')">retirer</button>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<details id="lc-details"><summary>Backend llama.cpp <span id="lc-badge" style="margin-left:auto;margin-right:12px;font-size:10px"></span></summary>
|
<details id="lc-details"><summary>Backend llama.cpp</summary>
|
||||||
<div id="lc-status" class="muted" style="font-size:12px;line-height:1.6">…</div>
|
<div id="lc-status" class="lc-head muted">…</div>
|
||||||
<div class="row" id="lc-actions" style="margin-top:8px;flex-wrap:wrap">
|
<div class="lc-modes">
|
||||||
<button id="lc-prebuilt" onclick="lcPrebuilt()" style="border-color:var(--accent);color:var(--accent)">⚡ binaires précompilés</button>
|
<div class="lc-mode" id="lc-mode-fast" onclick="lcPick('fast')">
|
||||||
<button id="lc-install" onclick="lcInstall()" style="display:none">🔧 compiler (sources)</button>
|
<div class="lc-mode-t">⚡ Version rapide <span class="lc-reco">conseillé</span></div>
|
||||||
<button id="lc-check" onclick="lcCheck()">↥ vérifier les maj</button>
|
<div class="lc-mode-d">prête à l'emploi, installée en ~2 min</div>
|
||||||
<button id="lc-update" onclick="lcUpdate(false)" style="display:none;border-color:var(--accent);color:var(--accent)">⬆ mettre à jour</button>
|
<div class="lc-mode-state" id="lc-fast-state"></div>
|
||||||
<button id="lc-rebuild" onclick="lcUpdate(true)" style="display:none">🔧 recompiler</button>
|
</div>
|
||||||
|
<div class="lc-mode" id="lc-mode-opt" onclick="lcPick('opt')">
|
||||||
|
<div class="lc-mode-t">🔧 Version optimisée</div>
|
||||||
|
<div class="lc-mode-d">compilée pour ta machine, plus longue</div>
|
||||||
|
<div class="lc-mode-state" id="lc-opt-state"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="lc-check-msg" class="muted" style="font-size:11px;margin-top:6px;line-height:1.5"></div>
|
<div id="lc-job" style="display:none;margin-top:10px">
|
||||||
<div id="lc-job" style="display:none;margin-top:8px">
|
|
||||||
<div id="lc-job-phase" style="font-size:12px;display:flex;align-items:center;gap:6px"></div>
|
<div id="lc-job-phase" style="font-size:12px;display:flex;align-items:center;gap:6px"></div>
|
||||||
<div class="lc-logbar" onclick="lcToggleLog()"><span id="lc-log-caret">▸</span> logs du build</div>
|
<div class="lc-logbar" onclick="lcToggleLog()"><span id="lc-log-caret">▸</span> détails</div>
|
||||||
<pre id="lc-log" hidden></pre>
|
<pre id="lc-log" hidden></pre>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@@ -1,132 +1,108 @@
|
|||||||
// Backend llama.cpp : statut, vérification des maj, install / update / rebuild
|
// Backend llama.cpp — vue simple : deux modes au choix.
|
||||||
// en job asynchrone côté serveur (logs + progression pollés sur /api/llamacpp/job).
|
// ⚡ Version rapide = binaires officiels précompilés (aucune compilation)
|
||||||
|
// 🔧 Version optimisée = compilée pour la machine
|
||||||
|
// Un clic sur une carte l'installe (ou l'active si déjà installée). La carte
|
||||||
|
// active propose « mettre à jour ». Aucun détail technique affiché.
|
||||||
let lcState = null, lcPoll = null, lcLogNext = 0;
|
let lcState = null, lcPoll = null, lcLogNext = 0;
|
||||||
|
|
||||||
function lcBadge(html){ document.getElementById('lc-badge').innerHTML = html; }
|
|
||||||
|
|
||||||
async function loadLlamacpp(){
|
async function loadLlamacpp(){
|
||||||
let s;
|
let s;
|
||||||
try{ s = await jget('/api/llamacpp'); }catch(_){ return; }
|
try{ s = await jget('/api/llamacpp'); }catch(_){ return; }
|
||||||
lcState = s;
|
lcState = s;
|
||||||
const st = document.getElementById('lc-status');
|
|
||||||
const parts = [];
|
|
||||||
if(!s.installed){
|
|
||||||
parts.push('<span style="color:var(--dim)">llama.cpp n\'est pas encore installé sur cette machine.</span>');
|
|
||||||
parts.push('<span style="color:var(--dim)">L\'installation clone le dépôt, détecte le GPU et compile <code>llama-server</code> automatiquement.</span>');
|
|
||||||
lcBadge('<span class="tag" style="opacity:.6">absent</span>');
|
|
||||||
} else {
|
|
||||||
const esc = t=>String(t||'').replace(/[<>&]/g,c=>({'<':'<','>':'>','&':'&'}[c]));
|
|
||||||
parts.push('commit <b style="color:var(--text)">'+esc(s.commit)+'</b>'+(s.branch?' <span style="opacity:.7">('+esc(s.branch)+')</span>':''));
|
|
||||||
if(s.commit_date) parts.push('<span style="opacity:.8">'+esc(String(s.commit_date).slice(0,16))+'</span> — '+esc(s.commit_msg));
|
|
||||||
if(s.bin){
|
|
||||||
parts.push('binaire : <span style="color:var(--ok)">✓ compilé</span>'+(s.in_use?' · <span style="color:var(--accent)">utilisé par la config</span>':' · <span style="opacity:.7">non utilisé par la config (BIN pointe ailleurs)</span>'));
|
|
||||||
} else {
|
|
||||||
parts.push('binaire : <span style="color:var(--err)">absent (pas encore compilé)</span>');
|
|
||||||
}
|
|
||||||
if(s.behind > 0) lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
|
||||||
else if(s.bin) lcBadge('<span class="tag ok">ok</span>');
|
|
||||||
else lcBadge('<span class="tag err">à compiler</span>');
|
|
||||||
}
|
|
||||||
if(s.plan){
|
|
||||||
const p = s.plan;
|
|
||||||
parts.push('accélérateur détecté : <b style="color:var(--text)">'+p.backend.toUpperCase()+'</b>'+(p.arch?' <span style="opacity:.7">(arch '+p.arch+')</span>':'')+' · '+p.jobs+' jobs');
|
|
||||||
}
|
|
||||||
// Mode actif : binaires officiels précompilés vs build local.
|
|
||||||
const pb = s.prebuilt || {};
|
const pb = s.prebuilt || {};
|
||||||
if(pb.in_use){
|
const fastActive = !!pb.in_use;
|
||||||
parts.push('mode : <b style="color:var(--accent)">⚡ binaires précompilés officiels</b>'+(pb.tag?' <span style="opacity:.7">('+pb.tag+')</span>':''));
|
const optActive = s.installed && s.in_use && !pb.in_use;
|
||||||
lcBadge('<span class="tag ok">précompilé</span>');
|
|
||||||
} else if(s.installed && s.in_use){
|
const head = document.getElementById('lc-status');
|
||||||
parts.push('mode : <b style="color:var(--text)">🔧 compilé depuis les sources</b>'+(pb.tag?' <span style="opacity:.6">(précompilé '+pb.tag+' aussi installé)</span>':''));
|
if(!fastActive && !optActive){
|
||||||
|
head.innerHTML = 'Le moteur d\'IA n\'est pas encore installé. Choisis une version ci-dessous — la <b>rapide</b> convient à presque tout le monde.';
|
||||||
|
} else if(fastActive){
|
||||||
|
head.innerHTML = 'Moteur actif : <b style="color:var(--accent)">version rapide</b>.';
|
||||||
|
} else {
|
||||||
|
head.innerHTML = 'Moteur actif : <b style="color:var(--accent)">version optimisée</b>.';
|
||||||
}
|
}
|
||||||
st.innerHTML = parts.join('<br>');
|
lcRenderMode('fast', fastActive, !!pb.bin);
|
||||||
document.getElementById('lc-install').style.display = s.installed ? 'none' : '';
|
lcRenderMode('opt', optActive, !!s.bin);
|
||||||
document.getElementById('lc-check').style.display = (s.installed || pb.in_use) ? '' : 'none';
|
|
||||||
document.getElementById('lc-update').style.display = (s.installed && !pb.in_use && (s.behind>0 || !s.bin)) ? '' : 'none';
|
// Job en cours (page rechargée pendant une install) → on raccroche l'affichage.
|
||||||
document.getElementById('lc-rebuild').style.display = (s.installed && !pb.in_use) ? '' : 'none';
|
|
||||||
document.getElementById('lc-prebuilt').textContent = pb.in_use ? '⚡ maj binaires précompilés' : '⚡ binaires précompilés';
|
|
||||||
// Job en cours (page rechargée pendant un build) → on raccroche le polling.
|
|
||||||
if(s.job && s.job.exists && s.job.running && !lcPoll){
|
if(s.job && s.job.exists && s.job.running && !lcPoll){
|
||||||
document.getElementById('lc-details').open = true;
|
document.getElementById('lc-details').open = true;
|
||||||
lcStartPolling();
|
lcStartPolling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcCheck(){
|
function lcRenderMode(mode, active, installed){
|
||||||
const btn = document.getElementById('lc-check');
|
const card = document.getElementById(mode==='fast' ? 'lc-mode-fast' : 'lc-mode-opt');
|
||||||
const msg = document.getElementById('lc-check-msg');
|
const state = document.getElementById(mode==='fast' ? 'lc-fast-state' : 'lc-opt-state');
|
||||||
btn.disabled = true; btn.textContent = '⏳ vérification…';
|
card.classList.toggle('active', active);
|
||||||
// Mode précompilé : on compare à la dernière release officielle, pas au git.
|
if(active){
|
||||||
if(lcState && lcState.prebuilt && lcState.prebuilt.in_use){
|
state.innerHTML = '<span class="lc-mode-active-tag">✓ Actif</span>'
|
||||||
try{
|
+ '<span class="lc-update-link" onclick="event.stopPropagation();lcUpdateActive()">↻ mettre à jour</span>';
|
||||||
const r = await jpost('/api/llamacpp/prebuilt/check');
|
} else if(installed){
|
||||||
if(!r.ok){ msg.innerHTML = '<span style="color:var(--err)">'+(r.error||'erreur')+'</span>'; return; }
|
state.innerHTML = '<span class="lc-mode-go">→ cliquer pour l\'activer</span>';
|
||||||
if(r.update){
|
} else {
|
||||||
msg.innerHTML = 'nouvelle release officielle : <b style="color:var(--accent)">'+r.latest+'</b> (installée : '+(r.current||'aucune')+')<br><span style="opacity:.8">'+String(r.variant||'').replace(/[<>&]/g,'')+' · ~'+r.size_mb+' Mo</span>';
|
state.innerHTML = '<span class="lc-mode-go">→ cliquer pour installer</span>';
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
|
||||||
} else {
|
|
||||||
msg.innerHTML = '<span style="color:var(--ok)">✓ à jour</span> ('+r.current+')';
|
|
||||||
}
|
|
||||||
} finally { btn.disabled = false; btn.textContent = '↥ vérifier les maj'; }
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try{
|
}
|
||||||
const r = await jpost('/api/llamacpp/check');
|
|
||||||
if(!r.ok){ msg.innerHTML = '<span style="color:var(--err)">'+(r.error||'erreur')+'</span>'; return; }
|
// Clic sur une carte : activer / installer le mode correspondant.
|
||||||
if(r.behind > 0){
|
async function lcPick(mode){
|
||||||
msg.innerHTML = '<b style="color:var(--accent)">'+r.behind+' commit(s) de retard</b> sur origin/'+r.branch+'<br>'+
|
const s = lcState || {}, pb = s.prebuilt || {};
|
||||||
'dernier distant : <b>'+r.remote+'</b> — '+String(r.remote_msg||'').replace(/[<>&]/g,'');
|
if(mode === 'fast'){
|
||||||
document.getElementById('lc-update').style.display = '';
|
if(pb.in_use) return; // déjà actif
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)">maj dispo</span>');
|
if(pb.bin){ // déjà téléchargée → simple bascule
|
||||||
} else if(!r.has_binary){
|
if(!await askConfirm('Basculer sur la version rapide (déjà installée) ? Le moteur va redémarrer.', {title:'Version rapide', okText:'Basculer'})) return;
|
||||||
msg.innerHTML = 'sources à jour mais <b>binaire absent</b> — lance une recompilation.';
|
return lcUse('fast');
|
||||||
} else {
|
|
||||||
msg.innerHTML = '<span style="color:var(--ok)">✓ à jour</span> ('+r.local+')';
|
|
||||||
}
|
}
|
||||||
} finally { btn.disabled = false; btn.textContent = '↥ vérifier les maj'; }
|
if(!await askConfirm('Installer la version rapide de llama.cpp : téléchargement prêt à l\'emploi (~2 min, aucune compilation). Le moteur va redémarrer à la fin.', {title:'Version rapide', okText:'Installer'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/prebuilt', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
lcStartPolling();
|
||||||
|
} else {
|
||||||
|
if(s.installed && s.in_use && !pb.in_use) return; // déjà actif
|
||||||
|
if(s.bin){ // déjà compilée → simple bascule
|
||||||
|
if(!await askConfirm('Basculer sur la version optimisée (déjà compilée) ? Le moteur va redémarrer.', {title:'Version optimisée', okText:'Basculer'})) return;
|
||||||
|
return lcUse('opt');
|
||||||
|
}
|
||||||
|
if(!await askConfirm('Compiler la version optimisée pour ta machine. Ça peut prendre de longues minutes (surtout avec une carte NVIDIA). Le moteur va redémarrer à la fin.', {title:'Version optimisée', okText:'Compiler'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/install', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
lcStartPolling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcInstall(){
|
// Bascule instantanée entre deux versions déjà installées (pas de recompilation).
|
||||||
if(!await askConfirm('Cloner llama.cpp puis compiler llama-server pour cette machine (détection GPU automatique). La compilation peut prendre de longues minutes (surtout en CUDA).', {title:'Installer llama.cpp ?', okText:'Installer'})) return;
|
async function lcUse(mode){
|
||||||
const r = await jpost('/api/llamacpp/install', {});
|
toast('bascule…');
|
||||||
|
const r = await jpost('/api/llamacpp/use', {mode});
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
toast(mode==='fast' ? 'version rapide activée' : 'version optimisée activée');
|
||||||
|
setTimeout(loadAll, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// « Mettre à jour » sur la carte active.
|
||||||
|
async function lcUpdateActive(){
|
||||||
|
const s = lcState || {}, pb = s.prebuilt || {};
|
||||||
|
if(pb.in_use){
|
||||||
|
if(!await askConfirm('Vérifier et installer la dernière version rapide. Le moteur va redémarrer.', {title:'Mettre à jour', okText:'Mettre à jour'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/prebuilt', {});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
} else {
|
||||||
|
if(!await askConfirm('Vérifier et installer la dernière version optimisée (recompilation si besoin). Le moteur va redémarrer.', {title:'Mettre à jour', okText:'Mettre à jour'})) return;
|
||||||
|
const r = await jpost('/api/llamacpp/update', {clean:false});
|
||||||
|
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
||||||
|
}
|
||||||
lcStartPolling();
|
lcStartPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lcPrebuilt(){
|
// --- Progression de l'installation (téléchargement / compilation) ----------
|
||||||
const inUse = lcState && lcState.prebuilt && lcState.prebuilt.in_use;
|
function lcBusy(on){ document.querySelector('.lc-modes').classList.toggle('busy', on); }
|
||||||
const msg = inUse
|
|
||||||
? 'Télécharger la dernière release officielle précompilée de llama.cpp (~2 min, aucune compilation). Le service sera arrêté pendant le remplacement puis redémarré.'
|
|
||||||
: 'Télécharger les binaires OFFICIELS précompilés de llama.cpp au lieu de compiler (~2 min). Le variant est choisi automatiquement (CUDA / Vulkan / Metal / CPU) et BIN pointera dessus. Le build local, s\'il existe, reste sur le disque — tu peux y revenir via l\'éditeur de preset. Note : builds génériques (pas de tuning natif) ; sous Linux il n\'existe pas de build CUDA officiel (variant Vulkan).';
|
|
||||||
if(!await askConfirm(msg, {title: inUse ? 'Mettre à jour les binaires précompilés ?' : 'Passer aux binaires précompilés ?', okText: inUse ? 'Mettre à jour' : 'Télécharger'})) return;
|
|
||||||
const r = await jpost('/api/llamacpp/prebuilt', {});
|
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
|
||||||
lcStartPolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function lcUpdate(clean){
|
|
||||||
const msg = clean
|
|
||||||
? 'Recompiler llama.cpp from scratch (build/ supprimé). Le service sera arrêté pendant le build puis redémarré.'
|
|
||||||
: 'Mettre à jour llama.cpp (git pull) et recompiler. Le service sera arrêté pendant le build puis redémarré.';
|
|
||||||
if(!await askConfirm(msg, {title: clean ? 'Recompiler llama.cpp ?' : 'Mettre à jour llama.cpp ?', okText: clean ? 'Recompiler' : 'Mettre à jour'})) return;
|
|
||||||
const r = await jpost('/api/llamacpp/update', {clean: !!clean});
|
|
||||||
if(!r.ok){ toast('erreur : '+(r.error||'')); return; }
|
|
||||||
lcStartPolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
function lcSetButtons(disabled){
|
|
||||||
['lc-install','lc-check','lc-update','lc-rebuild','lc-prebuilt'].forEach(id=>{
|
|
||||||
const b=document.getElementById(id); if(b) b.disabled = disabled;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function lcStartPolling(){
|
function lcStartPolling(){
|
||||||
const jobBox = document.getElementById('lc-job');
|
document.getElementById('lc-job').style.display = '';
|
||||||
jobBox.style.display = '';
|
|
||||||
document.getElementById('lc-log').textContent = '';
|
document.getElementById('lc-log').textContent = '';
|
||||||
lcLogNext = 0;
|
lcLogNext = 0;
|
||||||
lcSetButtons(true);
|
lcBusy(true);
|
||||||
lcBadge('<span class="tag" style="color:var(--accent);border-color:var(--accent)"><span class="lc-spin">⏳</span> build</span>');
|
|
||||||
if(lcPoll) clearInterval(lcPoll);
|
if(lcPoll) clearInterval(lcPoll);
|
||||||
lcPoll = setInterval(lcPollJob, 1000);
|
lcPoll = setInterval(lcPollJob, 1000);
|
||||||
lcPollJob();
|
lcPollJob();
|
||||||
@@ -137,7 +113,6 @@ async function lcPollJob(){
|
|||||||
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
try{ j = await jget('/api/llamacpp/job?from='+lcLogNext); }catch(_){ return; }
|
||||||
if(!j.exists) return;
|
if(!j.exists) return;
|
||||||
const phaseEl = document.getElementById('lc-job-phase');
|
const phaseEl = document.getElementById('lc-job-phase');
|
||||||
// Logs incrémentaux (offset absolu — le serveur tronque le début si besoin).
|
|
||||||
if(j.lines && j.lines.length){
|
if(j.lines && j.lines.length){
|
||||||
const pre = document.getElementById('lc-log');
|
const pre = document.getElementById('lc-log');
|
||||||
const stick = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 20;
|
const stick = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 20;
|
||||||
@@ -149,21 +124,19 @@ async function lcPollJob(){
|
|||||||
phaseEl.innerHTML = '<span class="lc-spin">⏳</span> <span>'+String(j.phase||'…').replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span class="lc-spin">⏳</span> <span>'+String(j.phase||'…').replace(/[<>&]/g,'')+'</span>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Terminé.
|
|
||||||
clearInterval(lcPoll); lcPoll = null;
|
clearInterval(lcPoll); lcPoll = null;
|
||||||
lcSetButtons(false);
|
lcBusy(false);
|
||||||
if(j.error){
|
if(j.error){
|
||||||
phaseEl.innerHTML = '<span style="color:var(--err)">✗ '+String(j.error).replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span style="color:var(--err)">✗ '+String(j.error).replace(/[<>&]/g,'')+'</span>';
|
||||||
// Ouvre les logs pour que l'erreur réelle soit visible tout de suite.
|
|
||||||
const pre = document.getElementById('lc-log');
|
const pre = document.getElementById('lc-log');
|
||||||
if(pre.hasAttribute('hidden')) lcToggleLog();
|
if(pre.hasAttribute('hidden')) lcToggleLog();
|
||||||
pre.scrollTop = pre.scrollHeight;
|
pre.scrollTop = pre.scrollHeight;
|
||||||
toast('build llama.cpp en échec');
|
toast('échec — voir les détails');
|
||||||
} else {
|
} else {
|
||||||
phaseEl.innerHTML = '<span style="color:var(--ok)">✓ '+String(j.phase||'terminé').replace(/[<>&]/g,'')+'</span>';
|
phaseEl.innerHTML = '<span style="color:var(--ok)">✓ '+String(j.phase||'terminé').replace(/[<>&]/g,'')+'</span>';
|
||||||
toast('llama.cpp : '+(j.phase||'terminé'));
|
toast('c\'est prêt ✓');
|
||||||
}
|
}
|
||||||
loadAll(); // rafraîchit tout (config, statut service… et ce panneau)
|
loadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function lcToggleLog(){
|
function lcToggleLog(){
|
||||||
|
|||||||
@@ -271,7 +271,21 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
|||||||
h2,details>summary{font-size:10px}
|
h2,details>summary{font-size:10px}
|
||||||
#menubtn{width:32px;height:32px;font-size:15px;top:calc(6px + env(safe-area-inset-top))}
|
#menubtn{width:32px;height:32px;font-size:15px;top:calc(6px + env(safe-area-inset-top))}
|
||||||
}
|
}
|
||||||
/* --- Backend llama.cpp (gestion install/update depuis l'UI) --------------- */
|
/* --- Backend llama.cpp (2 modes : rapide / optimisée) --------------------- */
|
||||||
|
.lc-head{font-size:12px;line-height:1.5;margin-bottom:10px}
|
||||||
|
.lc-modes{display:flex;flex-direction:column;gap:8px}
|
||||||
|
.lc-modes.busy{opacity:.5;pointer-events:none}
|
||||||
|
.lc-mode{border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;transition:border-color .12s,background .12s}
|
||||||
|
.lc-mode:hover{border-color:var(--accent)}
|
||||||
|
.lc-mode.active{border-color:var(--accent);background:var(--panel);cursor:default}
|
||||||
|
.lc-mode-t{font-size:13px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}
|
||||||
|
.lc-reco{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--accent);border:1px solid var(--accent);border-radius:8px;padding:1px 6px}
|
||||||
|
.lc-mode-d{font-size:11px;color:var(--dim);margin-top:3px}
|
||||||
|
.lc-mode-state{font-size:11px;margin-top:7px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
|
||||||
|
.lc-mode-go{color:var(--accent)}
|
||||||
|
.lc-mode-active-tag{color:var(--accent);font-weight:600}
|
||||||
|
.lc-update-link{color:var(--dim);text-decoration:underline;cursor:pointer}
|
||||||
|
.lc-update-link:hover{color:var(--accent)}
|
||||||
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em;font-weight:600;padding:6px 0;margin-top:4px}
|
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em;font-weight:600;padding:6px 0;margin-top:4px}
|
||||||
.lc-logbar #lc-log-caret{font-size:9px;transition:transform .12s;display:inline-block}
|
.lc-logbar #lc-log-caret{font-size:9px;transition:transform .12s;display:inline-block}
|
||||||
.lc-logbar.open #lc-log-caret{transform:rotate(90deg)}
|
.lc-logbar.open #lc-log-caret{transform:rotate(90deg)}
|
||||||
|
|||||||
@@ -313,6 +313,39 @@ func lcRunPrebuilt() {
|
|||||||
lcDone("binaires précompilés installés (" + tag + ")")
|
lcDone("binaires précompilés installés (" + tag + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleLlamacppUse bascule BIN entre deux versions DÉJÀ installées, sans
|
||||||
|
// rien recompiler : "fast" = binaires précompilés, "opt" = build local. Le
|
||||||
|
// service redémarre pour prendre le nouveau binaire. L'UI n'appelle ceci que
|
||||||
|
// quand la version cible existe déjà (sinon elle lance un job d'installation).
|
||||||
|
func handleLlamacppUse(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req struct {
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
}
|
||||||
|
_ = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
var bin string
|
||||||
|
switch req.Mode {
|
||||||
|
case "fast":
|
||||||
|
bin = prebuiltServerBin()
|
||||||
|
case "opt":
|
||||||
|
bin = llamaServerBin(llamacppRepoDir())
|
||||||
|
default:
|
||||||
|
sendJSON(w, 400, map[string]any{"ok": false, "error": "mode inconnu"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bin == "" {
|
||||||
|
sendJSON(w, 400, map[string]any{"ok": false, "error": "cette version n'est pas installée"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := SetConfigKey("BIN", bin); err != nil {
|
||||||
|
sendJSON(w, 500, map[string]any{"ok": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if serviceIsActive() {
|
||||||
|
_ = serviceAction("restart")
|
||||||
|
}
|
||||||
|
sendJSON(w, 200, map[string]any{"ok": true, "bin": bin})
|
||||||
|
}
|
||||||
|
|
||||||
// handleLlamacppJob renvoie l'état du job courant + les lignes de log depuis
|
// handleLlamacppJob renvoie l'état du job courant + les lignes de log depuis
|
||||||
// l'offset absolu ?from=N (le client mémorise `next` et enchaîne).
|
// l'offset absolu ?from=N (le client mémorise `next` et enchaîne).
|
||||||
func handleLlamacppJob(w http.ResponseWriter, r *http.Request) {
|
func handleLlamacppJob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func newWebMux() *http.ServeMux {
|
|||||||
api("/api/llamacpp/job", handleLlamacppJob) // progression + logs du job
|
api("/api/llamacpp/job", handleLlamacppJob) // progression + logs du job
|
||||||
api("/api/llamacpp/prebuilt", handleLlamacppPrebuilt) // job : binaires officiels précompilés
|
api("/api/llamacpp/prebuilt", handleLlamacppPrebuilt) // job : binaires officiels précompilés
|
||||||
api("/api/llamacpp/prebuilt/check", handleLlamacppPrebuiltCheck) // dernière release officielle vs installée
|
api("/api/llamacpp/prebuilt/check", handleLlamacppPrebuiltCheck) // dernière release officielle vs installée
|
||||||
|
api("/api/llamacpp/use", handleLlamacppUse) // bascule BIN entre versions déjà installées
|
||||||
api("/api/presets", handlePresets)
|
api("/api/presets", handlePresets)
|
||||||
api("/api/preset", handlePreset)
|
api("/api/preset", handlePreset)
|
||||||
api("/api/preset/save", handlePresetSave)
|
api("/api/preset/save", handlePresetSave)
|
||||||
|
|||||||
Reference in new issue
Block a user