Presets : glisser-déposer, pastilles contexte et capacités, prompt système dans l'éditeur

Repris d'AJEAN et adapté à Loki :
- liste réordonnable par glisser-déposer (SortableJS 1.15.6, MIT), ordre
  gardé côté serveur (/api/presets/order) et suivi par le sélecteur de
  l'en-tête ; le clic de fin de glissement ne bascule pas de preset
- pastille de contexte (32K, 128K, 40K…) et pastille « capacités » : œil
  quand le preset lit les images (MMPROJ, API externe déclarée multimodale,
  Strata), ampoule + niveau quand il raisonne
- éditeur : prompt système DE CE preset (rangé en base, sysprompt:<id>),
  projecteur vision sur le CPU (--no-mmproj-offload, visible seulement avec
  un mmproj)
- les projecteurs mmproj ne sont plus proposés comme modèle (éditeur et
  sélecteur de l'en-tête)
- l'aide « ? » reste survolable sur un réglage grisé

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-07 09:36:59 +02:00
1 parent 127013b32b
commit ecff7de5a3
11 files changed
+412 -27

No files matched your search

+46 -5
View File
@@ -441,9 +441,20 @@ func handlePresets(w http.ResponseWriter, r *http.Request) {
if m := strings.TrimSpace(cfg[extKeyModel]); m != "" {
item["model"] = m
}
if strings.TrimSpace(cfg[extKeyVision]) == "1" {
item["vision"] = true
}
out = append(out, item)
continue
}
// Pastilles de la liste : taille de contexte, et l'œil quand le preset
// charge un projecteur vision (repris d'AJEAN v0.10.1 / v0.10.9).
if c := presetCtx(content); c != "" {
item["ctx"] = c
}
if strings.TrimSpace(parseEnv(content)["MMPROJ"]) != "" {
item["vision"] = true
}
// Strata : réglages dans sa propre fenêtre, pas l'éditeur llama.cpp.
if cfg := parseEnv(content); isStrataConfig(cfg) {
item["strata"] = true
@@ -481,6 +492,26 @@ func handlePresets(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, out)
}
// handlePresetsOrder (POST {ids}) : l'ordre de la liste choisi par glisser-déposer.
func handlePresetsOrder(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
sendJSON(w, 405, map[string]any{"ok": false, "error": "POST attendu"})
return
}
var req struct {
IDs []string `json:"ids"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
if err := savePresetOrder(req.IDs); err != nil {
sendJSON(w, 500, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true})
}
func handlePreset(w http.ResponseWriter, r *http.Request) {
id := strings.TrimSpace(r.URL.Query().Get("id"))
if id == "" {
@@ -492,16 +523,20 @@ func handlePreset(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 404, map[string]any{"error": "not found"})
return
}
sendJSON(w, 200, map[string]any{"id": id, "name": presetDisplayName(content, id), "content": content})
sendJSON(w, 200, map[string]any{"id": id, "name": presetDisplayName(content, id), "content": content,
"sysprompt": getStr(bkState, sysPromptKeyPrefix+id)})
}
// presetSaveReq is the preset editor payload. `id` identifies an existing
// preset to update ("" creates a new one); `name` is the display name.
// SysPrompt : le prompt système DE CE preset, édité dans l'éditeur (repris
// d'AJEAN v0.13.0) ; nil = champ absent, on n'y touche pas.
type presetSaveReq struct {
ID string `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
DeleteModel bool `json:"deleteModel"`
ID string `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
DeleteModel bool `json:"deleteModel"`
SysPrompt *string `json:"sysprompt,omitempty"`
}
// saveReq is the skill editor payload (skills keep name-as-identity + rename).
@@ -604,6 +639,12 @@ func handlePresetSave(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
if req.SysPrompt != nil {
if err := putStr(bkState, sysPromptKeyPrefix+newID, strings.TrimSpace(*req.SysPrompt)); err != nil {
sendJSON(w, 500, map[string]any{"ok": false, "error": err.Error()})
return
}
}
if applied {
fmt.Printf("%s config.env <- %s (preset actif modifié)\n", green("[ok]"), newID)
fmt.Println(dim("[info] redémarrage du service..."))