File size: 2,525 Bytes
6dd2ce8 3d20eb8 6dd2ce8 3d20eb8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ---
title: Harbor Run Viewer
emoji: 🔭
colorFrom: indigo
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
---
# Harbor Run Viewer
Train and eval runs from the OpenEnv × Harbor stack. **The data lives in a Hugging Face bucket; this app
only renders it.**
## The one design decision
A bucket mounted into a Space appears as an ordinary filesystem path inside the container, so the Space
and a laptop run *the same* code against *the same* layout — `DATA_DIR` is `/data` in one case and
`./data` in the other. There is no local-vs-remote branch to keep in step, and no data in the image.
```
laptop: DATA_DIR=./data uvicorn app:app
Space: DATA_DIR=/data ← bucket mounted read-only or read-write
fallback: DATA_BUCKET=ns/bucket ← read a bucket over HfFileSystem without mounting it
```
## Run it locally
```sh
pip install -r requirements.txt
DATA_DIR=./data uvicorn app:app --port 8000 # http://localhost:8000
```
## Publish runs to the bucket
```sh
hf buckets create <ns>/harbor-runs --exist-ok
hf sync ./data hf://buckets/<ns>/harbor-runs # add --delete to mirror exactly
```
## Point the Space at the bucket
```sh
hf spaces volumes set <ns>/harbor-run-viewer -v hf://buckets/<ns>/harbor-runs:/data
```
Only buckets support read-write mounts; models, datasets and Spaces are always read-only. A private
bucket needs `HF_TOKEN` as a Space secret.
Then press **⟳ Refresh** in the UI: it drops every cache and rescans. A mounted bucket already reflects
new writes, so nothing needs rebuilding or restarting to see a run that finished a minute ago.
## Layout
See [CONTRACT.md](CONTRACT.md). Briefly: one directory per run under `runs/<run_id>/`, identified by a
`run.json`, with optional `eval/summary.json` and `train/metrics.jsonl`. A run with only training data
renders as training; one with both shows both; nothing is required beyond `run.json`.
Two conventions in that contract carry real meaning rather than being formatting:
- **`reward: null` is not zero.** It means the verifier never ran — a dead sandbox, no answer. Those
attempts are excluded, and a task whose every attempt is null is counted as *unmeasured*. Averaging
them in as zeros makes infrastructure failure look like a weak model.
- **`reward_std` sits next to `reward`.** A step whose generations all score the same has zero advantage
and teaches nothing, so the viewer reports *steps with a gradient* — a reward curve alone cannot
distinguish learning from a flat run.
|