121 lines
3.5 KiB
Python
121 lines
3.5 KiB
Python
"""Actual server stop/restart and role-routing persistence, isolated from user data."""
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import time
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = Path(__file__).resolve().parents[2]
|
|
out = args.output.resolve()
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
base = "http://127.0.0.1:18740"
|
|
log = (out / "restart-server.log").open("w")
|
|
owned = []
|
|
|
|
|
|
def request(method, path, body=None):
|
|
data = json.dumps(body).encode() if body is not None else None
|
|
req = urllib.request.Request(
|
|
base + path,
|
|
data=data,
|
|
headers={"Content-Type": "application/json"},
|
|
method=method,
|
|
)
|
|
with urllib.request.urlopen(req, timeout=5) as response:
|
|
return json.load(response)
|
|
|
|
|
|
def start(resume=False):
|
|
proc = subprocess.Popen(
|
|
[
|
|
str(root / "calliope-backend/.venv/bin/python"),
|
|
str(root / "scripts/hel840/serve_fixture.py"),
|
|
"--data",
|
|
str(out / "fixture"),
|
|
*(["--resume"] if resume else []),
|
|
],
|
|
cwd=root,
|
|
stdout=log,
|
|
stderr=subprocess.STDOUT,
|
|
)
|
|
owned.append(proc)
|
|
for _ in range(100):
|
|
if proc.poll() is not None:
|
|
raise RuntimeError("fixture backend exited")
|
|
try:
|
|
request("GET", "/api/health")
|
|
return proc
|
|
except OSError:
|
|
time.sleep(0.1)
|
|
raise RuntimeError("fixture readiness timeout")
|
|
|
|
|
|
def stop(proc):
|
|
if proc.poll() is not None:
|
|
return
|
|
status = json.loads(
|
|
subprocess.check_output(
|
|
["multica", "daemon", "status", "--output", "json"], text=True
|
|
)
|
|
)
|
|
|
|
def pids(obj):
|
|
if isinstance(obj, dict):
|
|
return [v for k, v in obj.items() if k in ("pid", "process_id")] + [
|
|
n for v in obj.values() for n in pids(v)
|
|
]
|
|
if isinstance(obj, list):
|
|
return [n for v in obj for n in pids(v)]
|
|
return []
|
|
|
|
assert proc.pid not in pids(status)
|
|
proc.terminate()
|
|
proc.wait(timeout=10)
|
|
|
|
|
|
try:
|
|
first = start()
|
|
request(
|
|
"POST", "/api/settings", {"agent_llm_assignments": {"translate": "fixture-b"}}
|
|
)
|
|
request("POST", "/api/settings", {"agent_llm_assignments": {"polish": "fixture-a"}})
|
|
saved = request("GET", "/api/settings")["agent_llm_assignments"]
|
|
assert saved["translate"] == "fixture-b" and saved["polish"] == "fixture-a"
|
|
stop(first)
|
|
second = start(True)
|
|
loaded = request("GET", "/api/settings")["agent_llm_assignments"]
|
|
assert loaded == saved
|
|
translation = request(
|
|
"POST", "/api/prompt-tools/transform", {"action": "translate", "text": "一只猫"}
|
|
)
|
|
polish = request(
|
|
"POST", "/api/prompt-tools/transform", {"action": "polish", "text": "一只猫"}
|
|
)
|
|
assert (
|
|
translation["model"]["id"] == "fixture-b"
|
|
and polish["model"]["id"] == "fixture-a"
|
|
)
|
|
result = {
|
|
"before_pid": first.pid,
|
|
"after_pid": second.pid,
|
|
"assignments": loaded,
|
|
"translation_model": translation["model"],
|
|
"polish_model": polish["model"],
|
|
"passed": True,
|
|
}
|
|
(out / "restart-check.json").write_text(
|
|
json.dumps(result, ensure_ascii=False, indent=2)
|
|
)
|
|
print(
|
|
json.dumps({"passed": True, "before_pid": first.pid, "after_pid": second.pid})
|
|
)
|
|
finally:
|
|
for proc in owned:
|
|
stop(proc)
|
|
log.close()
|