feat(ollama): add Ollama local LLM (.27) card & remote SSH control to service-controller
This commit is contained in:
@@ -84,21 +84,29 @@ def _container_state(target: str) -> str:
|
||||
return out or ("unknown" if r.returncode != 0 else "unknown")
|
||||
|
||||
|
||||
def _systemd_user_state(target: str) -> str:
|
||||
r = subprocess.run(["systemctl", "--user", "is-active", target],
|
||||
capture_output=True, text=True)
|
||||
return (r.stdout.strip() or "inactive").lower()
|
||||
def _systemd_user_state(target: str, remote: str | None = None) -> str:
|
||||
cmd = ["systemctl", "--user", "is-active", target]
|
||||
if remote:
|
||||
cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes", remote] + cmd
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
out = (r.stdout.strip() or "inactive").lower()
|
||||
if out == "active":
|
||||
return "running"
|
||||
return out
|
||||
|
||||
|
||||
def _do(kind: str, action: str, target: str) -> str:
|
||||
def _do(kind: str, action: str, target: str, remote: str | None = None) -> str:
|
||||
"""Run start/stop. Returns the state after a short settle."""
|
||||
if kind == "container":
|
||||
subprocess.run(["docker", action, target], capture_output=True, text=True)
|
||||
time.sleep(1.5)
|
||||
return _container_state(target)
|
||||
subprocess.run(["systemctl", "--user", action, target], capture_output=True, text=True)
|
||||
cmd = ["systemctl", "--user", action, target]
|
||||
if remote:
|
||||
cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes", remote] + cmd
|
||||
subprocess.run(cmd, capture_output=True, text=True)
|
||||
time.sleep(1.5)
|
||||
return _systemd_user_state(target)
|
||||
return _systemd_user_state(target, remote=remote)
|
||||
|
||||
|
||||
def _targets(s: dict[str, Any]) -> list[str]:
|
||||
@@ -114,12 +122,13 @@ def _probe(s: dict[str, Any]) -> tuple[str, list[str]]:
|
||||
"""Collect the state of every target. Returns (aggregate, per-target list).
|
||||
Aggregate: running if ANY is running; stopped if ALL stopped; else partial."""
|
||||
kind = s["kind"]
|
||||
remote = s.get("remote")
|
||||
states = []
|
||||
for tgt in _targets(s):
|
||||
if kind == "container":
|
||||
states.append(_container_state(tgt))
|
||||
else:
|
||||
states.append(_systemd_user_state(tgt))
|
||||
states.append(_systemd_user_state(tgt, remote=remote))
|
||||
n_ok = sum(1 for st in states if st in ("running", "active", "started"))
|
||||
n_none = sum(1 for st in states if st in ("unknown",)) or 0
|
||||
if n_ok == len(states):
|
||||
@@ -184,9 +193,10 @@ async def service_action(sid: str, action: str,
|
||||
if s is None:
|
||||
raise HTTPException(status_code=404, detail="unknown service")
|
||||
actor = (who or "unknown-user").strip()[:40]
|
||||
remote = s.get("remote")
|
||||
results = {}
|
||||
for tgt in _targets(s):
|
||||
results[tgt] = _do(s["kind"], action, tgt)
|
||||
results[tgt] = _do(s["kind"], action, tgt, remote=remote)
|
||||
agg, _ = _probe(s)
|
||||
audit(f"{actor} {action} {sid} -> {agg} ({len(_targets(s))} targets)")
|
||||
return {"ok": True, "id": sid, "state": agg, "targets": results}
|
||||
|
||||
@@ -94,3 +94,14 @@ label = "World Monitor"
|
||||
ram_mb = 120
|
||||
default_state = "running"
|
||||
who = ["sam"]
|
||||
|
||||
[[services]]
|
||||
id = "ollama"
|
||||
kind = "systemd-user"
|
||||
target = "ollama.service"
|
||||
remote = "sam@192.168.20.27"
|
||||
group = "ai"
|
||||
label = "Local LLM Server (Ollama .27)"
|
||||
ram_mb = 9000
|
||||
default_state = "stopped"
|
||||
who = ["sam", "jo", "harry", "finn"]
|
||||
|
||||
@@ -85,6 +85,9 @@ def _cat() -> list[Tool]:
|
||||
"Chat admin console (admins only)", "https://pb.chat.lab.audasmedia.com.au", login=True, admin=True),
|
||||
Tool("ai-router", "AI Router", "ai",
|
||||
"OmniRoute LLM gateway dashboard", "https://omniroute.lab.audasmedia.com.au"),
|
||||
Tool("ollama", "Ollama Local LLM", "ai",
|
||||
"Local AI model server (Qwen 2.5 Coder on .27)", "http://192.168.20.27:11434",
|
||||
service_id="ollama", ram_mb=9000, togglable=True, lan=True),
|
||||
Tool("n8n", "n8n", "ai",
|
||||
"Workflow automation", "https://n8n.lab.audasmedia.com.au", login=True),
|
||||
Tool("prefect", "Prefect", "ai",
|
||||
|
||||
Reference in New Issue
Block a user