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

+56
View File
@@ -508,6 +508,62 @@ func handlePresetCacheRAM(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, out)
}
// handlePresetMoE : avis MoE et mode de chargement pour le preset en cours
// d'édition (voir backend_serve_moe.go), et ce que ferait « Dupliquer en
// placement auto » — sans rien écrire.
func handlePresetMoE(w http.ResponseWriter, r *http.Request) {
var req struct {
Content string `json:"content"`
}
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
notes, load := moePreview(req.Content)
out := map[string]any{"ok": true, "notes": notes, "load": load}
// L'assistant n'est proposé qu'à un preset qui place des experts à la
// main ; « rien à migrer » ne s'affiche pas.
if cfg := parseEnv(req.Content); !isExternalConfig(cfg) && cpuExperts(splitArgs(cfg["EXTRA_ARGS"]), nil) != "" {
if _, changes, err := autoPlacementPreset(req.Content); err != nil {
out["autoplace"] = map[string]any{"ok": false, "why": err.Error()}
} else {
out["autoplace"] = map[string]any{"ok": true, "changes": changes}
}
}
sendJSON(w, 200, out)
}
// handlePresetAutoPlace crée la copie en placement auto du preset édité (son
// contenu tel qu'affiché), sous un nouveau nom. Le preset d'origine n'est pas
// touché, et la copie n'est pas activée : c'est à l'utilisateur de basculer,
// puis de comparer (bench complet).
func handlePresetAutoPlace(w http.ResponseWriter, r *http.Request) {
if !postOnly(w, r) {
return
}
var req struct {
ID string `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
}
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
out, changes, err := autoPlacementPreset(req.Content)
if err != nil {
sendJSON(w, 200, map[string]any{"ok": false, "error": err.Error()})
return
}
name := firstNonEmpty(strings.TrimSpace(req.Name), presetDisplayName(req.Content, req.ID), "preset") + " (placement auto)"
newID, err := SavePreset("", name, out)
if err != nil {
sendJSON(w, 200, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true, "id": newID, "name": name, "changes": changes})
}
func handlePresetSave(w http.ResponseWriter, r *http.Request) {
var req presetSaveReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {