- llamaswap.sammeln(): Aenderungen lesen dieselbe Config aus dem Speicher, geschrieben wird einmal am Ende, bei einem Fehler gar nicht - Radar-Tausch und Hirn-Umstellung nutzen es; Hermes wird erst nach dem Schreiben umgestellt - Test-Attrappe fuer update_brain_model: der Radar-Test faesst auf der Box nie die echte Hermes-Config an - Doku: Ziel-Modell, strukturierter Verlauf, Sammel-Schreiben Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""Sammel-Schreiben der llama-swap-Config (24.09.2026): Ein Modelltausch schrieb die Config bis zu
|
|
siebenmal, und jedes Mal entlud llama-swap alle Modelle. In llamaswap.sammeln() gibt es genau einen
|
|
Schreibvorgang — oder keinen, wenn etwas scheitert."""
|
|
|
|
import threading
|
|
|
|
import pytest
|
|
from services import llamaswap
|
|
|
|
CONFIG = """\
|
|
models:
|
|
alt:
|
|
cmd: llama-server -m /srv/models/alt.gguf
|
|
aliases:
|
|
- hermes
|
|
- fast
|
|
ttl: 0
|
|
neu:
|
|
cmd: llama-server -m /srv/models/neu.gguf
|
|
ttl: 300
|
|
groups:
|
|
brains:
|
|
swap: false
|
|
persistent: true
|
|
members:
|
|
- alt
|
|
"""
|
|
|
|
|
|
@pytest.fixture
|
|
def config(tmp_path, monkeypatch):
|
|
datei = tmp_path / "config.yaml"
|
|
datei.write_text(CONFIG, encoding="utf-8")
|
|
monkeypatch.setattr(llamaswap, "CONFIG_PATH", datei)
|
|
schreibvorgaenge: list[int] = []
|
|
echt = llamaswap._schreiben
|
|
|
|
def zaehlen(cfg):
|
|
schreibvorgaenge.append(1)
|
|
echt(cfg)
|
|
|
|
monkeypatch.setattr(llamaswap, "_schreiben", zaehlen)
|
|
return datei, schreibvorgaenge
|
|
|
|
|
|
def test_ohne_sammeln_schreibt_jede_aenderung(config):
|
|
_, schreibvorgaenge = config
|
|
llamaswap.set_role("neu", "coder")
|
|
llamaswap.set_ttl("neu", 600)
|
|
assert len(schreibvorgaenge) == 2
|
|
|
|
|
|
def test_sammeln_schreibt_einmal_mit_allen_aenderungen(config):
|
|
datei, schreibvorgaenge = config
|
|
with llamaswap.sammeln():
|
|
llamaswap.set_role("neu", "hermes")
|
|
llamaswap.add_role("neu", "fast")
|
|
llamaswap.set_group("brains", ["neu"], swap=False, persist=True)
|
|
llamaswap.set_ttl("neu", 0)
|
|
llamaswap.set_ttl("alt", 300)
|
|
# Innerhalb des Sammelns sieht jede Abfrage den gesammelten Stand …
|
|
assert "hermes" in next(m for m in llamaswap.list_models() if m["name"] == "neu")["aliases"]
|
|
# … die Datei aber noch den alten.
|
|
assert "- hermes" in datei.read_text(encoding="utf-8").split("neu:")[0]
|
|
assert schreibvorgaenge == []
|
|
assert len(schreibvorgaenge) == 1
|
|
modelle = {m["name"]: m for m in llamaswap.list_models()}
|
|
assert {"hermes", "fast"} <= set(modelle["neu"]["aliases"]) and modelle["neu"]["ttl"] == 0
|
|
assert "hermes" not in modelle["alt"]["aliases"] and modelle["alt"]["ttl"] == 300
|
|
assert llamaswap.list_groups()["brains"]["members"] == ["neu"]
|
|
|
|
|
|
def test_sammeln_schreibt_nichts_wenn_etwas_scheitert(config):
|
|
datei, schreibvorgaenge = config
|
|
vorher = datei.read_text(encoding="utf-8")
|
|
with pytest.raises(RuntimeError), llamaswap.sammeln():
|
|
llamaswap.set_role("neu", "hermes")
|
|
raise RuntimeError("Hirn-Wechsel fehlgeschlagen")
|
|
assert schreibvorgaenge == [] and datei.read_text(encoding="utf-8") == vorher
|
|
|
|
|
|
def test_verschachtelt_schreibt_der_aeussere(config):
|
|
_, schreibvorgaenge = config
|
|
with llamaswap.sammeln():
|
|
llamaswap.set_ttl("neu", 1)
|
|
with llamaswap.sammeln():
|
|
llamaswap.set_ttl("neu", 2)
|
|
assert schreibvorgaenge == []
|
|
assert len(schreibvorgaenge) == 1
|
|
assert next(m for m in llamaswap.list_models() if m["name"] == "neu")["ttl"] == 2
|
|
|
|
|
|
def test_andere_threads_sehen_den_gesammelten_stand_nicht(config):
|
|
gesehen: list[list[str]] = []
|
|
with llamaswap.sammeln():
|
|
llamaswap.set_role("neu", "coder")
|
|
t = threading.Thread(target=lambda: gesehen.append(
|
|
next(m for m in llamaswap._lesen()["models"] if m == "neu") and
|
|
list(llamaswap._lesen()["models"]["neu"].get("aliases") or [])))
|
|
t.start()
|
|
t.join()
|
|
assert gesehen == [[]]
|
|
|
|
|
|
def test_hirn_umstellung_schreibt_einmal(config, monkeypatch):
|
|
from services import agent
|
|
|
|
_, schreibvorgaenge = config
|
|
monkeypatch.setattr(agent, "hermes_brain_info", dict)
|
|
umgestellt: list[str] = []
|
|
monkeypatch.setattr(agent, "update_brain_model", lambda alias: umgestellt.append(alias) or True)
|
|
antwort = agent.set_agent_brain("neu", hermes_umstellen=False)
|
|
assert antwort["ok"] and antwort["old"] == "alt"
|
|
assert len(schreibvorgaenge) == 1 and umgestellt == []
|
|
modelle = {m["name"]: m for m in llamaswap.list_models()}
|
|
assert "hermes" in modelle["neu"]["aliases"] and modelle["neu"]["ttl"] == 0
|
|
assert llamaswap.list_groups()["brains"]["members"] == ["neu"]
|
|
agent.set_agent_brain("neu") # schon das Hirn: härtet nur nach, Hermes wird nicht angefasst
|
|
assert umgestellt == []
|