Optimiseur : loki tune et bouton « Optimiser », essais sans perte sur un moteur privé

Chercher à la main le bon micro-lot, les threads ou la marge --fit d'un preset
demandait de dupliquer, basculer et comparer des bench complets un par un.
L'optimiseur le fait sur un moteur d'essai, sans jamais toucher à config.env ni
à ce que calcule le modèle, et n'écrit rien sans un clic.

- Isolation : moteur principal arrêté puis relancé ; chaque essai est une copie
  de la configuration (LOKI_HOME/tune/run), lancée par « loki serve » sur
  127.0.0.1 et un port libre, groupe de processus tué en sortie
- Verrou exclusif inter-processus (LOKI_HOME/tune.lock, PID + démarrage +
  binaire) vérifié par serviceAction start/restart et par le chat, les tâches,
  bash_bg, /v1, le bench, la bascule et l'enregistrement de preset, GPU, clé
  d'API, moteur ; essai orphelin arrêté avant tout démarrage du moteur
- Essais : lots, threads, délestage, marges --fit, files CUDA ; placement --fit
  et SPEC/CUDA_GRAPH_OPT/--backend-sampling seulement sur demande
- EXTRA_ARGS jeton par jeton, liste blanche seulement ; ligne de commande de
  chaque essai composée à blanc et comparée à la référence (« dénature »),
  contexte, slots et cache KV recontrôlés une fois chargé
- Mesure : bench complet du lot 1 à profondeur fixe ; score = durée d'un tour
  type (médianes de la télémétrie) ; gain > max(3 %, écart entre passages)
- Application sur clic : copie du preset, ou preset actuel sauvegardé, réécrit
  clé par clé, vérifié par une sonde et rétabli en cas d'échec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-04 11:56:11 +02:00
1 parent f87c7b615b
commit 9bffff78c2
36 files changed
+4580 -16

No files matched your search

+14
View File
@@ -565,6 +565,9 @@ func handlePresetAutoPlace(w http.ResponseWriter, r *http.Request) {
}
func handlePresetSave(w http.ResponseWriter, r *http.Request) {
if tuneDenyHTTP(w) {
return
}
var req presetSaveReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
@@ -703,6 +706,9 @@ func handleAgentToggle(w http.ResponseWriter, r *http.Request) {
// key?} l'écrit puis redémarre le service (llama-server lit --api-key au lancement).
func handleAPIKey(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
if tuneDenyHTTP(w) {
return
}
var req struct {
Action string `json:"action"`
Key string `json:"key"`
@@ -1300,6 +1306,9 @@ func handleMemDelete(w http.ResponseWriter, r *http.Request) {
// même contrat que handleSwitch. C'est ce qui rend le sélecteur de l'en-tête
// utilisable même sans avoir créé de preset.
func handleModelUse(w http.ResponseWriter, r *http.Request) {
if tuneDenyHTTP(w) {
return
}
var req struct {
Value string `json:"value"`
}
@@ -1327,6 +1336,11 @@ func handleModelUse(w http.ResponseWriter, r *http.Request) {
}
func handleSwitch(w http.ResponseWriter, r *http.Request) {
// Pendant une optimisation, la configuration active est celle qu'elle mesure
// et le moteur ne doit pas redémarrer (backend_tune_lock.go).
if tuneDenyHTTP(w) {
return
}
// id = identité stable du preset (nom de fichier). Le numéro seul est
// dangereux : si la liste a changé depuis l'affichage (réordonnée, preset
// ajouté ou supprimé depuis un autre appareil), la même position désigne un