Enhance CLI options and idle-killer functionality
- Added a `--update` option to the `up` command in the CLI for forced updates during warm ACTIVE states. - Improved the idle-killer logic with new functions to check for reusable credentials and refresh settings on existing remote credentials. - Enhanced the `seed_civitai` function to implement caching for model jobs, optimizing the provisioning process. - Updated the installation script for Ollama to prevent unnecessary restarts when the service configuration has not changed. - Added diagnostics for model job caching and improved error handling in various functions.
This commit is contained in:
+108
-7
@@ -38,6 +38,18 @@ DATA = "/mnt/swarm_data"
|
||||
_OLLAMA_INSTALL_ENV = ("OLLAMA_VERSION", "OLLAMA_SHA256")
|
||||
|
||||
|
||||
def _models_manifest_fp(cfg: Config) -> str:
|
||||
"""Stable fingerprint of models.yaml so warm seed can skip API+fetch."""
|
||||
import hashlib
|
||||
|
||||
path = Path(cfg.models_manifest)
|
||||
try:
|
||||
raw = path.read_bytes()
|
||||
except OSError:
|
||||
raw = b""
|
||||
return hashlib.sha256(raw).hexdigest()[:16]
|
||||
|
||||
|
||||
def _remote_llm_env(cfg: Config, *keys: str) -> dict[str, str]:
|
||||
import os
|
||||
|
||||
@@ -408,6 +420,41 @@ def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
log("model-seed пропущен: манифест пуст — дефолт SwarmUI")
|
||||
return
|
||||
|
||||
# Warm short-circuit: last successful jobs list still present on disk.
|
||||
cache_remote = f"{DATA}/.gpu-rent-models-jobs.json"
|
||||
try:
|
||||
cached_raw = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
f"test -f {cache_remote} && cat {cache_remote} || true",
|
||||
check=False,
|
||||
timeout=20,
|
||||
).strip()
|
||||
except Exception:
|
||||
cached_raw = ""
|
||||
if cached_raw.startswith("{"):
|
||||
try:
|
||||
cached = json.loads(cached_raw)
|
||||
dests = [str(d) for d in (cached.get("dests") or []) if d]
|
||||
fp = str(cached.get("fp") or "")
|
||||
want_fp = _models_manifest_fp(cfg)
|
||||
if dests and fp == want_fp:
|
||||
check = " && ".join(f"test -f {shlex.quote(d)}" for d in dests)
|
||||
ok = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
f"if {check}; then echo ALL_OK; else echo MISSING; fi",
|
||||
check=False,
|
||||
timeout=30,
|
||||
).strip()
|
||||
if "ALL_OK" in ok:
|
||||
log(
|
||||
f"model-seed: skip — {len(dests)} файл(ов) уже на диске (cache)"
|
||||
)
|
||||
return
|
||||
except (json.JSONDecodeError, TypeError, CloudError):
|
||||
pass
|
||||
|
||||
jobs: list[dict] = []
|
||||
for entry in entries:
|
||||
url = (entry.url or "").strip()
|
||||
@@ -514,6 +561,31 @@ def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
|
||||
if not any(e.kind == "checkpoint" for e in entries):
|
||||
log("в манифесте нет checkpoint — генерация может не стартовать")
|
||||
|
||||
dests = [str(j.get("dest") or "") for j in jobs if j.get("dest")]
|
||||
if dests:
|
||||
check = " && ".join(f"test -f {shlex.quote(d)}" for d in dests)
|
||||
present = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
f"if {check}; then echo ALL_OK; else echo MISSING; fi",
|
||||
check=False,
|
||||
timeout=30,
|
||||
).strip()
|
||||
if "ALL_OK" in present:
|
||||
put_text(
|
||||
cfg,
|
||||
host,
|
||||
cache_remote,
|
||||
json.dumps({"fp": _models_manifest_fp(cfg), "dests": dests}, indent=2)
|
||||
+ "\n",
|
||||
)
|
||||
log(
|
||||
f"model-seed: skip fetch — {len(dests)} файл(ов) уже на диске "
|
||||
f"(civitai={civ_n}, huggingface={hf_n})"
|
||||
)
|
||||
return
|
||||
|
||||
put_text(cfg, host, "/tmp/gpu-rent-civitai-jobs.json", json.dumps(jobs, indent=2))
|
||||
if cfg.civitai_api_token:
|
||||
put_text(
|
||||
@@ -533,6 +605,12 @@ def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
timeout=7200,
|
||||
log=log,
|
||||
)
|
||||
put_text(
|
||||
cfg,
|
||||
host,
|
||||
cache_remote,
|
||||
json.dumps({"fp": _models_manifest_fp(cfg), "dests": dests}, indent=2) + "\n",
|
||||
)
|
||||
|
||||
|
||||
def ensure_swarmui_running(cfg: Config, host: str, log: Log, restart: bool) -> None:
|
||||
@@ -594,16 +672,39 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
if not names:
|
||||
log("ollama-models.yaml пуст — pull skip")
|
||||
else:
|
||||
put_text(cfg, host, "/tmp/gpu-rent-ollama-models.json", json.dumps(names, indent=2))
|
||||
log(f"Ollama: pull {len(names)} из манифеста")
|
||||
run_python(
|
||||
# Fast path: all tags already present — skip upload/pull script.
|
||||
listed = run_ssh(
|
||||
cfg,
|
||||
host,
|
||||
_pkg_text("ollama_pull.py"),
|
||||
remote_path="/tmp/gpu-rent-ollama_pull.py",
|
||||
timeout=7200,
|
||||
log=log,
|
||||
"ollama list 2>/dev/null | awk 'NR>1 {print $1}' || true",
|
||||
check=False,
|
||||
timeout=30,
|
||||
)
|
||||
have = {ln.strip() for ln in listed.splitlines() if ln.strip()}
|
||||
missing = []
|
||||
for name in names:
|
||||
if name in have or (
|
||||
":" not in name and f"{name}:latest" in have
|
||||
) or (
|
||||
name.endswith(":latest") and name.rsplit(":", 1)[0] in have
|
||||
):
|
||||
continue
|
||||
missing.append(name)
|
||||
if not missing:
|
||||
log(f"ollama pull: skip — все {len(names)} уже есть")
|
||||
else:
|
||||
put_text(
|
||||
cfg, host, "/tmp/gpu-rent-ollama-models.json", json.dumps(missing, indent=2)
|
||||
)
|
||||
log(f"Ollama: pull {len(missing)} из манифеста (нет: {len(missing)})")
|
||||
run_python(
|
||||
cfg,
|
||||
host,
|
||||
_pkg_text("ollama_pull.py"),
|
||||
remote_path="/tmp/gpu-rent-ollama_pull.py",
|
||||
timeout=7200,
|
||||
log=log,
|
||||
)
|
||||
else:
|
||||
raise CloudError(f"неизвестный LLM_RUNTIME={runtime!r}")
|
||||
st = load_state()
|
||||
|
||||
Reference in New Issue
Block a user