feat: run dream cycle as a systemd user service + journald-visible logs
- deploy/lyra-dream.service: --loop 1800 user service on lyra-cortex, so Lyra's consolidation + reflection keeps ticking unattended between conversations - deploy/README.md: install / linger / operate runbook - logbus: mirror events to stderr so out-of-band runs (the dream service under journald) are observable, not just via the in-process web SSE feed Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
# Deploy
|
||||||
|
|
||||||
|
## Dream cycle (`lyra-dream.service`)
|
||||||
|
|
||||||
|
Lyra's unattended inner loop. Runs `lyra-dream --loop 1800` so she consolidates
|
||||||
|
memory and reflects every 30 min between conversations. Installed as a
|
||||||
|
**systemd user service** on `lyra-cortex` (10.0.0.41), running as `serversdown`
|
||||||
|
— no root needed to manage it.
|
||||||
|
|
||||||
|
### Install / update
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp deploy/lyra-dream.service ~/.config/systemd/user/lyra-dream.service
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now lyra-dream.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Persist across reboot / logout (one-time, needs sudo)
|
||||||
|
|
||||||
|
A user service stops when the user logs out and doesn't start at boot until
|
||||||
|
login — unless lingering is enabled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo loginctl enable-linger serversdown
|
||||||
|
```
|
||||||
|
|
||||||
|
### Operate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status lyra-dream.service # is she ticking?
|
||||||
|
journalctl --user -u lyra-dream.service -f # watch her think (logbus -> stderr)
|
||||||
|
systemctl --user restart lyra-dream.service # after a code change
|
||||||
|
systemctl --user stop lyra-dream.service # quiet her down
|
||||||
|
```
|
||||||
|
|
||||||
|
Tunables live in `lyra/dream.py` (drive thresholds, curiosity gains) and the
|
||||||
|
`--loop` interval in the unit's `ExecStart`. The consolidation backend follows
|
||||||
|
`SUMMARY_BACKEND` in `.env` (cloud gpt-4o-mini for bulk; the MI50 is too slow
|
||||||
|
for the summarization backfill).
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Lyra dream cycle — unattended consolidation + reflection loop
|
||||||
|
Documentation=https://github.com/serversdown/project-lyra
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/home/serversdown/project-lyra
|
||||||
|
# Clear any stray VIRTUAL_ENV so uv resolves the project's own .venv.
|
||||||
|
UnsetEnvironment=VIRTUAL_ENV
|
||||||
|
ExecStart=/home/serversdown/.local/bin/uv run lyra-dream --loop 1800
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -6,6 +6,7 @@ ephemeral — it's an activity feed, not durable logging.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
@@ -23,6 +24,10 @@ def log(level: str, msg: str, **fields) -> None:
|
|||||||
_EVENTS.append(
|
_EVENTS.append(
|
||||||
{"seq": _SEQ, "ts": time.time(), "level": level, "msg": msg, "fields": fields}
|
{"seq": _SEQ, "ts": time.time(), "level": level, "msg": msg, "fields": fields}
|
||||||
)
|
)
|
||||||
|
# Mirror to stderr so out-of-band runs (e.g. the dream service under
|
||||||
|
# systemd/journald) are observable, not just via the in-process SSE feed.
|
||||||
|
extra = " ".join(f"{k}={v}" for k, v in fields.items())
|
||||||
|
print(f"[{level}] {msg}{(' ' + extra) if extra else ''}", file=sys.stderr, flush=True)
|
||||||
|
|
||||||
|
|
||||||
def since(seq: int) -> list[dict]:
|
def since(seq: int) -> list[dict]:
|
||||||
|
|||||||
Reference in New Issue
Block a user