Download fly_trader/ui/runtime.py from bryceweiner/fly-trader: direct link, hf CLI and curl.
- Browser
- Download file 3.67 kB
-
https://huggingface.co/bryceweiner/fly-trader/resolve/main/fly_trader/ui/runtime.py
- Command line
-
hf download hf://bryceweiner/fly-trader/fly_trader/ui/runtime.py
-
curl -L -o runtime.py https://huggingface.co/bryceweiner/fly-trader/resolve/main/fly_trader/ui/runtime.py
3.67 kB
| """Which supervisor the console talks to. `fly-trader ui` hosts the workers (the process-wide Supervisor); `fly-trader ui | |
| --observe` serves the same dashboard without hosting anything — for a preview next to a running console, where a second | |
| Supervisor would wait on, and fight over, the first one's `processes` rows. The observer reads worker state from those | |
| rows and refuses every control.""" | |
| from __future__ import annotations | |
| import collections | |
| from datetime import datetime, timezone | |
| from .. import config | |
| HOSTS_WORKERS = True | |
| class ObserverMode(RuntimeError): | |
| pass | |
| class Observer: | |
| """The Supervisor's read side, from the `processes` rows of whichever console hosts the workers.""" | |
| autostarted = True | |
| def __init__(self): | |
| import os | |
| self.pid = os.getpid() | |
| def _rows(self) -> dict[str, dict]: | |
| from ..db.queries import q | |
| import psutil | |
| rows = q("SELECT DISTINCT ON (name) name, pid, started_at, stopped_at, exit_code, cmd FROM processes " | |
| "WHERE cmd[1] = 'thread' ORDER BY name, started_at DESC") | |
| out = {} | |
| for r in rows: | |
| alive = r["stopped_at"] is None and psutil.pid_exists(int(r["pid"])) | |
| out[r["name"]] = {**r, "alive": alive} | |
| return out | |
| def status(self) -> dict[str, dict]: | |
| from ..ops.supervisor import WORKERS | |
| rows = self._rows() | |
| return {n: {"alive": bool(rows.get(n, {}).get("alive")), "started_at": rows.get(n, {}).get("started_at"), | |
| "stopped_at": rows.get(n, {}).get("stopped_at"), | |
| "error": (f"exit code {rows[n]['exit_code']}" if n in rows and rows[n].get("exit_code") not in (None, 0) else None), | |
| "stopping": False} for n in WORKERS} | |
| def alive(self, name: str) -> bool: | |
| return bool(self.status().get(name, {}).get("alive")) | |
| def external(self) -> dict[str, dict]: | |
| """CLI-started worker processes, read only (procs.alive would also reconcile, i.e. write, the rows).""" | |
| from ..db.queries import q | |
| from ..ops.procs import _is_ours | |
| out = {} | |
| for r in q("SELECT * FROM processes WHERE stopped_at IS NULL AND cmd[1] <> 'thread' ORDER BY started_at"): | |
| if _is_ours(r["pid"], r["name"]): | |
| out[r["name"]] = r | |
| return out | |
| def log_tail(self, name: str, n: int = 60) -> str: | |
| return file_tail(name, n) | |
| def _refuse(self, *_a, **_k): | |
| raise ObserverMode("this console only observes (fly-trader ui --observe): start and stop workers from the console that hosts them") | |
| start = request_stop = stop = _refuse | |
| _OBSERVER: Observer | None = None | |
| def sup(): | |
| """The Supervisor (hosting) or the Observer (preview).""" | |
| global _OBSERVER | |
| if HOSTS_WORKERS: | |
| from ..ops.supervisor import get_supervisor | |
| return get_supervisor() | |
| if _OBSERVER is None: | |
| _OBSERVER = Observer() | |
| return _OBSERVER | |
| def file_tail(name: str, n: int = 200, max_bytes: int = 1 << 20) -> str: | |
| """The last ``n`` lines of logs/<name>.log (the rotating file every worker writes), read from the end.""" | |
| p = config.LOG_DIR / f"{name}.log" | |
| try: | |
| with open(p, "rb") as f: | |
| f.seek(0, 2); size = f.tell(); f.seek(max(0, size - max_bytes)) | |
| data = f.read() | |
| except OSError: | |
| return "" | |
| lines = data.decode("utf-8", "replace").splitlines() | |
| if size > max_bytes and lines: | |
| lines = lines[1:] # the first line read mid-way is partial | |
| return "\n".join(collections.deque(lines, maxlen=n)) | |
| def now() -> datetime: | |
| return datetime.now(timezone.utc) | |