Point gpu-rent logs at gpu-rent-ollama and harden Assistent bin probes.

Stock ollama.service is disabled by install; journal aliases must use
gpu-rent-ollama. Prefer bin/{Debug,Release}/net* when locating the
extension DLL and Sqlite private deps.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 06:46:57 +03:00
co-authored by Cursor
parent ca11467a0d
commit 23b07672d6
4 changed files with 46 additions and 20 deletions
+13 -2
View File
@@ -30,9 +30,17 @@ for root in roots:
for p in sorted(root.iterdir()): for p in sorted(root.iterdir()):
if "assistent" not in p.name.lower(): if "assistent" not in p.name.lower():
continue continue
# Prefer TFM output dirs (Debug/Release net*), then any bin/** fallback
dlls = []
for cfg_name in ("Debug", "Release"):
dlls.extend(sorted(p.glob(f"bin/{cfg_name}/net*/SwarmAssistentExtension.dll")))
if not dlls:
dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll")) dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll"))
if not dlls: if not dlls:
# Older layout / alternate assembly name # Older layout / alternate assembly name
for cfg_name in ("Debug", "Release"):
dlls.extend(sorted(p.glob(f"bin/{cfg_name}/net*/*Assistent*.dll")))
if not dlls:
dlls = sorted(p.glob("bin/**/*Assistent*.dll")) dlls = sorted(p.glob("bin/**/*Assistent*.dll"))
dll = dlls[0] if dlls else None dll = dlls[0] if dlls else None
dll_dir = dll.parent if dll else None dll_dir = dll.parent if dll else None
@@ -335,9 +343,12 @@ def collect_assistent_extension(cfg: Config, *, fs: dict[str, Any] | None = None
hints.append(f"{e.get('name')}: нет DLL — compile fail / Swarm не билдил") hints.append(f"{e.get('name')}: нет DLL — compile fail / Swarm не билдил")
ok = False ok = False
elif not e.get("sqlite_dll"): elif not e.get("sqlite_dll"):
where = e.get("dll_dir") or "bin/{Debug,Release}/net*"
hints.append( hints.append(
f"{e.get('name')}: DLL есть, но нет Microsoft.Data.Sqlite.dll — " f"{e.get('name')}: DLL есть ({where}), но рядом нет "
"чат/memory API могут падать (нужен ≥0.13.1 + seed-extensions)" "Microsoft.Data.Sqlite.dll (+ SQLitePCLRaw*) — чат/memory API упадут. "
"Нужен swarm-assistent ≥0.13.1 (CopyLocalLockFileAssemblies) + "
"gpu-rent seed-extensions + restart SwarmUI"
) )
ok = False ok = False
if not e.get("tab_html") or not e.get("bundle_js"): if not e.get("tab_html") or not e.get("bundle_js"):
+7 -1
View File
@@ -237,6 +237,10 @@ for root in roots:
for p in sorted(root.iterdir()): for p in sorted(root.iterdir()):
if "assistent" not in p.name.lower(): if "assistent" not in p.name.lower():
continue continue
dlls = []
for cfg_name in ("Debug", "Release"):
dlls.extend(sorted(p.glob(f"bin/{cfg_name}/net*/SwarmAssistentExtension.dll")))
if not dlls:
dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll")) dlls = sorted(p.glob("bin/**/SwarmAssistentExtension.dll"))
dll = dlls[0] if dlls else None dll = dlls[0] if dlls else None
dll_dir = dll.parent if dll else None dll_dir = dll.parent if dll else None
@@ -246,6 +250,7 @@ for root in roots:
"path": str(p), "path": str(p),
"name": p.name, "name": p.name,
"dll": str(dll) if dll else None, "dll": str(dll) if dll else None,
"dll_dir": str(dll_dir) if dll_dir else None,
"sqlite_dll": bool(sqlite and sqlite.is_file()), "sqlite_dll": bool(sqlite and sqlite.is_file()),
"sqlitepcl": bool(pcl), "sqlitepcl": bool(pcl),
}) })
@@ -330,8 +335,9 @@ def verify_assistent_sqlite_bins(cfg: Config, host: str, log: Log) -> bool:
log(f"Assistent {name}: Microsoft.Data.Sqlite есть (SQLitePCLRaw не найден)") log(f"Assistent {name}: Microsoft.Data.Sqlite есть (SQLitePCLRaw не найден)")
ok_any = True ok_any = True
else: else:
where = row.get("dll_dir") or "bin/{Debug,Release}/net*"
log( log(
f"⚠ Assistent {name}: DLL есть, но нет Microsoft.Data.Sqlite.dll — " f"⚠ Assistent {name}: DLL в {where}, но нет Microsoft.Data.Sqlite.dll — "
"чат/memory API упадут. Нужен ≥0.13.1 + seed-extensions + restart" "чат/memory API упадут. Нужен ≥0.13.1 + seed-extensions + restart"
) )
return ok_any return ok_any
+12 -9
View File
@@ -13,20 +13,23 @@ Log = Callable[[str], None]
DIGEST_LINES = 20 DIGEST_LINES = 20
FULL_LINES_DEFAULT = 80 FULL_LINES_DEFAULT = 80
# Stock `ollama.service` is disabled by install_ollama.sh; we run gpu-rent-ollama.
OLLAMA_UNIT = "gpu-rent-ollama"
KILLER_UNIT = "gpu-rent-idle-killer"
UNIT_ALIASES: dict[str, str] = { UNIT_ALIASES: dict[str, str] = {
"all": "all", "all": "all",
"swarm": "swarmui", "swarm": "swarmui",
"swarmui": "swarmui", "swarmui": "swarmui",
"ollama": "ollama", "ollama": OLLAMA_UNIT,
"killer": "gpu-rent-idle-killer", "gpu-rent-ollama": OLLAMA_UNIT,
"idle-killer": "gpu-rent-idle-killer", "killer": KILLER_UNIT,
"idle": "gpu-rent-idle-killer", "idle-killer": KILLER_UNIT,
"idle": KILLER_UNIT,
"cloud-init": "cloud-init", "cloud-init": "cloud-init",
"cloud": "cloud-init", "cloud": "cloud-init",
} }
KILLER_UNIT = "gpu-rent-idle-killer"
def units_for(cfg: Config) -> list[str]: def units_for(cfg: Config) -> list[str]:
"""Journal units enabled for this stack (no cloud-init).""" """Journal units enabled for this stack (no cloud-init)."""
@@ -34,7 +37,7 @@ def units_for(cfg: Config) -> list[str]:
if bool(getattr(cfg, "enable_swarmui", True)): if bool(getattr(cfg, "enable_swarmui", True)):
units.append("swarmui") units.append("swarmui")
if normalize_runtime(getattr(cfg, "llm_runtime", "none")) == "ollama": if normalize_runtime(getattr(cfg, "llm_runtime", "none")) == "ollama":
units.append("ollama") units.append(OLLAMA_UNIT)
units.append(KILLER_UNIT) units.append(KILLER_UNIT)
return units return units
@@ -81,7 +84,7 @@ def journal_units_for_target(target: str, *, cfg: Config | None = None) -> list[
if target == "all": if target == "all":
if cfg is not None: if cfg is not None:
return units_for(cfg) return units_for(cfg)
return ["swarmui", "ollama", KILLER_UNIT] return ["swarmui", OLLAMA_UNIT, KILLER_UNIT]
return [target] return [target]
@@ -115,7 +118,7 @@ def fetch_logs_for_cli(
target = resolve_unit_alias(unit) target = resolve_unit_alias(unit)
# Full dump keeps historical all=swarm+ollama+killer regardless of cfg. # Full dump keeps historical all=swarm+ollama+killer regardless of cfg.
if target == "all": if target == "all":
journal_units = ["swarmui", "ollama", KILLER_UNIT] journal_units = ["swarmui", OLLAMA_UNIT, KILLER_UNIT]
else: else:
journal_units = journal_units_for_target(target) journal_units = journal_units_for_target(target)
cmd = build_logs_remote_cmd( cmd = build_logs_remote_cmd(
+11 -5
View File
@@ -7,6 +7,7 @@ import pytest
from gpu_rent.vm_logs import ( from gpu_rent.vm_logs import (
DIGEST_LINES, DIGEST_LINES,
KILLER_UNIT, KILLER_UNIT,
OLLAMA_UNIT,
build_logs_remote_cmd, build_logs_remote_cmd,
fetch_unit_logs, fetch_unit_logs,
resolve_unit_alias, resolve_unit_alias,
@@ -23,7 +24,7 @@ class _Cfg:
def test_units_for_swarm_and_ollama(): def test_units_for_swarm_and_ollama():
assert units_for(_Cfg(enable_swarmui=True, llm_runtime="ollama")) == [ assert units_for(_Cfg(enable_swarmui=True, llm_runtime="ollama")) == [
"swarmui", "swarmui",
"ollama", OLLAMA_UNIT,
KILLER_UNIT, KILLER_UNIT,
] ]
@@ -37,7 +38,7 @@ def test_units_for_swarm_only():
def test_units_for_llm_only(): def test_units_for_llm_only():
assert units_for(_Cfg(enable_swarmui=False, llm_runtime="ollama")) == [ assert units_for(_Cfg(enable_swarmui=False, llm_runtime="ollama")) == [
"ollama", OLLAMA_UNIT,
KILLER_UNIT, KILLER_UNIT,
] ]
@@ -51,7 +52,9 @@ def test_digest_cmd_has_journal_no_cloud_init():
) )
assert "cloud-init" not in cmd assert "cloud-init" not in cmd
assert "journalctl -u swarmui" in cmd assert "journalctl -u swarmui" in cmd
assert "journalctl -u ollama" in cmd assert f"journalctl -u {OLLAMA_UNIT}" in cmd
# Stock ollama.service is disabled — digest must not query bare unit name
assert "journalctl -u ollama -n" not in cmd
assert f"journalctl -u {KILLER_UNIT}" in cmd assert f"journalctl -u {KILLER_UNIT}" in cmd
assert f"-n {DIGEST_LINES}" in cmd assert f"-n {DIGEST_LINES}" in cmd
assert "systemctl is-active swarmui" in cmd assert "systemctl is-active swarmui" in cmd
@@ -61,15 +64,18 @@ def test_full_logs_cmd_includes_cloud_init():
cmd = build_logs_remote_cmd( cmd = build_logs_remote_cmd(
lines=80, lines=80,
include_cloud_init=True, include_cloud_init=True,
journal_units=["swarmui", "ollama", KILLER_UNIT], journal_units=["swarmui", OLLAMA_UNIT, KILLER_UNIT],
) )
assert "cloud-init-output.log" in cmd assert "cloud-init-output.log" in cmd
assert "journalctl -u swarmui" in cmd assert "journalctl -u swarmui" in cmd
assert f"journalctl -u {OLLAMA_UNIT}" in cmd
def test_resolve_unit_alias(): def test_resolve_unit_alias():
assert resolve_unit_alias(None) == "all" assert resolve_unit_alias(None) == "all"
assert resolve_unit_alias("swarm") == "swarmui" assert resolve_unit_alias("swarm") == "swarmui"
assert resolve_unit_alias("ollama") == OLLAMA_UNIT
assert resolve_unit_alias("gpu-rent-ollama") == OLLAMA_UNIT
assert resolve_unit_alias("killer") == KILLER_UNIT assert resolve_unit_alias("killer") == KILLER_UNIT
with pytest.raises(ValueError, match="неизвестный"): with pytest.raises(ValueError, match="неизвестный"):
resolve_unit_alias("bogus") resolve_unit_alias("bogus")
@@ -90,4 +96,4 @@ def test_fetch_unit_logs_uses_ssh(monkeypatch):
assert "cloud-init" not in calls[0][1] assert "cloud-init" not in calls[0][1]
assert "journalctl -u swarmui" in calls[0][1] assert "journalctl -u swarmui" in calls[0][1]
assert f"journalctl -u {KILLER_UNIT}" in calls[0][1] assert f"journalctl -u {KILLER_UNIT}" in calls[0][1]
assert "journalctl -u ollama" not in calls[0][1] assert f"journalctl -u {OLLAMA_UNIT}" not in calls[0][1]