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.