feat(pruefstand): Etappe E5 Autonomie für Auto-Adoption und täglichen Rhythmus
Ampel / ampel (push) Failing after 22s

This commit is contained in:
Hitonabi
2026-07-26 21:28:50 +02:00
parent f524ef4375
commit 87f446c5ae
2 changed files with 85 additions and 8 deletions
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
import sys, re, shutil, time
if len(sys.argv) < 5:
print("Nutzung: auto_adopt.py <config_path> <rolle> <new_hf_id> <new_gguf_path> [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)
+40 -8
View File
@@ -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}.*)"