feat(rollen): echte Modellnamen als API-ID + Rollen als Tags/Alias

Modelle erscheinen jetzt mit ihrem ECHTEN Namen (= API-Name, den man in Zed/
OpenCode angibt) statt unter "coder"/"vision". Die Rolle wird ein zusaetzlicher,
sprechender Name (llama-swap-`aliases`) -> in der API funktionieren BEIDE.

- llamaswap.py: model_id_from_path (Repo-Ordner ohne -GGUF) + set_role_alias
  (Rolle als eindeutiger Alias, beim Setzen bei anderen Modellen entfernt) +
  ROLE_IDS (vision/coder/scout/reviewer/manager).
- models.py: register schreibt Key=Modellname, Rolle als Alias; /status liefert
  role + aliases + api_ids; neuer POST /set_role. Rueckwaertskompatibel: Legacy-
  Eintraege (coder/vision ohne Alias) behalten ihren Key als Rolle.
- cookbook.py: install-recipe/-model nutzen dasselbe Schema (Name + Rollen-Alias).
- models.js: Zeile zeigt echten Namen + Rollen-Tag + API-Name (klick=kopieren);
  neuer "Rolle"-Dialog mit 5 Schnellwahl-Rollen + freier Eingabe.
- cookbook.js: Profi-Download-Feld ist jetzt optionale Rolle (Name kommt vom
  Modell), Pflichtfeld-Pruefung entsprechend gelockert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-22 16:58:07 +02:00
parent 0c16fb28c2
commit 4077d06b2d
5 changed files with 169 additions and 17 deletions
+40 -6
View File
@@ -17,7 +17,8 @@ from config import (CMD_TEMPLATE, CONFIG_PATH, DEFAULT_TTL, HF_DOWNLOAD_ENV, LLA
MODELS_DIR, TOKEN, hf_bin)
from jobengine import JOBS, start_job, attach_download_progress
from routers.cookbook import hf_file_size
from llamaswap import _swap_get, read_config, write_config
from llamaswap import (_swap_get, read_config, write_config,
model_id_from_path, set_role_alias, ROLE_IDS)
from hw_math import extract_params_b, max_ctx_for, estimate_memory_gb
import re
import os
@@ -38,12 +39,18 @@ class DownloadReq(BaseModel):
class RegisterReq(BaseModel):
alias: str
alias: str = "" # rueckwaertskompatibel: wird als Rolle interpretiert, wenn 'role' fehlt
role: str | None = None # Rollen-Tag (vision/coder/scout/reviewer/manager o.ae.)
model_path: str
ctx: int = 8192
ttl: int | None = None
class RoleReq(BaseModel):
alias: str # die Modell-ID (config-Key)
role: str = "" # leer = Rolle entfernen
class ChatReq(BaseModel):
model: str
message: str
@@ -92,17 +99,28 @@ def status():
if q_match:
quant = q_match.group(1).upper()
# Rolle aus dem llama-swap-Alias ableiten; Legacy-Eintraege ohne Alias, deren Key
# selbst eine Rolle ist (coder/vision/...), behalten diese als Rolle.
aliases = spec.get("aliases") or []
if isinstance(aliases, str):
aliases = [aliases]
aliases = [str(a) for a in aliases]
role = aliases[0].lower() if aliases else (name.lower() if name.lower() in ROLE_IDS else None)
caps = ["Text"]
if "coder" in name.lower() or (m_path and "code" in m_path.group(1).lower()):
if role == "coder" or "coder" in name.lower() or (m_path and "code" in m_path.group(1).lower()):
caps = ["Code"]
if "--mmproj" in cmd:
caps.append("Bild")
# "incomplete" = Rolle existiert in der config, hat aber kein Modell (-m) hinterlegt
# (z.B. von provision.sh angelegter Platzhalter). Ehrlich kennzeichnen statt "bereit".
incomplete = not (m_path and m_path.group(1))
configured[name] = {
"name": name,
"role": role,
"aliases": aliases,
"api_ids": [name] + aliases,
"ttl": spec.get("ttl", cfg.get("globalTTL", 0)),
"cmd": cmd,
"state": "idle",
@@ -184,15 +202,31 @@ def register(req: RegisterReq):
cfg = read_config()
cmd = CMD_TEMPLATE.replace("{model}", req.model_path).replace("{ctx}", str(req.ctx))
cmd = _augment_vision(cmd, req.model_path)
cfg["models"][req.alias] = {
# Neues Schema: Schluessel = sprechender Modellname (steht so in der Modell-Liste und ist
# der API-Name), die Rolle kommt als llama-swap-Alias obendrauf (beide Namen funktionieren).
role = (req.role or req.alias or "").strip().lower()
model_id = model_id_from_path(req.model_path)
cfg["models"][model_id] = {
"cmd": LiteralScalarString(cmd + "\n"),
"ttl": req.ttl if req.ttl is not None else DEFAULT_TTL,
}
set_role_alias(cfg, model_id, role)
write_config(cfg)
return {"ok": True, "alias": req.alias,
return {"ok": True, "alias": model_id, "model_id": model_id, "role": role,
"note": "In config.yaml geschrieben. llama-swap mit -watch-config laedt automatisch neu."}
@router.post("/set_role")
def set_role(req: RoleReq):
"""Rollen-Tag eines Modells setzen/aendern (als eindeutiger llama-swap-Alias)."""
cfg = read_config()
if req.alias not in cfg.get("models", {}):
raise HTTPException(404, "Modell nicht gefunden.")
set_role_alias(cfg, req.alias, req.role.strip().lower() or None)
write_config(cfg)
return {"ok": True, "alias": req.alias, "role": req.role.strip().lower()}
@router.post("/update_model")
def update_model(req: UpdateReq):
cfg = read_config()