mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 15:08:16 +02:00
Moteur : corrections de relecture des garde-fous de fidélité — les variables LLAMA_ARG_* et le glissement par défaut des anciens moteurs se voient aussi
Les garde-fous ne lisaient qu'EXTRA_ARGS et KV_TYPE. Or llama.cpp applique ses variables LLAMA_ARG_* avant la ligne de commande : un conteneur lancé avec LLAMA_ARG_CACHE_TYPE_K=q8_0 ou LLAMA_ARG_CACHE_REUSE=256 changeait les calculs sans un mot. Et un llama-server d'avant --context-shift glisse le contexte PAR DÉFAUT : il jetait des jetons alors que rien n'était écrit nulle part. - Type de cache effectif : variables, puis KV_TYPE*, puis EXTRA_ARGS, la source la plus forte est nommée dans la note ; warnSlowKV suit. - --context-shift et --cache-reuse lus aussi dans LLAMA_ARG_CONTEXT_SHIFT et LLAMA_ARG_CACHE_REUSE ; moteur ancien (aide sans --context-shift) : on dit qu'il glisse et que --no-context-shift l'en empêche. Aucun drapeau ajouté. - Vision reconnue aussi par --mmproj d'EXTRA_ARGS et LLAMA_ARG_MMPROJ. - Placement figé lu par tensorOverride : --n-cpu-moe 0 ne compte plus, --n-cpu-ffn et LLAMA_ARG_N_CPU_MOE si. Même règle dans l'interface. - Test du paquet : les étiquettes json:"cache_prompt" des structures sont aussi refusées hors du benchmark. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2bf388f7b7
commit
c4f051a0e9
5 files changed
+232
-114
No files matched your search
@@ -2,6 +2,7 @@ package loki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -16,21 +17,55 @@ import (
|
||||
//
|
||||
// Tout ici est pur : buildServeArgs en tire des notes, cmdServe les écrit.
|
||||
|
||||
// fidelityArgEnv : les LLAMA_ARG_* qui, sans drapeau, posent un cache
|
||||
// quantifié, un cache approché ou la vision. llama.cpp les applique AVANT la
|
||||
// ligne de commande : un -ctk de KV_TYPE ou d'EXTRA_ARGS les écrase, mais seuls
|
||||
// ils décident. LLAMA_ARG_NO_CONTEXT_SHIFT est celle des moteurs anciens.
|
||||
var fidelityArgEnv = []string{"LLAMA_ARG_CACHE_TYPE_K", "LLAMA_ARG_CACHE_TYPE_V",
|
||||
"LLAMA_ARG_CONTEXT_SHIFT", "LLAMA_ARG_NO_CONTEXT_SHIFT", "LLAMA_ARG_CACHE_REUSE",
|
||||
"LLAMA_ARG_MMPROJ", "LLAMA_ARG_MMPROJ_URL"}
|
||||
|
||||
// probeFidelityEnv complète ArgEnv avec fidelityArgEnv. Appelée après
|
||||
// probeServeGPUs, qui crée ArgEnv.
|
||||
func probeFidelityEnv(si *serveSysInfo) {
|
||||
if si.ArgEnv == nil {
|
||||
si.ArgEnv = map[string]string{}
|
||||
}
|
||||
for _, k := range fidelityArgEnv {
|
||||
if v, ok := os.LookupEnv(k); ok && v != "" {
|
||||
si.ArgEnv[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effectiveKVTypes donne les types de cache K et V que le moteur utilisera
|
||||
// VRAIMENT : ceux du preset (serveKVTypes), puis ceux d'EXTRA_ARGS par-dessus.
|
||||
// EXTRA_ARGS ferme la ligne de commande et llama-server retient la dernière
|
||||
// occurrence d'un drapeau : un -ctk q8_0 écrit à la main l'emporte sur KV_TYPE.
|
||||
// fromExtra dit qu'EXTRA_ARGS a tranché au moins l'un des deux. Vide = défaut
|
||||
// du moteur (f16).
|
||||
func effectiveKVTypes(cfg map[string]string, extra []string) (k, v string, fromExtra bool) {
|
||||
k, v = serveKVTypes(cfg)
|
||||
// VRAIMENT. Trois sources, de la plus faible à la plus forte : les variables
|
||||
// LLAMA_ARG_CACHE_TYPE_K/V (lues avant la ligne de commande), le preset
|
||||
// (serveKVTypes, traduit en -ctk/-ctv), puis EXTRA_ARGS, qui ferme la ligne :
|
||||
// llama-server retient la dernière occurrence d'un drapeau, un -ctk q8_0 écrit
|
||||
// à la main l'emporte donc sur KV_TYPE. src nomme la source la plus forte qui a
|
||||
// tranché l'un des deux (vide : aucune). Vide = défaut du moteur (f16).
|
||||
func effectiveKVTypes(cfg map[string]string, extra []string, argEnv map[string]string) (k, v, src string) {
|
||||
k, v = argEnv["LLAMA_ARG_CACHE_TYPE_K"], argEnv["LLAMA_ARG_CACHE_TYPE_V"]
|
||||
if k != "" || v != "" {
|
||||
src = "défini par LLAMA_ARG_CACHE_TYPE_K/V"
|
||||
}
|
||||
if ck, cv := serveKVTypes(cfg); ck != "" || cv != "" {
|
||||
src = "KV_TYPE"
|
||||
if ck != "" {
|
||||
k = ck
|
||||
}
|
||||
if cv != "" {
|
||||
v = cv
|
||||
}
|
||||
}
|
||||
if s := flagValue(extra, "-ctk", "--cache-type-k"); s != "" {
|
||||
k, fromExtra = s, true
|
||||
k, src = s, "défini par EXTRA_ARGS"
|
||||
}
|
||||
if s := flagValue(extra, "-ctv", "--cache-type-v"); s != "" {
|
||||
v, fromExtra = s, true
|
||||
v, src = s, "défini par EXTRA_ARGS"
|
||||
}
|
||||
return k, v, fromExtra
|
||||
return k, v, src
|
||||
}
|
||||
|
||||
// kvFidelity classe un type de cache par rapport au f16 de référence. Rang 0 =
|
||||
@@ -56,10 +91,11 @@ func kvFidelity(t string) (rank int, label string) {
|
||||
// fidélité et d'où il vient. Le choix reste celui du preset : Loki ne le change
|
||||
// jamais, ni dans un sens ni dans l'autre. Pas d'estimation de la VRAM qu'un
|
||||
// retour en f16 demanderait : sans les métadonnées du GGUF, ce serait un chiffre
|
||||
// inventé. En revanche, si le placement est figé à la main (-ot, --n-cpu-moe…),
|
||||
// --fit ne tourne pas et ne rattrapera pas ce surplus — mieux vaut le savoir
|
||||
// avant de basculer et de finir en « out of memory ».
|
||||
func kvFidelityNote(k, v string, fromExtra bool, extra []string) string {
|
||||
// inventé. En revanche, si le placement est figé à la main (-ot, --n-cpu-moe N>0,
|
||||
// en drapeau ou en variable : tensorOverride), --fit ne tourne pas et ne
|
||||
// rattrapera pas ce surplus — mieux vaut le savoir avant de basculer et de
|
||||
// finir en « out of memory ».
|
||||
func kvFidelityNote(k, v, src string, extra []string, argEnv map[string]string) string {
|
||||
rk, lk := kvFidelity(k)
|
||||
rv, lv := kvFidelity(v)
|
||||
if rk == 0 && rv == 0 {
|
||||
@@ -69,13 +105,9 @@ func kvFidelityNote(k, v string, fromExtra bool, extra []string) string {
|
||||
if rv > rk {
|
||||
label = lv
|
||||
}
|
||||
src := "KV_TYPE"
|
||||
if fromExtra {
|
||||
src = "défini par EXTRA_ARGS"
|
||||
}
|
||||
note := fmt.Sprintf("cache KV %s/%s (%s) : %s — choix du preset, laissé tel quel.",
|
||||
note := fmt.Sprintf("cache KV %s/%s (%s) : %s — choix de l'utilisateur, laissé tel quel.",
|
||||
orF16(k), orF16(v), src, label)
|
||||
if placementFixed(extra) {
|
||||
if tensorOverride(extra, argEnv) != "" {
|
||||
note += " Repasser en f16 demande plus de VRAM, et le placement fixé par -ot/--n-cpu-moe " +
|
||||
"empêche --fit de compenser : il faudrait sans doute relever --n-cpu-moe."
|
||||
}
|
||||
@@ -89,18 +121,12 @@ func orF16(t string) string {
|
||||
return t
|
||||
}
|
||||
|
||||
// placementFixed : l'utilisateur a placé lui-même des tenseurs. llama.cpp
|
||||
// abandonne alors son placement automatique (« tensor_buft_overrides already
|
||||
// set by user, abort ») — rien ne viendra absorber un cache plus gros.
|
||||
func placementFixed(extra []string) bool {
|
||||
return hasAnyFlag(extra, "-ot", "--override-tensor", "--n-cpu-moe", "-ncmoe", "--cpu-moe", "-cmoe")
|
||||
}
|
||||
|
||||
// lossyCacheNotes avertit des drapeaux de cache qui changent ce que voit le
|
||||
// lossyCacheNotes avertit des réglages de cache qui changent ce que voit le
|
||||
// modèle. Seuls deux le font :
|
||||
//
|
||||
// - --context-shift : contexte plein → le moteur JETTE des jetons anciens et
|
||||
// continue, le modèle perd une partie de la conversation sans le savoir ;
|
||||
// - le glissement de contexte : contexte plein → le moteur JETTE des jetons
|
||||
// anciens et continue, le modèle perd une partie de la conversation sans le
|
||||
// savoir ;
|
||||
// - --cache-reuse N (N > 0) : recolle des morceaux de cache calculés sous un
|
||||
// AUTRE préfixe, simplement décalés — le résultat n'est plus celui d'un
|
||||
// calcul complet. --cache-reuse 0 le désactive : rien à dire.
|
||||
@@ -108,46 +134,74 @@ func placementFixed(extra []string) bool {
|
||||
// --swa-full n'en fait PAS partie : il garde le cache complet des couches à
|
||||
// fenêtre glissante, ce qui rend la réutilisation du préfixe exacte (au prix de
|
||||
// VRAM), et ne fait rien sur un modèle sans SWA. Correspondance exacte des noms
|
||||
// (hasAnyFlag/flagValue) : --no-context-shift ne déclenche rien, et la dernière
|
||||
// occurrence gagne comme dans llama-server.
|
||||
// (flagValue) : --no-context-shift ne déclenche rien, et la dernière occurrence
|
||||
// gagne comme dans llama-server. Les variables LLAMA_ARG_* comptent quand la
|
||||
// ligne de commande se tait.
|
||||
//
|
||||
// Avec la vision chargée, llama.cpp désactive lui-même les deux (non pris en
|
||||
// charge en multimodal) : on le dit plutôt que de crier au loup. Les modèles
|
||||
// hybrides ou récurrents les ignorent aussi, mais sans métadonnées du GGUF on
|
||||
// ne sait pas les reconnaître ici : on se tait sur ce point plutôt que deviner.
|
||||
func lossyCacheNotes(extra []string, mmproj bool) []string {
|
||||
// Avec la vision chargée (MMPROJ, --mmproj d'EXTRA_ARGS ou LLAMA_ARG_MMPROJ),
|
||||
// llama.cpp désactive lui-même les deux (non pris en charge en multimodal) : on
|
||||
// le dit plutôt que de crier au loup. Les modèles hybrides ou récurrents les
|
||||
// ignorent aussi, mais sans métadonnées du GGUF on ne sait pas les reconnaître
|
||||
// ici : on se tait sur ce point plutôt que deviner.
|
||||
func lossyCacheNotes(extra []string, si serveSysInfo) []string {
|
||||
var notes []string
|
||||
ignored := ""
|
||||
if mmproj {
|
||||
if si.MMProj != "" || hasAnyFlag(extra, "-mm", "--mmproj", "-mmu", "--mmproj-url") ||
|
||||
si.ArgEnv["LLAMA_ARG_MMPROJ"] != "" || si.ArgEnv["LLAMA_ARG_MMPROJ_URL"] != "" {
|
||||
ignored = " (vision chargée : llama.cpp l'ignore de toute façon)"
|
||||
}
|
||||
if contextShiftOn(extra) {
|
||||
notes = append(notes, "avertissement : --context-shift (EXTRA_ARGS) — contexte plein, le moteur jette "+
|
||||
switch on, src := contextShift(extra, si.ArgEnv, si.Help); {
|
||||
case on && src == "":
|
||||
notes = append(notes, "avertissement : ce llama-server ancien glisse le contexte par défaut — contexte "+
|
||||
"plein, il jette des jetons anciens et le modèle perd une partie de la conversation sans le savoir"+
|
||||
ignored+". --no-context-shift dans EXTRA_ARGS l'en empêche.")
|
||||
case on:
|
||||
notes = append(notes, "avertissement : --context-shift ("+src+") — contexte plein, le moteur jette "+
|
||||
"des jetons anciens et le modèle perd une partie de la conversation sans le savoir"+ignored+".")
|
||||
}
|
||||
if s := flagValue(extra, "--cache-reuse"); s != "" {
|
||||
if n, err := strconv.Atoi(strings.TrimSpace(s)); err == nil && n > 0 {
|
||||
notes = append(notes, fmt.Sprintf("avertissement : --cache-reuse %d (EXTRA_ARGS) — réutilise des morceaux "+
|
||||
"de cache calculés sous un autre préfixe : les sorties ne sont plus exactement celles d'un calcul "+
|
||||
"complet%s.", n, ignored))
|
||||
}
|
||||
s, src := flagValue(extra, "--cache-reuse"), "EXTRA_ARGS"
|
||||
if s == "" {
|
||||
s, src = si.ArgEnv["LLAMA_ARG_CACHE_REUSE"], "LLAMA_ARG_CACHE_REUSE"
|
||||
}
|
||||
if n, err := strconv.Atoi(strings.TrimSpace(s)); err == nil && n > 0 {
|
||||
notes = append(notes, fmt.Sprintf("avertissement : --cache-reuse %d (%s) — réutilise des morceaux "+
|
||||
"de cache calculés sous un autre préfixe : les sorties ne sont plus exactement celles d'un calcul "+
|
||||
"complet%s.", n, src, ignored))
|
||||
}
|
||||
return notes
|
||||
}
|
||||
|
||||
// contextShiftOn : --context-shift et --no-context-shift peuvent cohabiter
|
||||
// (preset copié, puis corrigé) ; comme llama-server, la dernière occurrence
|
||||
// décide.
|
||||
func contextShiftOn(extra []string) bool {
|
||||
on := false
|
||||
// contextShift dit si le moteur glissera le contexte, et qui l'a décidé (vide :
|
||||
// son propre défaut). --context-shift et --no-context-shift peuvent cohabiter
|
||||
// dans EXTRA_ARGS (preset copié, puis corrigé) ; comme llama-server, la
|
||||
// dernière occurrence décide. Sans drapeau, la variable du moteur, puis son
|
||||
// défaut — qui a changé : les llama-server d'avant --context-shift (mi-2025) ne
|
||||
// connaissent que --no-context-shift et GLISSENT par défaut. Aide vide (moteur
|
||||
// inconnu) : on ne suppose rien.
|
||||
func contextShift(extra []string, argEnv map[string]string, help string) (on bool, src string) {
|
||||
set := false
|
||||
for _, a := range extra {
|
||||
name, _, _ := strings.Cut(a, "=")
|
||||
switch name {
|
||||
case "--context-shift":
|
||||
on = true
|
||||
on, set = true, true
|
||||
case "--no-context-shift":
|
||||
on = false
|
||||
on, set = false, true
|
||||
}
|
||||
}
|
||||
return on
|
||||
if set {
|
||||
return on, "EXTRA_ARGS"
|
||||
}
|
||||
if strings.Contains(help, "--no-context-shift") && !strings.Contains(help, "--context-shift") {
|
||||
// Moteur ancien : seul LLAMA_ARG_NO_CONTEXT_SHIFT (lu comme un booléen
|
||||
// vrai) l'arrête ; LLAMA_ARG_CONTEXT_SHIFT lui est inconnue.
|
||||
if envTruthy(argEnv["LLAMA_ARG_NO_CONTEXT_SHIFT"]) {
|
||||
return false, "LLAMA_ARG_NO_CONTEXT_SHIFT"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
if envTruthy(argEnv["LLAMA_ARG_CONTEXT_SHIFT"]) {
|
||||
return true, "LLAMA_ARG_CONTEXT_SHIFT"
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
Reference in new issue
Block a user