Files
mission-control-v2/backend/services/backup.py
T
Hitonabi c48e583790 Feat: Vulkan/RADV-Engine + vocab-gepruefte Spec-Drafts + Provisioning/Sync
Engine-Cutover ROCm/HIP -> Vulkan/RADV (gfx1151): +12-22% tg auf MoE (llama-bench
verifiziert, fast 53->65 t/s). ROCm-Build bleibt als Rollback unter /opt/llamacpp.

- Backend: vocab-aware Speculative Decoding. services/gguf_meta.py liest den
  Tokenizer-Fingerprint (model/pre/n_vocab) direkt aus dem GGUF-Header (ohne Modell-Load);
  register_model + migrate_config haengen nur VOCAB-KOMPATIBLE Drafts an (inkl. --spec-type,
  das in dieser llama.cpp-Generation noetig ist). Neue Endpoints /api/models/drafts + /{id}/draft.
- Frontend: idiotensichere Spec-Draft-UI (SpecDraftModal) - nur kompatible Drafts waehlbar,
  inkompatible gesperrt mit Begruendung; SPEC/SPEC?-Badge nach echtem Aktiv-Status; Rolle in AddModel.
- maintenance.py: Engine-Update-Quelle -> ggml-org/llama.cpp (Build-Nummer-Vergleich),
  ENGINE_PATH=/opt/llamacpp-vulkan.
- Startup-Warmup der brains (deploy/warmup.sh, self-detaching ExecStartPost) + deploy/provision-engine.sh.
- Cleanup: tote LiteLLM gateway/config.yaml + alle Referenzen (config.py/backup.py/backup.sh) entfernt;
  README + docs/memory aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:54:29 +02:00

52 lines
1.6 KiB
Python

"""
Backup der „Verfassung" (Shared-Memory-SQLite) + aller Configs.
Snapshot in einen Zeitstempel-Ordner; die letzten N bleiben erhalten.
"""
import shutil
import time
from pathlib import Path
from config import CONFIG_PATH, MEMORY_DB, MODELS_DIR
BACKUP_DIR = Path(MODELS_DIR) / "mc2-backups"
RETAIN = 7
def _safe_copy(src: Path, dst_dir: Path) -> str | None:
try:
if src and src.exists():
shutil.copy2(src, dst_dir / src.name)
return src.name
except Exception:
pass
return None
def backup_now() -> dict:
"""Erstellt einen Snapshot (Memory-DB inkl. WAL/SHM + Configs). Alte Snapshots
über RETAIN hinaus werden entfernt."""
ts = time.strftime("%Y%m%d-%H%M%S")
dst = BACKUP_DIR / ts
dst.mkdir(parents=True, exist_ok=True)
saved = []
for src in (MEMORY_DB, Path(str(MEMORY_DB) + "-wal"), Path(str(MEMORY_DB) + "-shm"),
CONFIG_PATH):
if (name := _safe_copy(src, dst)):
saved.append(name)
# Aufräumen: nur die letzten RETAIN Snapshots behalten.
snaps = sorted([p for p in BACKUP_DIR.iterdir() if p.is_dir()], reverse=True)
for old in snaps[RETAIN:]:
shutil.rmtree(old, ignore_errors=True)
return {"ok": bool(saved), "snapshot": ts, "files": saved, "dir": str(dst)}
def list_backups() -> list[dict]:
if not BACKUP_DIR.exists():
return []
out = []
for p in sorted([d for d in BACKUP_DIR.iterdir() if d.is_dir()], reverse=True):
files = [f.name for f in p.iterdir() if f.is_file()]
out.append({"snapshot": p.name, "files": files})
return out