Add Homepage dashboard + dual-export OpenCode traces
Homepage as the front door: single page at framework:7575 with one tile per service, live widgets where the upstream supports it (Ollama loaded models, container state via docker.sock, etc.), bookmarks for reference docs. Config files are pyinfra-managed — source of truth lives in compose/homepage/, sync by editing there and re-running ./run.sh. OpenCode plugin now dual-exports spans to Phoenix and OpenLIT in parallel. Phoenix remains the per-trace waterfall view; OpenLIT picks up the same data for fleet-level metrics. Each destination has its own batch processor so a hiccup at one doesn't block the other. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,14 +42,14 @@ Or run it ephemerally without installing: `uvx pyinfra inventory.py deploy.py`.
|
||||
Requires a reboot to activate — pyinfra rewrites `/etc/default/grub`
|
||||
and runs `update-grub`, but won't reboot for you.
|
||||
- `/models/<vendor>/` layout
|
||||
- `/srv/docker/{llama,vllm,ollama,openwebui,beszel,openlit,phoenix,openhands}/docker-compose.yml`
|
||||
- `/srv/docker/{llama,vllm,ollama,openwebui,beszel,openlit,phoenix,openhands,homepage}/docker-compose.yml`
|
||||
dropped in, not auto-started — you edit the model path then
|
||||
`docker compose up -d`
|
||||
(OpenWebUI needs no edits — it's pre-configured to find Ollama at
|
||||
`host.docker.internal:11434` and uses `searxng.n0n.io` for web search)
|
||||
(Beszel hub at :8090, OpenLIT UI at :3001, Phoenix UI at :6006,
|
||||
OpenHands UI at :3030 — see "Monitoring stack" and "Agent harnesses"
|
||||
below)
|
||||
OpenHands UI at :3030, Homepage at :7575 — see "Monitoring stack",
|
||||
"Agent harnesses", and "Front door" below)
|
||||
|
||||
If a previous run installed the native llama.cpp build / full ROCm /
|
||||
native Ollama, those are auto-cleaned the next time `./run.sh` runs.
|
||||
@@ -80,8 +80,27 @@ Top of `deploy.py`:
|
||||
https://github.com/Umio-Yasuno/amdgpu_top/releases.
|
||||
|
||||
Compose images in
|
||||
`compose/{llama,vllm,ollama,openwebui,beszel,openlit,phoenix,openhands}.yml`
|
||||
— pin tags here.
|
||||
`compose/{llama,vllm,ollama,openwebui,beszel,openlit,phoenix,openhands,homepage}.yml`
|
||||
— pin tags here. Homepage's tile/layout config is in `compose/homepage/`
|
||||
(`services.yaml`, `settings.yaml`, etc.); edit there, `./run.sh`, restart
|
||||
the homepage container.
|
||||
|
||||
## Front door
|
||||
|
||||
- **Homepage** (`/srv/docker/homepage`, http://framework:7575) — single
|
||||
page with one tile per service in this stack, with native widgets
|
||||
pulling live state (loaded models from Ollama, container status from
|
||||
the docker socket, etc.). Bookmarks for the reference docs across the
|
||||
bottom. Use as the default landing page on this network.
|
||||
|
||||
Bring-up: `cd /srv/docker/homepage && docker compose up -d`. No
|
||||
first-run setup — it reads `/srv/docker/homepage/config/*.yaml` and
|
||||
renders.
|
||||
|
||||
To customize: edit `pyinfra/framework/compose/homepage/*.yaml` in the
|
||||
repo, run `./run.sh`, then `docker compose restart homepage` on the
|
||||
box. Direct edits to `/srv/docker/homepage/config/*.yaml` will be
|
||||
overwritten on the next pyinfra deploy.
|
||||
|
||||
## Monitoring stack
|
||||
|
||||
|
||||
28
pyinfra/framework/compose/homepage.yml
Normal file
28
pyinfra/framework/compose/homepage.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
# Homepage — service launcher + status dashboard for the localgenai stack.
|
||||
# https://gethomepage.dev
|
||||
#
|
||||
# Single tile per service, with live widgets (loaded models, container
|
||||
# state, request rates) where the upstream supports it. Reads its config
|
||||
# from /srv/docker/homepage/config; pyinfra ships the initial files there
|
||||
# and leaves them alone afterwards (edit them directly to add bookmarks,
|
||||
# tweak the layout, etc).
|
||||
#
|
||||
# Bring-up: `docker compose up -d`. UI at http://framework:7575.
|
||||
services:
|
||||
homepage:
|
||||
image: ghcr.io/gethomepage/homepage:latest
|
||||
container_name: homepage
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# 7575 picked to avoid the soup of 30xx ports already in use
|
||||
# (OpenWebUI 3000, OpenLIT 3001, OpenHands 3030).
|
||||
- "7575:3000"
|
||||
volumes:
|
||||
- /srv/docker/homepage/config:/app/config
|
||||
# Read-only docker socket so homepage can render container status
|
||||
# (running / stopped, CPU, memory) on each tile.
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
# Required when accessed from a host other than localhost (i.e.
|
||||
# over Tailscale by hostname). Comma-separated list; * matches all.
|
||||
HOMEPAGE_ALLOWED_HOSTS: "framework:7575,framework,localhost:7575,localhost,*"
|
||||
22
pyinfra/framework/compose/homepage/bookmarks.yaml
Normal file
22
pyinfra/framework/compose/homepage/bookmarks.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
# https://gethomepage.dev/configs/bookmarks/
|
||||
|
||||
- Reference:
|
||||
- Strix Halo wiki:
|
||||
- abbr: SH
|
||||
href: https://strixhalo.wiki
|
||||
|
||||
- kyuz0 backend benchmarks:
|
||||
- abbr: KB
|
||||
href: https://kyuz0.github.io/amd-strix-halo-toolboxes/
|
||||
|
||||
- llama.cpp:
|
||||
- abbr: LC
|
||||
href: https://github.com/ggml-org/llama.cpp
|
||||
|
||||
- OpenCode:
|
||||
- abbr: OC
|
||||
href: https://opencode.ai
|
||||
|
||||
- Phoenix:
|
||||
- abbr: PX
|
||||
href: https://arize.com/docs/phoenix
|
||||
8
pyinfra/framework/compose/homepage/docker.yaml
Normal file
8
pyinfra/framework/compose/homepage/docker.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Docker integration. https://gethomepage.dev/configs/docker/
|
||||
#
|
||||
# `localhost-docker` is the name services.yaml refers to via
|
||||
# `server: localhost-docker`. Homepage uses the mounted /var/run/docker.sock
|
||||
# (read-only) to render container state on each tile.
|
||||
|
||||
localhost-docker:
|
||||
socket: /var/run/docker.sock
|
||||
80
pyinfra/framework/compose/homepage/services.yaml
Normal file
80
pyinfra/framework/compose/homepage/services.yaml
Normal file
@@ -0,0 +1,80 @@
|
||||
# Service tiles for the localgenai stack. Edit in place — pyinfra
|
||||
# ships this once and never overwrites.
|
||||
#
|
||||
# Widget reference: https://gethomepage.dev/widgets/
|
||||
|
||||
- Inference:
|
||||
- Ollama:
|
||||
icon: ollama.svg
|
||||
href: http://framework:11434
|
||||
description: Local model server (Qwen3-Coder-30B and friends)
|
||||
server: localhost-docker
|
||||
container: ollama
|
||||
widget:
|
||||
type: ollama
|
||||
url: http://framework:11434
|
||||
|
||||
- llama.cpp:
|
||||
icon: si-llama
|
||||
href: http://framework:8080
|
||||
description: Vulkan-backed llama.cpp server (gfx1151)
|
||||
server: localhost-docker
|
||||
container: llama
|
||||
# No native widget; a ping check confirms liveness.
|
||||
widget:
|
||||
type: customapi
|
||||
url: http://framework:8080/health
|
||||
refreshInterval: 30000
|
||||
mappings:
|
||||
- field: status
|
||||
label: Status
|
||||
|
||||
- vLLM:
|
||||
icon: mdi-server-network
|
||||
href: http://framework:8000
|
||||
description: Batched OpenAI-compatible serving (ROCm)
|
||||
server: localhost-docker
|
||||
container: vllm
|
||||
|
||||
- Agent UIs:
|
||||
- OpenWebUI:
|
||||
icon: open-webui.svg
|
||||
href: http://framework:3000
|
||||
description: Chat UI in front of Ollama, with SearXNG search
|
||||
server: localhost-docker
|
||||
container: openwebui
|
||||
|
||||
- OpenHands:
|
||||
icon: mdi-robot
|
||||
href: http://framework:3030
|
||||
description: Autonomous coding agent (loopback — needs SSH tunnel)
|
||||
server: localhost-docker
|
||||
container: openhands
|
||||
|
||||
- Observability:
|
||||
- Beszel:
|
||||
icon: beszel.svg
|
||||
href: http://framework:8090
|
||||
description: Host + container + AMD GPU dashboard
|
||||
server: localhost-docker
|
||||
container: beszel
|
||||
|
||||
- OpenLIT:
|
||||
icon: mdi-chart-line-variant
|
||||
href: http://framework:3001
|
||||
description: LLM fleet metrics (cost, tokens, latency)
|
||||
server: localhost-docker
|
||||
container: openlit
|
||||
|
||||
- Phoenix:
|
||||
icon: arize-phoenix.svg
|
||||
href: http://framework:6006
|
||||
description: Per-trace agent waterfall / flamegraph
|
||||
server: localhost-docker
|
||||
container: phoenix
|
||||
|
||||
- External:
|
||||
- SearXNG:
|
||||
icon: searxng.svg
|
||||
href: https://searxng.n0n.io
|
||||
description: Self-hosted metasearch (used by OpenWebUI + OpenCode)
|
||||
26
pyinfra/framework/compose/homepage/settings.yaml
Normal file
26
pyinfra/framework/compose/homepage/settings.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
# Top-level dashboard settings. https://gethomepage.dev/configs/settings/
|
||||
|
||||
title: localgenai
|
||||
favicon: https://gethomepage.dev/favicon.ico
|
||||
theme: dark
|
||||
color: slate
|
||||
|
||||
headerStyle: clean
|
||||
language: en
|
||||
target: _blank
|
||||
|
||||
# Layout: keep the four service groups in a 3-column grid each so a tile
|
||||
# fits comfortably without wrapping on a 1440-wide laptop.
|
||||
layout:
|
||||
Inference:
|
||||
style: row
|
||||
columns: 3
|
||||
Agent UIs:
|
||||
style: row
|
||||
columns: 3
|
||||
Observability:
|
||||
style: row
|
||||
columns: 3
|
||||
External:
|
||||
style: row
|
||||
columns: 3
|
||||
19
pyinfra/framework/compose/homepage/widgets.yaml
Normal file
19
pyinfra/framework/compose/homepage/widgets.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Top-of-page widgets. https://gethomepage.dev/widgets/info/
|
||||
|
||||
- search:
|
||||
provider: custom
|
||||
url: https://searxng.n0n.io/search?q=
|
||||
target: _blank
|
||||
focus: true
|
||||
|
||||
- resources:
|
||||
label: framework
|
||||
cpu: true
|
||||
memory: true
|
||||
disk: /
|
||||
|
||||
- datetime:
|
||||
text_size: xl
|
||||
format:
|
||||
timeStyle: short
|
||||
dateStyle: medium
|
||||
@@ -338,6 +338,7 @@ for svc in (
|
||||
"openlit",
|
||||
"phoenix",
|
||||
"openhands",
|
||||
"homepage",
|
||||
):
|
||||
files.directory(
|
||||
name=f"compose/{svc} dir",
|
||||
@@ -441,6 +442,34 @@ files.directory(
|
||||
_sudo=True,
|
||||
)
|
||||
|
||||
# Homepage config. The compose loop above only copies homepage.yml; the
|
||||
# YAML config files live in compose/homepage/ on the source side and at
|
||||
# /srv/docker/homepage/config/ on the box. Source-of-truth is the repo —
|
||||
# `./run.sh` syncs the config files. Edits should happen here in the
|
||||
# repo, not on the box, since pyinfra will overwrite drift.
|
||||
files.directory(
|
||||
name="Homepage config dir",
|
||||
path=f"{COMPOSE_DIR}/homepage/config",
|
||||
group="docker",
|
||||
mode="2775",
|
||||
_sudo=True,
|
||||
)
|
||||
for cfg in (
|
||||
"services.yaml",
|
||||
"settings.yaml",
|
||||
"widgets.yaml",
|
||||
"docker.yaml",
|
||||
"bookmarks.yaml",
|
||||
):
|
||||
files.put(
|
||||
name=f"homepage config: {cfg}",
|
||||
src=f"compose/homepage/{cfg}",
|
||||
dest=f"{COMPOSE_DIR}/homepage/config/{cfg}",
|
||||
group="docker",
|
||||
mode="664",
|
||||
_sudo=True,
|
||||
)
|
||||
|
||||
# --- Cleanup of artifacts from the prior native-build deploy ----------------
|
||||
# All idempotent — `present=False` is a no-op when the target is absent.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user