MoE : avis de placement, copie en placement auto et garde du mode de chargement

Un MoE aux experts sur CPU se règle aujourd'hui à la main (-ot, --n-cpu-moe
40, UBATCH 512) sans que Loki dise ce que --fit ferait mieux, ni qu'un
--load-mode none/mlock trop gros pour la RAM mène au swap ou à l'OOM
(llama.cpp #26110). Rien n'est réécrit en douce : des avis, une copie sur
demande, un refus seulement quand l'échec est certain.

- moeNotes (pure, comme nglArgs) : experts placés à la main → --fit ne les
  place pas, et ne pas monter UBATCH seul (tampon plus gros sur chaque carte) ;
  placement auto, MoE plus gros que la VRAM, UBATCH ≤ 512 → « UBATCH 2048+ » ;
  moteur sans --fit → « garde --n-cpu-moe » ; --fit coupé par un autre -ot.
  Le détecteur cpuExperts ne compte un -ot que s'il vise les experts (exps)
  vers le CPU : celui de per_layer_token_embd n'en est pas.
- « Dupliquer en placement auto… » (éditeur, /api/preset/autoplace) : COPIE du
  preset affiché, sans -ot des experts, --n-cpu-moe, --cpu-moe, -ngl,
  --tensor-split, --fit off, -b/-ub ni drapeaux de chargement ; NGL retiré,
  UBATCH 2048, BATCH 4096, --load-mode mmap ; CTX, cache KV, échantillonnage
  intacts. Refusée si --fit resterait inactif ou si CTX vaut 0. L'original
  n'est pas touché, la copie n'est pas activée.
- loadModeRisk : none, mlock, dio, mmap+mlock (et --no-mmap, --mlock,
  LLAMA_ARG_*) face à la RAM effective (limite cgroup comprise), en comptant
  modèle moins VRAM (tout le modèle sur macOS) : avertissement au-delà de
  80 %, refus au lancement au-delà de 90 % de la borne basse seulement ;
  LOAD_GUARD=off le lève. VRAM inconnue : jamais de refus.
- Éditeur : avis sous « Experts MoE sur CPU » et sous « Chargement ».

Ligne de commande et environnement du moteur inchangés (test de
non-régression) ; presets externes ignorés.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-04 11:12:56 +02:00
1 parent 986a636c20
commit f87c7b615b
11 files changed
+1001 -8

No files matched your search

+15 -3
View File
@@ -1225,7 +1225,14 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
</div>
<div class="pe-row">
<span class="pe-row-l">Experts MoE sur CPU<span class="pe-sub">vide = non · sur 2 GPU, coupe le pipeline entre cartes (prompt plus lent)</span></span>
<span class="pe-row-c"><input id="s-moe" class="pe-val" type="number" min="0" step="1" placeholder="—" oninput="eaSetValued('--n-cpu-moe', this.value)"></span>
<span class="pe-row-c"><input id="s-moe" class="pe-val" type="number" min="0" step="1" placeholder="—" oninput="eaSetValued('--n-cpu-moe', this.value); refreshMoeSoon()"></span>
</div>
<!-- Avis MoE (refreshMoe) : experts placés à la main, UBATCH conseillé,
et l'assistant « Dupliquer en placement auto » — une COPIE du
preset, l'original reste tel quel. Masqué tant qu'il n'y a rien à dire. -->
<div class="pe-row stack" id="m-moe-row" style="display:none">
<div class="pe-note" id="m-moe-note"></div>
<button class="pe-link" id="m-moe-dup" onclick="dupAutoPlacement()" hidden>Dupliquer en placement auto…</button>
</div>
<div class="pe-row">
<span class="pe-row-l">Threads CPU<span class="pe-sub">vide ou 0 = cœurs physiques</span></span>
@@ -1241,7 +1248,7 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
</div>
<div class="pe-row">
<span class="pe-row-l">UBATCH</span>
<span class="pe-row-c"><input id="s-ubatch" class="pe-val" type="number" min="0" step="128" placeholder="512" oninput="cfgWriteKey('UBATCH', this.value)"></span>
<span class="pe-row-c"><input id="s-ubatch" class="pe-val" type="number" min="0" step="128" placeholder="512" oninput="cfgWriteKey('UBATCH', this.value); refreshMoeSoon()"></span>
</div>
<div class="pe-row">
<!-- Cache de prompts en RAM hôte (--cache-ram, Mio) : copie exacte de
@@ -1329,7 +1336,7 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
(normalizeLoadFlags). -->
<div class="pe-row">
<span class="pe-row-l">Chargement<span class="pe-sub" id="s-loadmode-sub"></span></span>
<span class="pe-row-c"><span class="pe-selc"><select id="s-loadmode" onchange="eaSetLoadMode(this.value)">
<span class="pe-row-c"><span class="pe-selc"><select id="s-loadmode" onchange="eaSetLoadMode(this.value); refreshMoeSoon()">
<option value="">auto</option>
<option value="mmap">mmap</option>
<option value="none">none</option>
@@ -1338,6 +1345,11 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
<option value="dio">dio</option>
</select></span></span>
</div>
<!-- Mode résident (none, mlock, dio, mmap+mlock) avec trop de poids en
RAM : l'avertissement du lancement (loadModeRisk), ou son refus. -->
<div class="pe-row stack" id="s-loadmode-warn-row" style="display:none">
<div class="pe-note" id="s-loadmode-warn" style="color:var(--warn)"></div>
</div>
</div>
</div>
+50
View File
@@ -1018,6 +1018,7 @@ function populateSettings(){
set('s-ubatch', cfgReadKey('UBATCH'));
set('s-cram', cfgReadKey('CACHE_RAM'));
refreshCacheRAM();
refreshMoe();
set('s-tbatch', cfgReadKey('THREADS_BATCH'));
set('s-kv', cfgReadKey('KV_TYPE'));
syncKVSub();
@@ -1115,6 +1116,55 @@ async function refreshCacheRAM(){
if(sub) sub.textContent = 'RAM hôte, copie exacte du contexte · jamais la VRAM' +
(r.why ? ' · ' + r.why : '') + (r.mib > 0 || fixed ? '' : ' · défaut du moteur') + side;
}
// Avis MoE et mode de chargement, calculés côté serveur comme au lancement
// (backend_serve_moe.go) : experts placés à la main, UBATCH conseillé, moteur
// sans --fit, chargement résident trop gros pour la RAM. Des avis seulement :
// rien n'est écrit dans le preset. Une demande en vol ; la dernière gagne.
let moeSeq = 0, moeTimer = null, moeChanges = null;
function refreshMoeSoon(){ clearTimeout(moeTimer); moeTimer = setTimeout(refreshMoe, 400); }
async function refreshMoe(){
const row = document.getElementById('m-moe-row'), note = document.getElementById('m-moe-note');
const dup = document.getElementById('m-moe-dup');
const lrow = document.getElementById('s-loadmode-warn-row'), lw = document.getElementById('s-loadmode-warn');
if(!row) return;
const seq = ++moeSeq;
let r = {};
try{ r = await jpost('/api/preset/moe', {content: document.getElementById('m-content').value}); }
catch(_){ r = {}; }
if(seq !== moeSeq) return;
const ap = r.ok ? r.autoplace : null;
const parts = (r.ok && r.notes) ? r.notes.slice() : [];
if(ap && !ap.ok) parts.push('placement auto impossible : ' + ap.why);
note.textContent = parts.join(' · ');
moeChanges = (ap && ap.ok) ? (ap.changes || []) : null;
dup.hidden = !moeChanges;
row.style.display = (parts.length || moeChanges) ? '' : 'none';
if(lrow && lw){
lw.textContent = (r.ok && r.load) || '';
lrow.style.display = lw.textContent ? '' : 'none';
}
}
// « Dupliquer en placement auto » : une COPIE du preset tel qu'affiché, sans
// les experts placés à la main (--fit les répartit carte par carte), UBATCH
// 2048 / BATCH 4096, NGL auto, chargement mmap, CTX et cache KV intacts. Le
// preset d'origine ne bouge pas : c'est le repli si --fit échoue.
async function dupAutoPlacement(){
if(!moeChanges) return;
const msg = 'Créer une COPIE de ce preset en placement automatique (--fit place les experts carte par carte) :\n\n'
+ moeChanges.map(c => '• ' + c).join('\n')
+ '\n\nLe preset d\'origine reste tel quel : c\'est le repli. Après la bascule sur la copie, le journal doit dire '
+ '« successfully fit params » — « failed to fit » met tous les experts sur GPU : revenir à l\'original. '
+ 'Puis comparer les deux au « bench complet ».';
if(!await askConfirm(msg, {title:'Placement auto', okText:'Créer la copie'})) return;
let r = {};
try{
r = await jpost('/api/preset/autoplace', {id: editingKey || '', name: document.getElementById('m-name').value.trim(),
content: document.getElementById('m-content').value});
}catch(e){ r = {ok:false, error:e.message}; }
if(!r.ok){ toast('erreur : ' + (r.error||'')); return; }
toast('copie créée : ' + r.name);
KINDS.preset.reload();
}
function syncKVSub(){
const el = document.getElementById('s-kv-sub');
if(!el) return;