# AGENTS.md — llm_monitor Read-only real-time dashboard for **two LLM engines side by side**: a gufo server (llama.cpp-family, `llamacpp:*` metrics + `/slots`) and a vLLM server (`vllm:*` metrics). Either engine is optional — it appears only if its `*_BASE_URL` env var is set, so a vLLM-only user gets a one-panel dashboard with no dead gufo half. FastAPI backend, one HTML page, vanilla JS, inline-SVG sparklines. No build step. ## Commands ```bash uv sync # install (incl. dev group) -> .venv uv run uvicorn app:app --host 127.0.0.1 --port 8090 # run — ALWAYS pass --port 8090 uv run python app.py # same, via DASHBOARD_HOST/DASHBOARD_PORT uv run pytest # 91 tests, no network, no engines needed uv run pytest --cov # coverage gate: app.py should stay >=95% uv run -p 3.10 pytest # requires-python is >=3.10; keep the floor uv lock # after editing pyproject.toml deps uv export --no-dev --no-hashes > requirements.txt # regenerate the pip shim ``` `uv run` auto-syncs, so you rarely need a bare `uv sync`. Use `uv run --no-dev sync` for a production install. Never `pip install` into the system interpreter here. ## Non-negotiables 1. **Read-only. GET only.** The backend must only ever call `client.get(...)`. Do not add a POST/PUT/PATCH/DELETE route, do not add "clear cache"/"unload model" buttons, do not POST a test prompt to the engines. Tests enforce 405 on every non-GET verb for every route. 2. **Engines are opt-in, and ports come from the deployment repo, not from memory.** An engine exists **only** when its `GUFO_BASE_URL` / `VLLM_BASE_URL` is set: there is no in-code default host on purpose, because a built-in `127.0.0.1:8731` gives a vLLM-only user a permanently red panel for a server they do not run. Unset → `/api/gufo/*` answers `404 {"configured": false}` **without dialling**, and the page drops that section (panel, strip card, polling, fleet share); neither set → the setup card. The enabled state is derived from the base URL (`bool(GUFO_BASE_URL)`) and published as `gufo_enabled` / `vllm_enabled` — never add a separate `*_ENABLED` flag that could disagree with it. Source of truth for real endpoints: `~/Deployments/reeseapps/ai/deployments/*/quadlets/*.container` — today gufo is `8731` (`deskwork-ai`, `deskwork.reeselink.com`), vLLM radiance is `8000` (`driveripper-ai`, `driveripper.reeselink.com`). Re-check those files before documenting a port, and keep examples tunnel-friendly (`127.0.0.1`). Never make the dashboard bind `:8000` — that is an engine's port, and the dashboard would either fail to start or scrape itself. 3. **No CDN, no third-party assets, no bundler.** These engines sit on an internet-disconnected podman network; a CDN `