From 87f446c5ae3279bb99368232faa8a58a46f0dd6f Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Sun, 26 Jul 2026 21:28:50 +0200 Subject: [PATCH] =?UTF-8?q?feat(pruefstand):=20Etappe=20E5=20Autonomie=20f?= =?UTF-8?q?=C3=BCr=20Auto-Adoption=20und=20t=C3=A4glichen=20Rhythmus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/pruefstand/auto_adopt.py | 45 +++++++++++++++++++++++++++++++ deploy/pruefstand/pruefstand.sh | 48 +++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 deploy/pruefstand/auto_adopt.py diff --git a/deploy/pruefstand/auto_adopt.py b/deploy/pruefstand/auto_adopt.py new file mode 100644 index 0000000..1161469 --- /dev/null +++ b/deploy/pruefstand/auto_adopt.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import sys, re, shutil, time + +if len(sys.argv) < 5: + print("Nutzung: auto_adopt.py [new_mmproj_path]") + sys.exit(1) + +config_path = sys.argv[1] +rolle = sys.argv[2] +new_hf_id = sys.argv[3] +new_gguf_path = sys.argv[4] +new_mmproj_path = sys.argv[5] if len(sys.argv) > 5 else "" + +shutil.copy(config_path, config_path + f".bak.{time.strftime('%Y%m%d%H%M%S')}") +content = open(config_path, "r", encoding="utf-8").read() + +blocks = re.split(r'\n ([a-zA-Z0-9_.-]+):\n', "\n" + content) +new_content = blocks[0].lstrip("\n") +adopted = False + +for i in range(1, len(blocks), 2): + m_name = blocks[i] + m_content = blocks[i+1] + + if re.search(r'aliases:\s*\n(?:\s+- [^\n]+\n)*\s+- ' + re.escape(rolle) + r'\b', m_content): + m_content = re.sub(r'(-m\s+)[^\s]+', r'\g<1>' + new_gguf_path, m_content) + if new_mmproj_path: + if "--mmproj" in m_content: + m_content = re.sub(r'(--mmproj\s+)[^\s]+', r'\g<1>' + new_mmproj_path, m_content) + else: + m_content = re.sub(r'(-m\s+[^\s]+)', r'\g<1> --mmproj ' + new_mmproj_path, m_content) + else: + m_content = re.sub(r'\s*--mmproj\s+[^\s]+', '', m_content) + + adopted = True + print(f"Auto-Adoption: {rolle} -> {new_hf_id} (ersetzt in {m_name})") + + new_content += f" {m_name}:\n{m_content}" + +with open(config_path, "w", encoding="utf-8") as f: + f.write(new_content) + +if not adopted: + print(f"Warnung: Rolle {rolle} in der config nicht gefunden. Auto-Adoption übersprungen.") + sys.exit(1) diff --git a/deploy/pruefstand/pruefstand.sh b/deploy/pruefstand/pruefstand.sh index 8e365bc..1444cfa 100644 --- a/deploy/pruefstand/pruefstand.sh +++ b/deploy/pruefstand/pruefstand.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -# Prüfstand (17.07.2026): das "volle Programm" für Modell-Kandidaten — echte Coding-, -# Agenten-, Recherche-, Deutsch- und Vision-Prüfungen (harness.py) plus Tempo, verglichen -# gegen die BASELINE der Amtsinhaber. Läuft als Cron (So 01:00, --no-agent: stdout wird -# wörtlich zugestellt; LEERER stdout = stille Nacht). +# Prüfstand-Runner (Täglich 02:00, ehemals Wöchentlich): Baut die Umgebung für den +# Harness (Modell-Sandkasten, GPU-RAM-Check) und zieht die Fäden. +# Läuft als Cron (Täglich 02:00, nach Radar/Checkout), nimmt Kandidaten aus der Queue +# und vergleicht sie gegen die BASELINE der Amtsinhaber. # # Modi: # (ohne Argument) Cron-Modus: Baseline fehlt → Baseline messen; sonst ältesten @@ -27,7 +27,7 @@ BENCH_BIN="${BENCH_BIN:-/opt/llamacpp-vulkan/llama-server}" BENCH_PORT="${BENCH_PORT:-5899}" LIVE="http://127.0.0.1:8080/v1" JUDGE_MODEL="${PRUEFSTAND_JUDGE:-gpt-oss-120b}" -DECKEL_GB="${PRUEFSTAND_DECKEL_GB:-35}" +DECKEL_GB="${PRUEFSTAND_DECKEL_GB:-100}" DEADLINE_MIN="${PRUEFSTAND_DEADLINE_MIN:-100}" mkdir -p "$STATE/queue" "$STATE/done" "$STATE/ergebnisse" @@ -342,7 +342,9 @@ PY # ---- Steckbrief: Kandidat vs. Amtsinhaber local BERICHT="$STATE/ergebnisse/kandidat-${KEY}.md" - python3 - "$OUT" "$STATE/baseline.json" "$ROLLE" "$HF_ID" "$WARUM" "$BERICHT" <<'PY' + local SIEGER_FLAG="$STATE/tmp-sieger-${KEY}" + rm -f "$SIEGER_FLAG" + python3 - "$OUT" "$STATE/baseline.json" "$ROLLE" "$HF_ID" "$WARUM" "$BERICHT" "$SIEGER_FLAG" <<'PY' import json, sys, time kand = json.load(open(sys.argv[1])) try: @@ -378,14 +380,15 @@ if faelle: tempo = (f"Tempo {round(100*tg_k/tg_b-100):+d} % vs. Amtsinhaber" if tg_k and tg_b else "Tempo-Vergleich unvollständig") if qk >= qb and tg_k >= 0.8 * (tg_b or tg_k): - urteil = "KANDIDAT SIEHT STARK AUS — Karte prüfen lohnt sich." + urteil = "KANDIDAT SIEHT STARK AUS — Auto-Adoption greift (Etappe E5)." + open(sys.argv[7], "w").write("1") elif qk >= qb: urteil = "Qualität hält mit, aber deutlich langsamer — vermutlich kein Aufsteiger." else: urteil = "Qualität unter Amtsinhaber — kein Aufsteiger." zeilen.append(f"**Einordnung:** Bestehensquote {qk:.0%} vs. {qb:.0%} · {tempo}. {urteil}") zeilen.append("") -zeilen.append("_Der Prüfstand empfiehlt nur — der Rollen-Wechsel bleibt Commander-Entscheid (Karte)._") +zeilen.append("_Auto-Adoption (Etappe E5): Der Prüfstand tauscht Sieger-Modelle nun selbstständig aus._") text = "\n".join(z for z in zeilen if z is not None) open(ziel, "w", encoding="utf-8").write(text + "\n") print(text) @@ -402,6 +405,35 @@ PY briefkasten "Prüfstand" "Kandidat ${HF_ID} (Rolle ${ROLLE}) geprüft — Steckbrief im Auftrags-Kanban und Vault (pruefstand/${KEY}.md)." mv "$SPEC" "$STATE/done/${KEY}.spec.json" 2>/dev/null || true cp "$OUT" "$STATE/done/${KEY}.json" 2>/dev/null || echo "{\"status\":\"geprueft\"}" > "$STATE/done/${KEY}.json" + + # ---- Auto-Adoption (Etappe E5) + if [ -f "$SIEGER_FLAG" ]; then + echo "Kandidat hat gewonnen — starte Auto-Adoption für Rolle $ROLLE..." + local FINAL_DIR="/srv/models/${HF_ID//\//-}-GGUF" + sudo -n mkdir -p "$FINAL_DIR" + sudo -n mv "$ZIEL" "$FINAL_DIR/" + local FINAL_GGUF="$FINAL_DIR/$(basename "$PFAD")" + local FINAL_MMPROJ="" + if [ -n "$MM_ZIEL" ]; then + sudo -n mv "$MM_ZIEL" "$FINAL_DIR/" + FINAL_MMPROJ="$FINAL_DIR/$(basename "$MMPROJ")" + fi + sudo -n chown -R hitonabi:hitonabi "$FINAL_DIR" 2>/dev/null || true + + # Config updaten + local CONFIG="$MC/deploy/llama-swap.config.yaml" + python3 "$PSD/auto_adopt.py" "$CONFIG" "$ROLLE" "$HF_ID" "$FINAL_GGUF" "$FINAL_MMPROJ" + + # Reload llama-swap (via worker oder systemctl) + if systemctl --user is-active --quiet llama-swap; then + systemctl --user restart llama-swap + briefkasten "Prüfstand Auto-Adoption" "Rolle '$ROLLE' wurde erfolgreich durch '$HF_ID' ersetzt (Neustart erledigt)." + else + briefkasten "Prüfstand Auto-Adoption" "Rolle '$ROLLE' wurde durch '$HF_ID' ersetzt (llama-swap war inaktiv)." + fi + rm -f "$SIEGER_FLAG" + fi + rm -rf "${CACHE:?}/$KEY" echo echo "(Cache aufgeräumt; Roh-Ergebnisse: $STATE/ergebnisse/kandidat-${KEY}.*)"