radar: Kandidaten messen mit der schnellsten Draft-Variante, Bild-Probe und Betrieb wie heute mit Zwilling
Ampel / ampel (push) Failing after 22s
Ampel / ampel (push) Failing after 22s
Das heutige Modell misst mit seinem Draft (Hirn 106 t/s, ohne 68), Kandidaten liefen ohne - Ornith fiel am 24.09. deshalb durch, obwohl es dieselbe Architektur hat. Jetzt probiert das Radar kurz: ohne Draft, eingebauten MTP-Kopf und den Draft des heutigen Modells (gleiche Architektur, Speicher reicht) und testet mit der schnellsten. Spekulatives Dekodieren aendert die Antworten nicht, nur das Tempo. Mit Draft laeuft die Bild-Probe auf einem Zwilling ohne Draft (llama.cpp: Draft und Bild = HTTP 500). Uebernehmen baut wie der Betrieb seit 24.09.: Hauptmodell mit dem gewaehlten Draft ohne Projektor, dazu ein Bild-Zwilling; der alte Zwilling fliegt raus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
42363229d4
commit
4a05bc2a05
+131
-19
@@ -102,6 +102,7 @@ HF_BASIS = "https://huggingface.co"
|
|||||||
ROLLEN = ("hirn", "coder")
|
ROLLEN = ("hirn", "coder")
|
||||||
ALIAS = {"hirn": "hermes", "coder": "coder"} # llama-swap-Alias der Rolle heute
|
ALIAS = {"hirn": "hermes", "coder": "coder"} # llama-swap-Alias der Rolle heute
|
||||||
ZWILLING = {"hirn": "vision", "coder": "coder-bild"} # Bild-Zwilling der Rolle (seit 24.09.)
|
ZWILLING = {"hirn": "vision", "coder": "coder-bild"} # Bild-Zwilling der Rolle (seit 24.09.)
|
||||||
|
ZWILLING_CTX = {"hirn": 65536, "coder": 131072} # Kontext der Zwillinge wie im Betrieb
|
||||||
DISCOVER_ROLLEN = {"coder": "coder", "fast": "hirn", "hermes": "hirn"}
|
DISCOVER_ROLLEN = {"coder": "coder", "fast": "hirn", "hermes": "hirn"}
|
||||||
PARALLEL = {"hirn": 2, "coder": 1} # Slots im Betrieb, wie heute
|
PARALLEL = {"hirn": 2, "coder": 1} # Slots im Betrieb, wie heute
|
||||||
KANDIDAT_FELDER = ("id", "name", "rolle", "repo", "datei", "groesse_gb", "passt", "eng", "quelle", "status",
|
KANDIDAT_FELDER = ("id", "name", "rolle", "repo", "datei", "groesse_gb", "passt", "eng", "quelle", "status",
|
||||||
@@ -109,7 +110,8 @@ KANDIDAT_FELDER = ("id", "name", "rolle", "repo", "datei", "groesse_gb", "passt"
|
|||||||
TEST_FELDER = ("id", "kandidat", "rolle", "datum", "ergebnis", "werte", "vergleich", "zusammenfassung")
|
TEST_FELDER = ("id", "kandidat", "rolle", "datum", "ergebnis", "werte", "vergleich", "zusammenfassung")
|
||||||
_REIHENFOLGE = {"wartet": 0, "neu": 1, "getestet": 2, "bestanden": 3, "uebernommen": 4, "durchgefallen": 5,
|
_REIHENFOLGE = {"wartet": 0, "neu": 1, "getestet": 2, "bestanden": 3, "uebernommen": 4, "durchgefallen": 5,
|
||||||
"verworfen": 6}
|
"verworfen": 6}
|
||||||
_SCHRITTE = {"download": "lädt herunter", "baseline": "misst das heutige Modell", "test": "testet den Kandidaten"}
|
_SCHRITTE = {"download": "lädt herunter", "baseline": "misst das heutige Modell",
|
||||||
|
"draft": "sucht die schnellste Draft-Variante", "test": "testet den Kandidaten"}
|
||||||
|
|
||||||
|
|
||||||
class Abbruch(Exception):
|
class Abbruch(Exception):
|
||||||
@@ -712,7 +714,8 @@ def _bewerte(fund: dict) -> dict:
|
|||||||
kv = (kv_je_token_gb(kopf) if kopf else None) or _kv_schaetzung(repo, groesse / 1e9)
|
kv = (kv_je_token_gb(kopf) if kopf else None) or _kv_schaetzung(repo, groesse / 1e9)
|
||||||
passung = speicher_passung(rolle, groesse / 1e9, kv, eng_markiert=bool(fund.get("eng")))
|
passung = speicher_passung(rolle, groesse / 1e9, kv, eng_markiert=bool(fund.get("eng")))
|
||||||
k.update(groesse_gb=round(groesse / 1e9, 1), passt=passung["passt"], eng=passung["eng"], ctx=passung["ctx"],
|
k.update(groesse_gb=round(groesse / 1e9, 1), passt=passung["passt"], eng=passung["eng"], ctx=passung["ctx"],
|
||||||
bedarf_gb=passung["bedarf_gb"], tauglich=passung["passt"], arch=arch)
|
bedarf_gb=passung["bedarf_gb"], tauglich=passung["passt"], arch=arch,
|
||||||
|
mtp=bool((kopf or {}).get(f"{arch}.nextn_predict_layers")))
|
||||||
k["begruendung"] = f"{herkunft} {passung['text']}{notiz}"
|
k["begruendung"] = f"{herkunft} {passung['text']}{notiz}"
|
||||||
if not kopf and not passung["passt"]:
|
if not kopf and not passung["passt"]:
|
||||||
# Ohne Kopf rechnet die Schätzung den KV-Cache grob zu groß — darauf hin nichts verwerfen,
|
# Ohne Kopf rechnet die Schätzung den KV-Cache grob zu groß — darauf hin nichts verwerfen,
|
||||||
@@ -724,7 +727,7 @@ def _bewerte(fund: dict) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
_UEBERNEHMEN = ("name", "rolle", "repo", "quant", "quelle", "rang", "datei", "dateien", "mmproj", "draft", "flags",
|
_UEBERNEHMEN = ("name", "rolle", "repo", "quant", "quelle", "rang", "datei", "dateien", "mmproj", "draft", "flags",
|
||||||
"groesse_gb", "passt", "eng", "ctx", "bedarf_gb", "arch", "begruendung")
|
"groesse_gb", "passt", "eng", "ctx", "bedarf_gb", "arch", "mtp", "begruendung")
|
||||||
|
|
||||||
|
|
||||||
def _suche_anwenden(stand: dict, bewertet: list[dict], quellen_ok: dict[str, bool], zeit: float) -> dict:
|
def _suche_anwenden(stand: dict, bewertet: list[dict], quellen_ok: dict[str, bool], zeit: float) -> dict:
|
||||||
@@ -1101,6 +1104,74 @@ def _baseline(rolle: str, frist: float) -> dict | None:
|
|||||||
protokoll=_protokoll)
|
protokoll=_protokoll)
|
||||||
|
|
||||||
|
|
||||||
|
_ARCH_CACHE: dict[tuple[str, float], str | None] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _lokale_architektur(pfad: str) -> str | None:
|
||||||
|
"""general.architecture einer lokalen GGUF-Datei (die ersten 8 MB reichen, der Kopf steht vorn)."""
|
||||||
|
try:
|
||||||
|
schluessel = (pfad, os.path.getmtime(pfad))
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
if schluessel not in _ARCH_CACHE:
|
||||||
|
try:
|
||||||
|
with open(pfad, "rb") as f:
|
||||||
|
kopf = gguf_kopf_aus_bytes(f.read(8 << 20))
|
||||||
|
except OSError:
|
||||||
|
kopf = None
|
||||||
|
_ARCH_CACHE[schluessel] = (kopf or {}).get("general.architecture")
|
||||||
|
return _ARCH_CACHE[schluessel]
|
||||||
|
|
||||||
|
|
||||||
|
def _live_draft(heute: dict) -> dict | None:
|
||||||
|
"""Spekulatives Dekodieren des heutigen Modells der Rolle: Typ, Draft-Datei, n_max und die Architektur."""
|
||||||
|
befehl = str(heute.get("cmd") or "")
|
||||||
|
typ = re.search(r"--spec-type\s+(\S+)", befehl)
|
||||||
|
if not typ:
|
||||||
|
return None
|
||||||
|
pfad = re.search(r"--spec-draft-model\s+(\S+)", befehl)
|
||||||
|
n_max = re.search(r"--spec-draft-n-max\s+(\d+)", befehl)
|
||||||
|
return {"typ": typ.group(1), "pfad": pfad.group(1) if pfad else None,
|
||||||
|
"n_max": int(n_max.group(1)) if n_max else None,
|
||||||
|
"arch": _lokale_architektur(str(heute.get("gguf_path") or ""))}
|
||||||
|
|
||||||
|
|
||||||
|
def _draft_varianten(k: dict, pfade: dict) -> list[dict]:
|
||||||
|
"""Womit der Kandidat spekulativ dekodieren könnte. Fairness: das heutige Modell misst mit seinem Draft
|
||||||
|
(Hirn 106 t/s, ohne Draft 68) — Ornith fiel am 24.09. ohne Draft durch, obwohl es dieselbe Architektur hat
|
||||||
|
und den Draft des Hirns hätte nutzen können. Ausprobiert werden: ohne Draft, ein eingebauter MTP-Kopf und
|
||||||
|
der Draft des heutigen Modells (nur bei gleicher Architektur und wenn der Speicher reicht). Ein Draft aus
|
||||||
|
der Merkliste ist eine bewusste Wahl und wird allein genommen. „ort“ sagt, wo die Draft-Datei liegt:
|
||||||
|
im Kandidaten-Ordner (wandert beim Übernehmen mit) oder fremd (fester Pfad)."""
|
||||||
|
ps = _pruefstand()
|
||||||
|
if (d := k.get("draft") or {}).get("typ"):
|
||||||
|
return [{"name": "Draft aus der Merkliste", "flags": ps.draft_flags(pfade.get("draft"), d["typ"], d.get("n_max")),
|
||||||
|
"draft": {"typ": d["typ"], "ort": "kandidat" if pfade.get("draft") else None, "n_max": d.get("n_max")}}]
|
||||||
|
varianten = [{"name": "ohne Draft", "flags": [], "draft": None}]
|
||||||
|
if k.get("mtp"):
|
||||||
|
varianten.append({"name": "eingebauter MTP-Kopf", "flags": ps.draft_flags(None, "draft-mtp"),
|
||||||
|
"draft": {"typ": "draft-mtp", "ort": None, "n_max": None}})
|
||||||
|
live = _live_draft(_heutige_modelle().get(k["rolle"]) or {})
|
||||||
|
if live and live["pfad"] and k.get("arch") and live["arch"] == k["arch"]:
|
||||||
|
try:
|
||||||
|
draft_gb = os.path.getsize(live["pfad"]) / 1e9
|
||||||
|
except OSError:
|
||||||
|
draft_gb = None
|
||||||
|
if draft_gb is not None and float(k.get("bedarf_gb") or 0) + draft_gb <= SPEICHER_GRENZE_GB - WARMSET_GB:
|
||||||
|
varianten.append({"name": "Draft des heutigen Modells",
|
||||||
|
"flags": ps.draft_flags(live["pfad"], live["typ"], live["n_max"]),
|
||||||
|
"draft": {"typ": live["typ"], "ort": "fremd", "pfad": live["pfad"], "n_max": live["n_max"]}})
|
||||||
|
return varianten
|
||||||
|
|
||||||
|
|
||||||
|
def _draft_text(wahl: dict) -> str:
|
||||||
|
"""„Gemessen mit …“ für die Begründung: gewählte Variante und die Tempi aller Varianten."""
|
||||||
|
tempi = [f"{name} {str(wert).replace('.', ',')} t/s" if isinstance(wert, (int, float)) else f"{name}: {wert}"
|
||||||
|
for name, wert in (wahl.get("messungen") or {}).items()]
|
||||||
|
return f" Gemessen {'mit' if wahl.get('draft') else ''} {wahl['name']}".replace(" ", " ") + (
|
||||||
|
f" (13k: {'; '.join(tempi)})." if len(tempi) > 1 else ".")
|
||||||
|
|
||||||
|
|
||||||
def teste(kid: str, frist: float) -> dict | None:
|
def teste(kid: str, frist: float) -> dict | None:
|
||||||
"""Einen Kandidaten prüfen: Download → Baseline → nur Warm-Set in llama-swap → Kandidat auf :5899 →
|
"""Einen Kandidaten prüfen: Download → Baseline → nur Warm-Set in llama-swap → Kandidat auf :5899 →
|
||||||
Urteil. Gibt den Test-Eintrag zurück (None, wenn nur gemessen wurde oder es den Kandidaten nicht gibt)."""
|
Urteil. Gibt den Test-Eintrag zurück (None, wenn nur gemessen wurde oder es den Kandidaten nicht gibt)."""
|
||||||
@@ -1110,24 +1181,31 @@ def teste(kid: str, frist: float) -> dict | None:
|
|||||||
return None
|
return None
|
||||||
_markiere_lauf(kid, "download", frist)
|
_markiere_lauf(kid, "download", frist)
|
||||||
ergebnis, satz, werte, vergleich, zaehlt = "abgebrochen", "", None, None, True
|
ergebnis, satz, werte, vergleich, zaehlt = "abgebrochen", "", None, None, True
|
||||||
waechter, von_aussen = None, None
|
waechter, von_aussen, draft_betrieb = None, None, None
|
||||||
try:
|
try:
|
||||||
ordner = _download(k, frist - TESTZEIT_MIN * 60)
|
ordner = _download(k, frist - TESTZEIT_MIN * 60)
|
||||||
pfade = _lokale_pfade(k, ordner)
|
pfade = _lokale_pfade(k, ordner)
|
||||||
_markiere_lauf(kid, "baseline", frist)
|
_markiere_lauf(kid, "baseline", frist)
|
||||||
basis = _baseline(k["rolle"], frist)
|
basis = _baseline(k["rolle"], frist)
|
||||||
_markiere_lauf(kid, "test", frist)
|
|
||||||
waechter = _Nachtwaechter(_nur_warmset())
|
waechter = _Nachtwaechter(_nur_warmset())
|
||||||
waechter.start()
|
waechter.start()
|
||||||
draft = k.get("draft") or {}
|
varianten = _draft_varianten(k, pfade)
|
||||||
flags = ps.draft_flags(pfade["draft"], draft.get("typ"), draft.get("n_max")) + list(k.get("flags") or [])
|
if len(varianten) > 1:
|
||||||
|
_markiere_lauf(kid, "draft", frist)
|
||||||
|
wahl = ps.waehle_draft(pfade["gguf"], varianten, frist=frist, protokoll=_protokoll)
|
||||||
|
else:
|
||||||
|
wahl = {**varianten[0], "messungen": {}}
|
||||||
|
_markiere_lauf(kid, "test", frist)
|
||||||
|
flags = list(wahl["flags"]) + list(k.get("flags") or [])
|
||||||
lauf = ps.pruefe_kandidat(k["rolle"], pfade["gguf"], mmproj=pfade["mmproj"], flags=flags,
|
lauf = ps.pruefe_kandidat(k["rolle"], pfade["gguf"], mmproj=pfade["mmproj"], flags=flags,
|
||||||
ctx=int(k.get("ctx") or ps.CTX_STANDARD), basis=(basis or {}).get("werte"),
|
ctx=int(k.get("ctx") or ps.CTX_STANDARD), basis=(basis or {}).get("werte"),
|
||||||
frist=frist, protokoll=_protokoll)
|
frist=frist, protokoll=_protokoll)
|
||||||
werte, vergleich = lauf["werte"], lauf["vergleich"]
|
werte, vergleich = lauf["werte"], lauf["vergleich"]
|
||||||
|
werte["draft"] = {"gewaehlt": wahl["name"], "messungen": wahl.get("messungen") or {}}
|
||||||
|
draft_betrieb = wahl.get("draft")
|
||||||
if basis:
|
if basis:
|
||||||
werte["baseline"] = {"modell": (basis.get("werte") or {}).get("modell"), "gemessen": _iso(basis.get("zeit"))}
|
werte["baseline"] = {"modell": (basis.get("werte") or {}).get("modell"), "gemessen": _iso(basis.get("zeit"))}
|
||||||
ergebnis, satz = lauf["ergebnis"], lauf["zusammenfassung"]
|
ergebnis, satz = lauf["ergebnis"], lauf["zusammenfassung"] + _draft_text(wahl)
|
||||||
except ps.Zeitende:
|
except ps.Zeitende:
|
||||||
satz = f"Das Nachtfenster endete um {FENSTER_ENDE}, bevor der Test fertig war."
|
satz = f"Das Nachtfenster endete um {FENSTER_ENDE}, bevor der Test fertig war."
|
||||||
except ps.ServerFehler as e:
|
except ps.ServerFehler as e:
|
||||||
@@ -1153,6 +1231,8 @@ def teste(kid: str, frist: float) -> dict | None:
|
|||||||
|
|
||||||
def abschluss(stand: dict):
|
def abschluss(stand: dict):
|
||||||
stand["lauf"] = None
|
stand["lauf"] = None
|
||||||
|
if kid in stand["kandidaten"]: # der Draft, mit dem gemessen wurde, läuft auch im Betrieb
|
||||||
|
stand["kandidaten"][kid]["draft_betrieb"] = draft_betrieb
|
||||||
return _abschluss(stand, kid, ergebnis, satz, werte=werte, vergleich=vergleich, zaehlt=zaehlt)
|
return _abschluss(stand, kid, ergebnis, satz, werte=werte, vergleich=vergleich, zaehlt=zaehlt)
|
||||||
test = _aendere(abschluss)
|
test = _aendere(abschluss)
|
||||||
log.info("radar: %s → %s (%s)", kid, ergebnis, satz)
|
log.info("radar: %s → %s (%s)", kid, ergebnis, satz)
|
||||||
@@ -1197,23 +1277,53 @@ def notstopp() -> None:
|
|||||||
# --- Übernehmen und Verwerfen ----------------------------------------------------------------
|
# --- Übernehmen und Verwerfen ----------------------------------------------------------------
|
||||||
|
|
||||||
def betriebs_befehl(rolle: str, pfade: dict, k: dict) -> str:
|
def betriebs_befehl(rolle: str, pfade: dict, k: dict) -> str:
|
||||||
"""llama-swap-Befehl für den übernommenen Kandidaten: die getesteten Flags plus die Betriebs-Flags
|
"""llama-swap-Befehl für den übernommenen Kandidaten wie heute im Betrieb: ohne Projektor (Bilder gehen an
|
||||||
der Rolle wie heute (Hirn 2 Slots, Coder 1 Slot, KV q8_0, Prompt-Cache 16 GB)."""
|
den Zwilling), mit dem Draft, mit dem er getestet wurde, dazu die Betriebs-Flags der Rolle (Hirn 2 Slots,
|
||||||
|
Coder 1 Slot, KV q8_0, Prompt-Cache 16 GB)."""
|
||||||
teile = ["llama-server", "-m", pfade["gguf"], "--host", "127.0.0.1", "--port", "${PORT}",
|
teile = ["llama-server", "-m", pfade["gguf"], "--host", "127.0.0.1", "--port", "${PORT}",
|
||||||
"-c", str(int(k.get("ctx") or CTX_ROLLE[rolle])), "-ngl", "999", "-fa", "on", "--load-mode", "none"]
|
"-c", str(int(k.get("ctx") or CTX_ROLLE[rolle])), "-ngl", "999", "-fa", "on", "--load-mode", "none",
|
||||||
if pfade.get("mmproj"):
|
"--jinja", "--parallel", str(PARALLEL[rolle]), "-cram", "16384", "-ctk", "q8_0", "-ctv", "q8_0"]
|
||||||
teile += ["--mmproj", pfade["mmproj"]]
|
draft = k.get("draft_betrieb")
|
||||||
teile += ["--jinja", "--parallel", str(PARALLEL[rolle]), "-cram", "16384", "-ctk", "q8_0", "-ctv", "q8_0"]
|
if draft is None and (k.get("draft") or {}).get("typ"): # vor dem 25.09. getestet: Draft der Merkliste
|
||||||
draft = k.get("draft") or {}
|
draft = {"typ": k["draft"]["typ"], "ort": "kandidat", "n_max": k["draft"].get("n_max")}
|
||||||
if draft.get("typ"):
|
if draft and draft.get("typ"):
|
||||||
teile += ["--spec-type", draft["typ"]]
|
teile += ["--spec-type", draft["typ"]]
|
||||||
if pfade.get("draft"):
|
pfad = pfade.get("draft") if draft.get("ort") == "kandidat" else draft.get("pfad")
|
||||||
teile += ["--spec-draft-model", pfade["draft"]]
|
if pfad:
|
||||||
|
teile += ["--spec-draft-model", pfad]
|
||||||
if draft.get("n_max"):
|
if draft.get("n_max"):
|
||||||
teile += ["--spec-draft-n-max", str(draft["n_max"])]
|
teile += ["--spec-draft-n-max", str(draft["n_max"])]
|
||||||
return " ".join(teile + [str(f) for f in k.get("flags") or []])
|
return " ".join(teile + [str(f) for f in k.get("flags") or []])
|
||||||
|
|
||||||
|
|
||||||
|
def zwilling_befehl(rolle: str, pfade: dict, k: dict) -> str:
|
||||||
|
"""Bild-Zwilling im Betrieb (seit 24.09.): gleiche Gewichte plus Projektor, OHNE Draft — llama.cpp kann
|
||||||
|
Draft und Bild nicht zusammen. Das Gateway schickt nur Anfragen mit einem neuen Bild hierher."""
|
||||||
|
teile = ["llama-server", "-m", pfade["gguf"], "--host", "127.0.0.1", "--port", "${PORT}",
|
||||||
|
"-c", str(ZWILLING_CTX[rolle]), "-ngl", "999", "-fa", "on", "--load-mode", "none",
|
||||||
|
"--mmproj", pfade["mmproj"], "--jinja", "--parallel", "1", "-cram", "8192", "-ctk", "q8_0", "-ctv", "q8_0"]
|
||||||
|
return " ".join(teile + _pruefstand().ohne_draft([str(f) for f in k.get("flags") or []]))
|
||||||
|
|
||||||
|
|
||||||
|
def _zwilling_eintragen(cfg: dict, rolle: str, modell_id: str, pfade: dict, k: dict) -> str:
|
||||||
|
"""Bild-Zwilling des neuen Modells anlegen (Gruppe „bild“, verdrängt nichts). Der alte Zwilling der Rolle
|
||||||
|
fliegt aus der Config — ohne sein Hauptmodell ist er nutzlos; seine Dateien bleiben liegen und
|
||||||
|
tauchen im Aufräumen beim alten Modell auf."""
|
||||||
|
alias = ZWILLING[rolle]
|
||||||
|
models = cfg.setdefault("models", {})
|
||||||
|
groups = cfg.setdefault("groups", {})
|
||||||
|
for alt in [n for n, sp in models.items() if alias in {str(a).lower() for a in (sp or {}).get("aliases") or []}]:
|
||||||
|
del models[alt]
|
||||||
|
for gruppe in groups.values():
|
||||||
|
if isinstance(gruppe, dict) and alt in (gruppe.get("members") or []):
|
||||||
|
gruppe["members"] = [m for m in gruppe["members"] if m != alt]
|
||||||
|
name = f"{modell_id}-Bild"
|
||||||
|
models[name] = {"cmd": LiteralScalarString(zwilling_befehl(rolle, pfade, k) + "\n"), "ttl": 900, "aliases": [alias]}
|
||||||
|
bild = groups.setdefault("bild", {"swap": True, "exclusive": False, "persistent": False, "members": []})
|
||||||
|
bild["members"] = [m for m in bild.get("members") or [] if m != name] + [name]
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _steward_neu_starten() -> str | None:
|
def _steward_neu_starten() -> str | None:
|
||||||
"""Drop-in neu einlesen und den Steward neu starten (User-Dienst, ohne sudo). Fehlertext oder None."""
|
"""Drop-in neu einlesen und den Steward neu starten (User-Dienst, ohne sudo). Fehlertext oder None."""
|
||||||
if os.name != "posix":
|
if os.name != "posix":
|
||||||
@@ -1275,12 +1385,14 @@ def _tausche_ein(k: dict) -> dict:
|
|||||||
ziel = quelle # schon verschoben (früherer, abgebrochener Versuch)
|
ziel = quelle # schon verschoben (früherer, abgebrochener Versuch)
|
||||||
pfade = _lokale_pfade(k, ziel)
|
pfade = _lokale_pfade(k, ziel)
|
||||||
modell_id = llamaswap.register_model(pfade["gguf"], role=ALIAS[rolle], ctx=int(k.get("ctx") or CTX_ROLLE[rolle]),
|
modell_id = llamaswap.register_model(pfade["gguf"], role=ALIAS[rolle], ctx=int(k.get("ctx") or CTX_ROLLE[rolle]),
|
||||||
mmproj_path=pfade["mmproj"], jinja=True, set_alias=False)
|
mmproj_path=None, jinja=True, set_alias=False)
|
||||||
cfg = llamaswap.read_config()
|
cfg = llamaswap.read_config()
|
||||||
try: # die getesteten Flags statt der Vorlage aus register_model (sonst liefe ein ungetesteter Draft mit)
|
try: # die getesteten Flags statt der Vorlage aus register_model (sonst liefe ein ungetesteter Draft mit)
|
||||||
cfg["models"][modell_id]["cmd"] = LiteralScalarString(betriebs_befehl(rolle, pfade, k) + "\n")
|
cfg["models"][modell_id]["cmd"] = LiteralScalarString(betriebs_befehl(rolle, pfade, k) + "\n")
|
||||||
except (KeyError, TypeError) as e:
|
except (KeyError, TypeError) as e:
|
||||||
raise RuntimeError(f"{modell_id} fehlt nach dem Eintragen in der llama-swap-Config.") from e
|
raise RuntimeError(f"{modell_id} fehlt nach dem Eintragen in der llama-swap-Config.") from e
|
||||||
|
if pfade.get("mmproj"):
|
||||||
|
_zwilling_eintragen(cfg, rolle, modell_id, pfade, k)
|
||||||
llamaswap.write_config(cfg)
|
llamaswap.write_config(cfg)
|
||||||
hinweis = None
|
hinweis = None
|
||||||
if rolle == "hirn":
|
if rolle == "hirn":
|
||||||
|
|||||||
@@ -355,7 +355,10 @@ def _uebernahme_vorbereiten(zustand, monkeypatch, rolle: str, alt: dict) -> tupl
|
|||||||
stand = radar._leer()
|
stand = radar._leer()
|
||||||
stand["kandidaten"]["o"] = _kandidat("o", rolle=rolle, status="bestanden", ordner=str(ordner))
|
stand["kandidaten"]["o"] = _kandidat("o", rolle=rolle, status="bestanden", ordner=str(ordner))
|
||||||
radar._speichere(stand)
|
radar._speichere(stand)
|
||||||
aufrufe, cfg = [], {"models": {}}
|
aufrufe = []
|
||||||
|
cfg = {"models": {"alter-zwilling": {"cmd": "llama-server -m /alt.gguf --mmproj /alt-mm.gguf",
|
||||||
|
"aliases": ["vision" if rolle == "hirn" else "coder-bild"]}},
|
||||||
|
"groups": {"bild": {"swap": True, "exclusive": False, "members": ["alter-zwilling"]}}}
|
||||||
|
|
||||||
def eintragen(pfad, **kw):
|
def eintragen(pfad, **kw):
|
||||||
aufrufe.append(("eintragen", pfad, kw["role"], kw["set_alias"]))
|
aufrufe.append(("eintragen", pfad, kw["role"], kw["set_alias"]))
|
||||||
@@ -386,7 +389,11 @@ def test_uebernehmen_hirn_nutzt_den_hirn_wechsel_und_nimmt_fast_mit(zustand, mon
|
|||||||
assert [a[0] for a in aufrufe] == ["eintragen", "schreiben", "hirn", "dazu", "steward"]
|
assert [a[0] for a in aufrufe] == ["eintragen", "schreiben", "hirn", "dazu", "steward"]
|
||||||
assert aufrufe[0][2:] == ("hermes", False) and aufrufe[3] == ("dazu", "o", "fast")
|
assert aufrufe[0][2:] == ("hermes", False) and aufrufe[3] == ("dazu", "o", "fast")
|
||||||
befehl = str(cfg["models"]["o"]["cmd"])
|
befehl = str(cfg["models"]["o"]["cmd"])
|
||||||
assert "--parallel 2" in befehl and "-ctk q8_0" in befehl and "--mmproj" in befehl
|
assert "--parallel 2" in befehl and "-ctk q8_0" in befehl and "--mmproj" not in befehl
|
||||||
|
# Wie im Betrieb seit 24.09.: Bilder sieht der neue Bild-Zwilling (Projektor, ohne Draft), der alte fliegt raus.
|
||||||
|
zwilling = cfg["models"]["o-Bild"]
|
||||||
|
assert zwilling["aliases"] == ["vision"] and "--mmproj" in str(zwilling["cmd"]) and "--spec" not in str(zwilling["cmd"])
|
||||||
|
assert "alter-zwilling" not in cfg["models"] and cfg["groups"]["bild"]["members"] == ["o-Bild"]
|
||||||
assert (zustand / "models" / "o-GGUF" / "o-Q4_K_M.gguf").exists() and not (radar.RADAR_DIR / "o").exists()
|
assert (zustand / "models" / "o-GGUF" / "o-Q4_K_M.gguf").exists() and not (radar.RADAR_DIR / "o").exists()
|
||||||
k = radar._lade()["kandidaten"]["o"]
|
k = radar._lade()["kandidaten"]["o"]
|
||||||
assert k["status"] == "uebernommen" and "vorher Qwen3.6-35B-A3B" in k["begruendung"]
|
assert k["status"] == "uebernommen" and "vorher Qwen3.6-35B-A3B" in k["begruendung"]
|
||||||
@@ -405,16 +412,98 @@ def test_uebernehmen_coder_nimmt_heavy_mit(zustand, monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
def test_betriebsbefehl_uebernimmt_die_getesteten_flags():
|
def test_betriebsbefehl_uebernimmt_die_getesteten_flags():
|
||||||
|
pfade = {"gguf": "/srv/models/F-GGUF/f.gguf", "mmproj": "/srv/models/F-GGUF/mm.gguf", "draft": None}
|
||||||
k = _kandidat("f", rolle="coder", ctx=65536, draft={"typ": "draft-mtp", "n_max": 2, "datei": None}, flags=["-ub", "512"])
|
k = _kandidat("f", rolle="coder", ctx=65536, draft={"typ": "draft-mtp", "n_max": 2, "datei": None}, flags=["-ub", "512"])
|
||||||
befehl = radar.betriebs_befehl("coder", {"gguf": "/srv/models/F-GGUF/f.gguf", "mmproj": "/srv/models/F-GGUF/mm.gguf",
|
befehl = radar.betriebs_befehl("coder", pfade, k)
|
||||||
"draft": None}, k)
|
|
||||||
assert "-c 65536" in befehl and "--parallel 1" in befehl and "-ctk q8_0 -ctv q8_0" in befehl
|
assert "-c 65536" in befehl and "--parallel 1" in befehl and "-ctk q8_0 -ctv q8_0" in befehl
|
||||||
assert "--mmproj /srv/models/F-GGUF/mm.gguf" in befehl and "--load-mode none" in befehl
|
assert "--mmproj" not in befehl and "--load-mode none" in befehl # Bilder sieht der Zwilling
|
||||||
assert befehl.endswith("--spec-type draft-mtp --spec-draft-n-max 2 -ub 512")
|
assert befehl.endswith("--spec-type draft-mtp --spec-draft-n-max 2 -ub 512")
|
||||||
|
zwilling = radar.zwilling_befehl("coder", pfade, k)
|
||||||
|
assert "--mmproj /srv/models/F-GGUF/mm.gguf" in zwilling and "--spec" not in zwilling and "-c 131072" in zwilling
|
||||||
|
assert zwilling.endswith("-ub 512")
|
||||||
assert "--parallel 2" in radar.betriebs_befehl("hirn", {"gguf": "x.gguf", "mmproj": None, "draft": None},
|
assert "--parallel 2" in radar.betriebs_befehl("hirn", {"gguf": "x.gguf", "mmproj": None, "draft": None},
|
||||||
_kandidat("h"))
|
_kandidat("h"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_betriebsbefehl_nimmt_den_im_test_gewaehlten_draft():
|
||||||
|
"""Gewählt wurde der Draft des heutigen Modells: fester Pfad, nicht der Kandidaten-Ordner."""
|
||||||
|
pfade = {"gguf": "/srv/models/O-GGUF/o.gguf", "mmproj": "/srv/models/O-GGUF/mm.gguf", "draft": None}
|
||||||
|
k = _kandidat("o", draft_betrieb={"typ": "draft-dflash", "ort": "fremd", "pfad": "/srv/models/D/d.gguf",
|
||||||
|
"n_max": None})
|
||||||
|
assert radar.betriebs_befehl("hirn", pfade, k).endswith("--spec-type draft-dflash --spec-draft-model /srv/models/D/d.gguf")
|
||||||
|
k["draft_betrieb"] = None # im Test war „ohne Draft“ am schnellsten
|
||||||
|
assert "--spec" not in radar.betriebs_befehl("hirn", pfade, k)
|
||||||
|
|
||||||
|
|
||||||
|
def _gguf_datei(pfad, arch: str) -> None:
|
||||||
|
"""Winzige GGUF-Datei mit general.architecture (genug für _lokale_architektur)."""
|
||||||
|
wert = arch.encode()
|
||||||
|
schluessel = b"general.architecture"
|
||||||
|
daten = (b"GGUF" + struct.pack("<I", 3) + struct.pack("<Q", 0) + struct.pack("<Q", 1)
|
||||||
|
+ struct.pack("<Q", len(schluessel)) + schluessel + struct.pack("<I", 8)
|
||||||
|
+ struct.pack("<Q", len(wert)) + wert)
|
||||||
|
pfad.write_bytes(daten)
|
||||||
|
|
||||||
|
|
||||||
|
def test_draft_varianten_probieren_den_draft_des_heutigen_modells_bei_gleicher_architektur(tmp_path, monkeypatch):
|
||||||
|
hirn_gguf, draft = tmp_path / "hirn.gguf", tmp_path / "dflash.gguf"
|
||||||
|
_gguf_datei(hirn_gguf, "qwen35moe")
|
||||||
|
draft.write_bytes(b"x" * 1000)
|
||||||
|
heute = {"name": "Hirn", "gguf_path": str(hirn_gguf),
|
||||||
|
"cmd": f"llama-server -m {hirn_gguf} --spec-type draft-dflash --spec-draft-model {draft}"}
|
||||||
|
monkeypatch.setattr(radar, "_heutige_modelle", lambda: {"hirn": heute})
|
||||||
|
pfade = {"gguf": "/x/k.gguf", "mmproj": "/x/mm.gguf", "draft": None}
|
||||||
|
|
||||||
|
gleich = radar._draft_varianten(_kandidat("k", arch="qwen35moe", bedarf_gb=24.2, mtp=True), pfade)
|
||||||
|
assert [v["name"] for v in gleich] == ["ohne Draft", "eingebauter MTP-Kopf", "Draft des heutigen Modells"]
|
||||||
|
assert gleich[2]["flags"] == ["--spec-type", "draft-dflash", "--spec-draft-model", str(draft)]
|
||||||
|
assert gleich[2]["draft"]["ort"] == "fremd"
|
||||||
|
|
||||||
|
fremd = radar._draft_varianten(_kandidat("k", arch="qwen3next", bedarf_gb=24.2), pfade)
|
||||||
|
assert [v["name"] for v in fremd] == ["ohne Draft"] # andere Architektur: nicht probieren
|
||||||
|
zu_gross = radar._draft_varianten(_kandidat("k", arch="qwen35moe", bedarf_gb=88.0), pfade)
|
||||||
|
assert [v["name"] for v in zu_gross] == ["ohne Draft"] # Draft passt nicht mehr in den Speicher
|
||||||
|
merkliste = radar._draft_varianten(_kandidat("k", arch="qwen35moe", draft={"typ": "draft-simple"}),
|
||||||
|
{**pfade, "draft": "/x/d.gguf"})
|
||||||
|
assert [v["name"] for v in merkliste] == ["Draft aus der Merkliste"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_waehle_draft_nimmt_die_schnellste_variante_die_startet(ps, monkeypatch):
|
||||||
|
tempi = {"": 67.4, "draft-mtp": 81.0, "draft-dflash": 98.2}
|
||||||
|
|
||||||
|
def mit_server(gguf, arbeit, *, flags=(), **kw):
|
||||||
|
typ = flags[1] if flags else ""
|
||||||
|
if typ == "draft-mtp":
|
||||||
|
raise ps.ServerFehler("vocab mismatch")
|
||||||
|
return {"tiefe": {"tg_tps": tempi[typ], "acc": 0.8 if typ else None}}, None
|
||||||
|
|
||||||
|
monkeypatch.setattr(ps, "mit_server", mit_server)
|
||||||
|
varianten = [{"name": "ohne Draft", "flags": []},
|
||||||
|
{"name": "eingebauter MTP-Kopf", "flags": ["--spec-type", "draft-mtp"]},
|
||||||
|
{"name": "Draft des heutigen Modells", "flags": ["--spec-type", "draft-dflash"]}]
|
||||||
|
wahl = ps.waehle_draft("/x/k.gguf", varianten, protokoll=lambda *_: None)
|
||||||
|
assert wahl["name"] == "Draft des heutigen Modells"
|
||||||
|
assert wahl["messungen"]["ohne Draft"] == 67.4 and "geht nicht" in wahl["messungen"]["eingebauter MTP-Kopf"]
|
||||||
|
assert "Gemessen mit Draft des heutigen Modells" in radar._draft_text({**wahl, "draft": {"typ": "draft-dflash"}})
|
||||||
|
|
||||||
|
|
||||||
|
def test_mit_draft_laeuft_die_bild_probe_auf_einem_zwilling_ohne_draft(ps, monkeypatch):
|
||||||
|
"""Draft und Bild gehen in llama.cpp nicht zusammen (HTTP 500) — der Prüfstand trennt sie wie der Betrieb."""
|
||||||
|
starts = []
|
||||||
|
|
||||||
|
def mit_server(gguf, arbeit, *, mmproj=None, flags=(), **kw):
|
||||||
|
starts.append((mmproj, list(flags)))
|
||||||
|
return ({"bild": None, "tiefe": {}} if len(starts) == 1 else {"direkt": 1.0, "agentisch": True}), \
|
||||||
|
type("S", (), {"befehl": ["llama-server"], "ladezeit": 4})()
|
||||||
|
|
||||||
|
monkeypatch.setattr(ps, "mit_server", mit_server)
|
||||||
|
lauf = ps.pruefe_kandidat("hirn", "/x/k.gguf", mmproj="/x/mm.gguf",
|
||||||
|
flags=["--spec-type", "draft-dflash", "--spec-draft-model", "/d.gguf", "-ub", "512"])
|
||||||
|
assert starts[0] == (None, ["--spec-type", "draft-dflash", "--spec-draft-model", "/d.gguf", "-ub", "512"])
|
||||||
|
assert starts[1] == ("/x/mm.gguf", ["-ub", "512"])
|
||||||
|
assert lauf["werte"]["bild"] == {"direkt": 1.0, "agentisch": True}
|
||||||
|
|
||||||
|
|
||||||
# --- Prüfstand: Urteil, Programmieraufgaben, Bild ----------------------------------------------------
|
# --- Prüfstand: Urteil, Programmieraufgaben, Bild ----------------------------------------------------
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
|
|||||||
+81
-17
@@ -763,18 +763,21 @@ def pruefe(rolle: str, basis: str, modell: str, *, bild: bool = False, frist: fl
|
|||||||
else:
|
else:
|
||||||
werte["code"] = messe_code(basis, modell, frist)
|
werte["code"] = messe_code(basis, modell, frist)
|
||||||
protokoll(f" code : {werte['code']['ok']}/{werte['code']['von']} in {werte['code']['sekunden']} s")
|
protokoll(f" code : {werte['code']['ok']}/{werte['code']['von']} in {werte['code']['sekunden']} s")
|
||||||
werte["bild"] = None
|
werte["bild"] = bild_probe(basis, bild_modell or modell, rolle, frist, protokoll) if bild else None
|
||||||
if bild:
|
werte["dauer_s"] = round(time.time() - t0)
|
||||||
|
return werte
|
||||||
|
|
||||||
|
|
||||||
|
def bild_probe(basis: str, modell: str, rolle: str, frist: float | None = None, protokoll=log) -> dict:
|
||||||
|
"""Bild-Probe mit Fehlerfang: ein Absturz der Probe ist ein Messwert, kein Programmfehler."""
|
||||||
try:
|
try:
|
||||||
werte["bild"] = messe_bild(basis, bild_modell or modell, rolle, frist)
|
ergebnis = messe_bild(basis, modell, rolle, frist)
|
||||||
except Zeitende:
|
except Zeitende:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
werte["bild"] = {"direkt": 0.0, "gefunden": [], "agentisch": False, "fehler": str(e)[:160]}
|
ergebnis = {"direkt": 0.0, "gefunden": [], "agentisch": False, "fehler": str(e)[:160]}
|
||||||
protokoll(f" bild : {werte['bild'].get('direkt')} direkt, im Ablauf "
|
protokoll(f" bild : {ergebnis.get('direkt')} direkt, im Ablauf {'ja' if ergebnis.get('agentisch') else 'nein'}")
|
||||||
f"{'ja' if werte['bild'].get('agentisch') else 'nein'}")
|
return ergebnis
|
||||||
werte["dauer_s"] = round(time.time() - t0)
|
|
||||||
return werte
|
|
||||||
|
|
||||||
|
|
||||||
# --- Kandidaten-Server -------------------------------------------------------------------
|
# --- Kandidaten-Server -------------------------------------------------------------------
|
||||||
@@ -908,30 +911,91 @@ def starte_server(gguf: str, *, mmproj: str | None = None, ctx: int = CTX_STANDA
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def pruefe_kandidat(rolle: str, gguf: str, *, mmproj: str | None = None, flags=(), ctx: int = CTX_STANDARD,
|
def mit_server(gguf: str, arbeit, *, mmproj: str | None = None, ctx: int = CTX_STANDARD, flags=(),
|
||||||
basis: dict | None = None, frist: float | None = None, protokoll=log) -> dict:
|
frist: float | None = None, protokoll=log):
|
||||||
"""Ganzer Durchlauf eines Kandidaten: Server starten → messen → stoppen → urteilen.
|
"""Kandidat starten, arbeit() laufen lassen, stoppen. Gibt (Ergebnis, Server) zurück; ein Absturz
|
||||||
basis = Werte der Baseline (ohne sie gibt es kein Urteil, ergebnis None).
|
während der Arbeit wird zu ServerFehler — ein toter Server darf nicht wie ein schlechtes Modell aussehen."""
|
||||||
Rückgabe: {"werte", "ergebnis", "vergleich", "zusammenfassung"}."""
|
|
||||||
server = starte_server(gguf, mmproj=mmproj, ctx=ctx, flags=flags, frist=frist, protokoll=protokoll)
|
server = starte_server(gguf, mmproj=mmproj, ctx=ctx, flags=flags, frist=frist, protokoll=protokoll)
|
||||||
|
|
||||||
def absturz() -> str:
|
def absturz() -> str:
|
||||||
return f"Während des Tests abgestürzt (Code {server.prozess.returncode}): {log_ende(SERVER_LOG, 300)}"
|
return f"Während des Tests abgestürzt (Code {server.prozess.returncode}): {log_ende(SERVER_LOG, 300)}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
werte = pruefe(rolle, KANDIDAT_URL, "kandidat", bild=bool(mmproj), frist=frist, protokoll=protokoll)
|
ergebnis = arbeit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not isinstance(e, Zeitende) and server.prozess.poll() is not None:
|
if not isinstance(e, Zeitende) and server.prozess.poll() is not None:
|
||||||
raise ServerFehler(absturz()) from e
|
raise ServerFehler(absturz()) from e
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
# Einzelproben fangen Fehler ab — ein toter Server darf nicht wie ein schlechtes Modell aussehen.
|
if server.prozess.poll() is not None: # Einzelproben fangen Fehler ab — darum hier nachsehen
|
||||||
if server.prozess.poll() is not None:
|
|
||||||
raise ServerFehler(absturz())
|
raise ServerFehler(absturz())
|
||||||
finally:
|
finally:
|
||||||
stoppe_server(server)
|
stoppe_server(server)
|
||||||
time.sleep(3) # Speicher freigeben lassen, bevor jemand anderes lädt
|
time.sleep(3) # Speicher freigeben lassen, bevor jemand anderes lädt
|
||||||
werte.update(ladezeit_s=server.ladezeit, ctx=ctx, befehl=" ".join(server.befehl))
|
return ergebnis, server
|
||||||
|
|
||||||
|
|
||||||
|
_SPEC_FLAGS = ("--spec-type", "--spec-draft-model", "--spec-draft-n-max")
|
||||||
|
|
||||||
|
|
||||||
|
def ohne_draft(flags) -> list[str]:
|
||||||
|
"""Flags ohne spekulatives Dekodieren (jeweils Schalter plus Wert)."""
|
||||||
|
rest, flags, i = [], list(flags), 0
|
||||||
|
while i < len(flags):
|
||||||
|
if flags[i] in _SPEC_FLAGS:
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
rest.append(flags[i])
|
||||||
|
i += 1
|
||||||
|
return rest
|
||||||
|
|
||||||
|
|
||||||
|
def waehle_draft(gguf: str, varianten: list[dict], *, frist: float | None = None, protokoll=log) -> dict:
|
||||||
|
"""Schnellste Art des spekulativen Dekodierens für einen Kandidaten. Jede Variante {"name", "flags", …}
|
||||||
|
wird gestartet und bei 13k Tiefe gemessen; startet eine nicht (z. B. passt das Vokabular des Drafts
|
||||||
|
nicht), fällt sie raus. Spekulatives Dekodieren ändert die Antworten nicht, nur das Tempo — darum
|
||||||
|
entscheidet allein das Tempo. Rückgabe: die gewählte Variante plus "messungen" (Name → t/s bzw. Grund)."""
|
||||||
|
messungen: dict[str, object] = {}
|
||||||
|
beste: tuple[dict, float] | None = None
|
||||||
|
for v in varianten:
|
||||||
|
try:
|
||||||
|
tempo, _ = mit_server(gguf, lambda: messe_tempo(KANDIDAT_URL, "kandidat", frist), ctx=32768,
|
||||||
|
flags=v["flags"], frist=frist, protokoll=protokoll)
|
||||||
|
except Zeitende:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
messungen[v["name"]] = f"geht nicht: {str(e)[:120]}"
|
||||||
|
protokoll(f" Draft-Variante {v['name']}: geht nicht ({str(e)[:120]})")
|
||||||
|
continue
|
||||||
|
tiefe = tempo.get("tiefe") or {}
|
||||||
|
tps = float(tiefe.get("tg_tps") or 0)
|
||||||
|
messungen[v["name"]] = tps
|
||||||
|
protokoll(f" Draft-Variante {v['name']}: {tps} t/s bei 13k (Akzeptanz {tiefe.get('acc')})")
|
||||||
|
if beste is None or tps > beste[1]:
|
||||||
|
beste = (v, tps)
|
||||||
|
gewaehlt = beste[0] if beste else next((v for v in varianten if not v["flags"]), varianten[0])
|
||||||
|
return {**gewaehlt, "messungen": messungen}
|
||||||
|
|
||||||
|
|
||||||
|
def pruefe_kandidat(rolle: str, gguf: str, *, mmproj: str | None = None, flags=(), ctx: int = CTX_STANDARD,
|
||||||
|
basis: dict | None = None, frist: float | None = None, protokoll=log) -> dict:
|
||||||
|
"""Ganzer Durchlauf eines Kandidaten: Server starten → messen → stoppen → urteilen.
|
||||||
|
basis = Werte der Baseline (ohne sie gibt es kein Urteil, ergebnis None).
|
||||||
|
Mit Draft wie im Betrieb seit 24.09.: Text auf einem Server ohne Projektor, die Bild-Probe auf einem
|
||||||
|
Zwilling ohne Draft — llama.cpp kann Draft und Bild nicht zusammen (HTTP 500).
|
||||||
|
Rückgabe: {"werte", "ergebnis", "vergleich", "zusammenfassung"}."""
|
||||||
|
mit_draft = "--spec-type" in list(flags)
|
||||||
|
text_mmproj = None if mit_draft else mmproj
|
||||||
|
werte, server = mit_server(
|
||||||
|
gguf, lambda: pruefe(rolle, KANDIDAT_URL, "kandidat", bild=bool(text_mmproj), frist=frist, protokoll=protokoll),
|
||||||
|
mmproj=text_mmproj, ctx=ctx, flags=flags, frist=frist, protokoll=protokoll)
|
||||||
|
befehle = [" ".join(server.befehl)]
|
||||||
|
if mit_draft and mmproj:
|
||||||
|
werte["bild"], zwilling = mit_server(
|
||||||
|
gguf, lambda: bild_probe(KANDIDAT_URL, "kandidat", rolle, frist, protokoll),
|
||||||
|
mmproj=mmproj, ctx=min(ctx, 32768), flags=ohne_draft(flags), frist=frist, protokoll=protokoll)
|
||||||
|
befehle.append(" ".join(zwilling.befehl))
|
||||||
|
werte.update(ladezeit_s=server.ladezeit, ctx=ctx, befehl=" | ".join(befehle))
|
||||||
if not basis:
|
if not basis:
|
||||||
return {"werte": werte, "ergebnis": None, "vergleich": {},
|
return {"werte": werte, "ergebnis": None, "vergleich": {},
|
||||||
"zusammenfassung": "Gemessen, aber ohne Vergleichswerte des heutigen Modells."}
|
"zusammenfassung": "Gemessen, aber ohne Vergleichswerte des heutigen Modells."}
|
||||||
|
|||||||
Reference in New Issue
Block a user