diff --git a/internal/loki/backend_presets.go b/internal/loki/backend_presets.go index 15670ee..264d07c 100644 --- a/internal/loki/backend_presets.go +++ b/internal/loki/backend_presets.go @@ -432,6 +432,9 @@ func DeletePreset(id string) error { // sans ce ménage, il resterait orphelin et ressusciterait sur un preset recréé // sous le même id. _ = putStr(bkState, sysPromptKeyPrefix+id, "") + // Même ménage pour son benchmark : un preset recréé sous cet id en + // hériterait, alors qu'il a été mesuré sur un autre modèle. + deletePresetBench(id) return nil } diff --git a/internal/loki/llm_bench.go b/internal/loki/llm_bench.go index 53b84dc..5c51ad7 100644 --- a/internal/loki/llm_bench.go +++ b/internal/loki/llm_bench.go @@ -158,6 +158,24 @@ func loadLastBench() *savedBench { return &sb } +// benchMatchesPreset : le bench a-t-il été mesuré sur le modèle ACTUEL du +// preset ? Les benchs sont rangés par id de preset : sans ce contrôle, un +// preset dont le modèle a changé affichait les mesures d'un autre modèle. +// Repris d'AJEAN 0.16.3. +func benchMatchesPreset(sb savedBench, cfg map[string]string) bool { + m := strings.TrimSpace(cfg["MODEL"]) + return m != "" && sb.Model == filepath.Base(m) +} + +// deletePresetBench oublie le bench d'un preset supprimé. +func deletePresetBench(id string) { + m := loadBenchStore() + if _, ok := m[id]; ok { + delete(m, id) + _ = putJSON(bkState, "bench_presets", m) + } +} + // loadBenchStore renvoie les benchmarks par preset (vide s'il n'y en a pas). func loadBenchStore() map[string]savedBench { m := map[string]savedBench{} diff --git a/internal/loki/llm_bench_match_test.go b/internal/loki/llm_bench_match_test.go new file mode 100644 index 0000000..01dd104 --- /dev/null +++ b/internal/loki/llm_bench_match_test.go @@ -0,0 +1,17 @@ +package loki + +import "testing" + +// Un bench n'est affiché que pour le modèle sur lequel il a été mesuré. +func TestBenchMatchesPreset(t *testing.T) { + sb := savedBench{Model: "Qwen3-27B-Q4_K_M.gguf"} + if !benchMatchesPreset(sb, map[string]string{"MODEL": "/models/Qwen3-27B-Q4_K_M.gguf"}) { + t.Fatal("même modèle : le bench doit s'afficher") + } + if benchMatchesPreset(sb, map[string]string{"MODEL": "/models/Autre.gguf"}) { + t.Fatal("autre modèle : le bench ne doit pas s'afficher") + } + if benchMatchesPreset(sb, map[string]string{}) { + t.Fatal("preset sans modèle : pas de bench") + } +} diff --git a/internal/loki/web_api.go b/internal/loki/web_api.go index efb492e..3215abe 100644 --- a/internal/loki/web_api.go +++ b/internal/loki/web_api.go @@ -399,7 +399,8 @@ func handlePresets(w http.ResponseWriter, r *http.Request) { out := []map[string]any{} for _, p := range list { item := map[string]any{"id": p.ID, "name": p.Name, "active": p.Active} - if content, err := ReadPreset(p.ID); err == nil { + content, _ := ReadPreset(p.ID) + if content != "" { // Preset externe : ni quant, ni raisonnement local à annoncer — on // l'étiquette et on donne le nom du modèle distant à afficher. if cfg := parseEnv(content); isExternalConfig(cfg) { @@ -417,7 +418,7 @@ func handlePresets(w http.ResponseWriter, r *http.Request) { item["reasoning"] = strings.ToLower(r) } } - if sb, ok := store[p.ID]; ok { + if sb, ok := store[p.ID]; ok && benchMatchesPreset(sb, parseEnv(content)) { item["bench"] = map[string]any{ "prefill": sb.Result.PromptPerSecond, "decode": sb.Result.PredictedPerSec,