Five required + four optional slides, slotted into the existing flow without renumbering the visible deck UI: REQUIRED - problem-statement (after motivation): single-sentence problem, three numeric stat cards, explicit task-type justification (multi-class classification, why not regression/ranking) - research-questions (after problem-statement): two-column literature gap layout + RQ1/RQ2/RQ3 - solution-overview (after research-questions): inline-SVG block diagram of the pipeline (fleet hosts → receiver → episodes → windowing → model zoo → per-window phase → trust score → containment + reset) - evaluation-setup (between chunking and models): four blocks covering split recipe, primary metric, baselines compared, and what's reported alongside accuracy. Each block leads with the *why*, matching the assignment's "explain not only what will be measured but why" requirement. - conclusion-future (before references): two-column "what we showed" + unsupervised next steps (clustering / anomaly / SSL pretrain / embedding viz). Addresses Section 8 of the assignment guide. OPTIONAL - theoretical-contributions: window-centre labelling, schema-hashed checkpoints, cross-host as eval axis - practical-contributions: /proc-only deployment, producer-agnostic dashboard, labelled dataset on disk - design-principles: one-loop-many-models, typed events as contract, two-agent path ownership - limitations: two-host fleet, synthetic profiles, 10 Hz floor, KNN cross-host gap Plus references/links.md gains four real online references (PyTorch, XGBoost, scikit-learn, proc(5)) bringing the citation count from 8 to 12 — over the assignment's 10-source minimum. CSS additions cover the new layouts (.problem-claim, .problem-stats, .research-grid, .pipeline-svg + .pipeline-stage / .pipeline-arrow, .eval-blocks, .conclusion-grid). Limitations cards reuse the motivation-card pattern with an armed-phase amber marker for the "warning" feel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| static | ||
| __init__.py | ||
| __main__.py | ||
| app.py | ||
| client.py | ||
| dashboard.caddy | ||
| events.py | ||
| feeder.py | ||
| PRODUCERS.md | ||
| README.md | ||
training/dashboard/
Live web display served at https://dashboard.wg. A Starlette app
on 127.0.0.1:8447 behind Caddy; messages from Python are pushed to
connected browsers over a WebSocket.
This is intentionally a blank slate — the default page just
appends every received JSON message to a scrolling log. Build the
real widgets on top of window.dashboard.onMessage.
Run locally
uv run python -m training.dashboard
# → open http://127.0.0.1:8447
Push live data from Python
Same process (e.g. a notebook driving the page):
from training.dashboard.app import broadcaster
await broadcaster.publish({"type": "metric", "name": "loss", "value": 0.42})
Different process (orchestrator, receiver, ad-hoc shell):
curl -s http://127.0.0.1:8447/publish \
-H 'content-type: application/json' \
-d '{"type":"hello","msg":"from cron"}'
The /publish endpoint is loopback-only (403 otherwise) and is not
reverse-proxied by Caddy, so it cannot be hit from the WG mesh.
Producing events from your own code
If you're writing code that should drive a dashboard widget (model
inference, training-loop metrics, profile envelopes), see
PRODUCERS.md — it documents every event type
the widgets subscribe to, the loopback-only /publish contract, the
reconnect gotcha, and the systemd hardening that constrains
producers running on the Pi. There's a stdlib-only Python helper at
client.py:
from training.dashboard.client import Publisher
Publisher().publish({"type": "model_metric", "model": "lstm", "accuracy": 0.928})
Customizing the page
The default static/index.html exposes window.dashboard:
window.dashboard.onMessage = (msg) => {
if (msg.type === 'metric') updateChart(msg.name, msg.value);
};
window.dashboard.send({type: 'request-snapshot'}); // browser → server
Override onMessage to dispatch to your own widgets. The blank-slate
log renderer is just there so a fresh deploy is observably alive.
Deploying to the Pi
sudo cp etc/cis490-dashboard.service /etc/systemd/system/sudo cp training/dashboard/dashboard.caddy /etc/caddy/Caddyfile.d/sudo systemctl daemon-reload && sudo systemctl enable --now cis490-dashboard.servicesudo systemctl reload caddy