sacpaint 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sacpaint-0.2.0/PKG-INFO +229 -0
- sacpaint-0.2.0/README.md +202 -0
- sacpaint-0.2.0/pyproject.toml +65 -0
- sacpaint-0.2.0/setup.cfg +4 -0
- sacpaint-0.2.0/src/sacpaint/__init__.py +28 -0
- sacpaint-0.2.0/src/sacpaint/assets/sacramento-line-v0.json +97 -0
- sacpaint-0.2.0/src/sacpaint/assets/sacramento-line-v0.png +0 -0
- sacpaint-0.2.0/src/sacpaint/assets/sacramento-line-v0.spec.json +531 -0
- sacpaint-0.2.0/src/sacpaint/claude_shim/__init__.py +45 -0
- sacpaint-0.2.0/src/sacpaint/claude_shim/__main__.py +80 -0
- sacpaint-0.2.0/src/sacpaint/claude_shim/server.py +357 -0
- sacpaint-0.2.0/src/sacpaint/claude_shim/translate.py +580 -0
- sacpaint-0.2.0/src/sacpaint/cli.py +511 -0
- sacpaint-0.2.0/src/sacpaint/mock.py +254 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/__init__.py +52 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/calibrate.py +180 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/calibration.py +305 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/cameras.py +378 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/client.py +514 -0
- sacpaint-0.2.0/src/sacpaint/opencastor/embodiment.py +606 -0
- sacpaint-0.2.0/src/sacpaint/rectify.py +171 -0
- sacpaint-0.2.0/src/sacpaint/reference.py +379 -0
- sacpaint-0.2.0/src/sacpaint/scorers.py +414 -0
- sacpaint-0.2.0/src/sacpaint/tasks.py +94 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/PKG-INFO +229 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/SOURCES.txt +31 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/dependency_links.txt +1 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/entry_points.txt +19 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/requires.txt +14 -0
- sacpaint-0.2.0/src/sacpaint.egg-info/top_level.txt +1 -0
- sacpaint-0.2.0/tests/test_claude_shim.py +862 -0
- sacpaint-0.2.0/tests/test_opencastor.py +987 -0
- sacpaint-0.2.0/tests/test_sacpaint.py +254 -0
sacpaint-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sacpaint
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Sacramento PaintBench: draw a fixed Sacramento skyline reference from camera feedback, scored by landmark geometry. An Inspect Robots benchmark.
|
|
5
|
+
Author: Craig Merry
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/craigm26/sacpaint
|
|
8
|
+
Project-URL: Issues, https://github.com/craigm26/sacpaint/issues
|
|
9
|
+
Keywords: robotics,benchmark,inspect-robots,evaluation,vision-language-action
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: inspect-robots>=0.58.0
|
|
17
|
+
Requires-Dist: numpy>=1.26
|
|
18
|
+
Requires-Dist: opencv-python-headless>=4.9
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
22
|
+
Requires-Dist: build; extra == "dev"
|
|
23
|
+
Requires-Dist: twine; extra == "dev"
|
|
24
|
+
Provides-Extra: agent
|
|
25
|
+
Requires-Dist: inspect-robots-agent>=0.26.0; extra == "agent"
|
|
26
|
+
Provides-Extra: opencastor
|
|
27
|
+
|
|
28
|
+
# Sacramento PaintBench (`sacpaint`)
|
|
29
|
+
|
|
30
|
+
A physical-AI benchmark on [Inspect Robots](https://github.com/robocurve/inspect-robots):
|
|
31
|
+
a robot with a pen must reproduce a fixed reference drawing from camera
|
|
32
|
+
feedback. One fixed prompt, one pinned reference image, geometric scoring that
|
|
33
|
+
anyone can recompute offline from a photo. No printed markers, no special
|
|
34
|
+
fixture: a sheet of paper on a desk and any camera, including an iPhone.
|
|
35
|
+
|
|
36
|
+
> Can a general-purpose frontier model reproduce a visual target with a physical
|
|
37
|
+
> tool, using camera feedback to correct itself, with no task-specific training?
|
|
38
|
+
|
|
39
|
+
The built-in reference is a simplified Sacramento composition: the Tower
|
|
40
|
+
Bridge above the Capitol dome and cupola, joined by the Capitol Mall, two
|
|
41
|
+
building masses, a horizon. Its SHA-256 is the benchmark's identity. The
|
|
42
|
+
physical sheet is 150 × 200 mm (A5 or half-letter with margins), small enough
|
|
43
|
+
for a desk arm such as the SO-ARM101 to reach every corner.
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+
|
|
47
|
+
## Sixty seconds, no robot
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install sacpaint
|
|
51
|
+
sacpaint score photo-of-my-drawing.jpg # any photo of a finished sheet -> score + overlay
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`score` finds the sheet in the photo (markers if present, otherwise the largest
|
|
55
|
+
bright quadrilateral, otherwise corners you pass with `--corners`), rectifies
|
|
56
|
+
it to the canonical canvas, and writes three files next to the photo: the
|
|
57
|
+
rectified canvas, an overlay with the reference ink in red, and a JSON
|
|
58
|
+
breakdown. That is the whole "process a new eval" path for a drawing made by
|
|
59
|
+
any robot, any policy, any hardware.
|
|
60
|
+
|
|
61
|
+
The full benchmark, in the built-in mock plotter world:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sacpaint run --policy sacpaint_trace --embodiment sacpaint_plotter --no-rerun --no-prompt # oracle, composite ~0.98
|
|
65
|
+
sacpaint run --policy sacpaint_idle --embodiment sacpaint_plotter --no-rerun --no-prompt # floor, 0.00
|
|
66
|
+
sacpaint export logs --out submission --label oracle # publishable bundle
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
With a frontier model driving the same mock world (needs `pip install inspect-robots-agent` and a key):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ANTHROPIC_API_KEY=... sacpaint run --policy agent --model anthropic/claude-fable-5 --embodiment sacpaint_plotter -- -P images=on_demand
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
No API key? One flag runs the same policy through a Claude subscription (the
|
|
76
|
+
Claude Code CLI, logged in on the machine) for development runs. Scores from
|
|
77
|
+
this path are labelled `wire=claude-code-cli` and are not leaderboard-comparable;
|
|
78
|
+
see [docs/subscription.md](docs/subscription.md).
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
sacpaint run --subscription --model haiku --policy agent --embodiment sacpaint_plotter --max-llm-calls 40 --no-rerun --no-prompt
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## The prompt
|
|
85
|
+
|
|
86
|
+
Fixed. Do not tune it per model.
|
|
87
|
+
|
|
88
|
+
> Draw the reference image on the canvas with the pen. You may look at the
|
|
89
|
+
> overhead camera to inspect your work and make corrections. Stop when you
|
|
90
|
+
> believe the drawing is complete.
|
|
91
|
+
|
|
92
|
+
The model receives the reference as an image stream named `reference` and the
|
|
93
|
+
canvas as a stream named `overhead`, because the agent policy attaches camera
|
|
94
|
+
frames to observations and the instruction cannot carry an image. Every
|
|
95
|
+
embodiment exposes those two streams.
|
|
96
|
+
|
|
97
|
+
## Scoring
|
|
98
|
+
|
|
99
|
+
All scorers are pure readers of the final canvas. Nothing calls a model. The
|
|
100
|
+
final canvas is the `observe_parked()` frame (pen lifted clear), rectified
|
|
101
|
+
unless the embodiment marks it canonical, then thresholded to ink.
|
|
102
|
+
|
|
103
|
+
| Scorer | Weight | What it measures |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `landmark_geometry` | 0.45 | Per landmark: precision × recall of ink inside its box at 1% of the canvas diagonal (2.5 mm) after centroid alignment (presence), times a centroid-offset term (position). Plus relations (tower over dome, same x, horizon above tower), gated on both landmarks being present. |
|
|
106
|
+
| `structure` | 0.30 | Precision × recall of all ink within 2% of the diagonal (5 mm) of reference ink. Product, not F1, because a dense scribble recalls everything. |
|
|
107
|
+
| `discipline` | 0.15 | 1 − fraction of ink farther than 2.5% of the diagonal (6 mm) from any reference ink, scaled down past 4× the reference's ink. Blank canvas scores 0. |
|
|
108
|
+
| `efficiency` | 0.10 | 1 − steps/max_steps for a declared finish; scaled by `structure` in the composite so finishing a bad drawing fast earns nothing. |
|
|
109
|
+
| `composite` | | The leaderboard number. |
|
|
110
|
+
|
|
111
|
+
Calibration on synthetic canvases:
|
|
112
|
+
|
|
113
|
+
| Canvas | composite | landmark | structure | discipline |
|
|
114
|
+
|---|---|---|---|---|
|
|
115
|
+
| perfect trace | 0.98 | 1.00 | 1.00 | 1.00 |
|
|
116
|
+
| perfect trace photographed at an angle, plain sheet, rectified | 0.97 | 0.97 | 1.00 | 1.00 |
|
|
117
|
+
| hand wobble, σ = 1 mm | 0.97 | 0.97 | 1.00 | 1.00 |
|
|
118
|
+
| whole drawing shifted 5 mm | 0.77 | 0.63 | 1.00 | 0.69 |
|
|
119
|
+
| top half only (bridge, no dome) | 0.53 | 0.38 | 0.55 | 1.00 |
|
|
120
|
+
| random scribble, 30 / 60 / 400 lines | 0.30 / 0.42 / 0.33 | 0.32 / 0.47 / 0.33 | 0.28 / 0.41 / 0.43 | 0.33 / 0.35 / 0.09 |
|
|
121
|
+
| blank | 0.00 | 0.00 | 0.00 | 0.00 |
|
|
122
|
+
|
|
123
|
+
Through the CLI with the framework's default guardrails, 3 epochs: oracle
|
|
124
|
+
`sacpaint_trace` composite 0.984, `sacpaint_idle` 0.000.
|
|
125
|
+
|
|
126
|
+
Known properties: global registration counts (a 5 mm offset is a placement
|
|
127
|
+
error, by design); a dense random scribble still collects about 0.3 to 0.4
|
|
128
|
+
because the reference covers much of the canvas; the tower, dome and road
|
|
129
|
+
dominate through their weights.
|
|
130
|
+
|
|
131
|
+
## Tracks
|
|
132
|
+
|
|
133
|
+
| Track | Memory across episodes | Camera | Measures |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| Cold | none | `-P images=on_demand`, never called | raw open-loop competence |
|
|
136
|
+
| Closed loop (default) | none | throughout | self-correction within one drawing |
|
|
137
|
+
| Learning | prior attempts via `inspect-robots summarize` + `-P prior_learnings=` | throughout | improvement across 5 canvases |
|
|
138
|
+
|
|
139
|
+
The learning track reuses the framework's own `summarize` / `prior_learnings`
|
|
140
|
+
mechanism, so the "memory" is an auditable markdown file with a recorded hash.
|
|
141
|
+
Report initial, final, best-of-5, and slope per attempt.
|
|
142
|
+
|
|
143
|
+
## Your own reference in two commands
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
sacpaint new mytown --canvas 210x297 # writes ~/.sacpaint/references/mytown.spec.json + a preview PNG
|
|
147
|
+
sacpaint run --task sacpaint/mytown --policy sacpaint_trace --embodiment sacpaint_plotter -- -E reference=mytown
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A reference is a JSON file of polylines grouped by landmark (millimetres,
|
|
151
|
+
origin bottom-left, y up), optional landmark weights and boxes, relations
|
|
152
|
+
(`above`, `left_of`, `same_x`), and tolerances. Every spec in
|
|
153
|
+
`~/.sacpaint/references/` registers as the task `sacpaint/<name>` the moment
|
|
154
|
+
the package is imported. The built-in spec is at
|
|
155
|
+
`src/sacpaint/assets/sacramento-line-v0.spec.json`; copy it, edit it, done.
|
|
156
|
+
|
|
157
|
+
## Real robots
|
|
158
|
+
|
|
159
|
+
Any embodiment that exposes this contract runs the benchmark unchanged:
|
|
160
|
+
|
|
161
|
+
- action space `eef_abs_pose`, dims `(x, y, z)` in metres in the canvas frame
|
|
162
|
+
(x right, y up the sheet, z above the paper; the pen marks at z ≤ 0.002 m,
|
|
163
|
+
travels at z ≥ 0.005 m);
|
|
164
|
+
- images `overhead` and `reference`, state `eef_pos` (3,);
|
|
165
|
+
- `supported_target_kinds` includes `reference_drawing`;
|
|
166
|
+
- `observe_parked()` lifts the pen clear and returns a fresh observation;
|
|
167
|
+
- corners of the sheet in the overhead frame, if known, as
|
|
168
|
+
`observation.extra["canvas_corners"]` (four `[x, y]` pairs, 0..1, TL TR BR BL);
|
|
169
|
+
otherwise the scorer finds the sheet itself.
|
|
170
|
+
|
|
171
|
+
| Body | How |
|
|
172
|
+
|---|---|
|
|
173
|
+
| Mock plotter (built in) | `--embodiment sacpaint_plotter`, options `-E reference=NAME -E photo_mode=sheet` |
|
|
174
|
+
| OpenCastor + SO-ARM101 with signed receipts | [docs/opencastor.md](docs/opencastor.md): `--embodiment opencastor` |
|
|
175
|
+
| iPhone as the overhead camera and operator microphone | the OpenCastor iOS app's Eval mode, see [docs/opencastor.md](docs/opencastor.md) |
|
|
176
|
+
| Any other arm | implement the contract above; `inspect-robots-so101` (LeRobot, joint space) is a fallback body that needs the agent's `move_joints` |
|
|
177
|
+
|
|
178
|
+
## Publishing a run the way robocurve does
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
sacpaint export logs --out submission --label opus
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
produces the layout of robocurve's published run datasets (clapboardbench):
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
submission/
|
|
188
|
+
├── README.md # provenance, reference hash, how to recompute
|
|
189
|
+
├── <log>.json # raw EvalLog: config, git rev, versions, scores, transcript
|
|
190
|
+
├── runs/README.md # index table: model, policy, embodiment, status, composite, steps
|
|
191
|
+
├── runs/<label>-<n>.md # one page per run: metadata, scores per epoch, per-landmark table,
|
|
192
|
+
│ # final canvas, the model's note for every tool call
|
|
193
|
+
├── html/ # inspect-robots view reports (frames the model saw)
|
|
194
|
+
├── canvases/ # the rectified final canvas and score JSON per trial
|
|
195
|
+
├── videos/ # inspect-robots video (when ffmpeg is installed)
|
|
196
|
+
└── reference.png, reference.sha256, rubric.json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`sacpaint worldevals-entry` prints the `Benchmark(...)` block for a
|
|
200
|
+
[WorldEvals](https://github.com/robocurve/worldevals) catalog pull request.
|
|
201
|
+
|
|
202
|
+
## Status
|
|
203
|
+
|
|
204
|
+
| Piece | State |
|
|
205
|
+
|---|---|
|
|
206
|
+
| Task `sacpaint/line-v0`, five scorers, three epochs | done, registered via entry points |
|
|
207
|
+
| Mock plotter + oracle/idle policies | done; the whole stack runs with no hardware |
|
|
208
|
+
| Marker-free rectification (given corners, ArUco, plain sheet) | done, tested under perspective |
|
|
209
|
+
| `sacpaint score / new / run / export / worldevals-entry` | done |
|
|
210
|
+
| `--policy agent` (frontier LLM) | works against the mock; needs a key or the subscription shim |
|
|
211
|
+
| OpenCastor / SO-ARM101 embodiment | see docs/opencastor.md |
|
|
212
|
+
| Hardware runs | not yet |
|
|
213
|
+
| WorldEvals catalog entry | after the first real-robot log |
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
git clone https://github.com/craigm26/sacpaint && cd sacpaint
|
|
219
|
+
uv venv && source .venv/bin/activate && uv pip install -e ".[dev,agent]"
|
|
220
|
+
pytest
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Regenerate the built-in assets (only when the reference itself changes; it re-versions the task):
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
python -c "from sacpaint.reference import write_assets; write_assets('src/sacpaint/assets')"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
MIT.
|
sacpaint-0.2.0/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Sacramento PaintBench (`sacpaint`)
|
|
2
|
+
|
|
3
|
+
A physical-AI benchmark on [Inspect Robots](https://github.com/robocurve/inspect-robots):
|
|
4
|
+
a robot with a pen must reproduce a fixed reference drawing from camera
|
|
5
|
+
feedback. One fixed prompt, one pinned reference image, geometric scoring that
|
|
6
|
+
anyone can recompute offline from a photo. No printed markers, no special
|
|
7
|
+
fixture: a sheet of paper on a desk and any camera, including an iPhone.
|
|
8
|
+
|
|
9
|
+
> Can a general-purpose frontier model reproduce a visual target with a physical
|
|
10
|
+
> tool, using camera feedback to correct itself, with no task-specific training?
|
|
11
|
+
|
|
12
|
+
The built-in reference is a simplified Sacramento composition: the Tower
|
|
13
|
+
Bridge above the Capitol dome and cupola, joined by the Capitol Mall, two
|
|
14
|
+
building masses, a horizon. Its SHA-256 is the benchmark's identity. The
|
|
15
|
+
physical sheet is 150 × 200 mm (A5 or half-letter with margins), small enough
|
|
16
|
+
for a desk arm such as the SO-ARM101 to reach every corner.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## Sixty seconds, no robot
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install sacpaint
|
|
24
|
+
sacpaint score photo-of-my-drawing.jpg # any photo of a finished sheet -> score + overlay
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`score` finds the sheet in the photo (markers if present, otherwise the largest
|
|
28
|
+
bright quadrilateral, otherwise corners you pass with `--corners`), rectifies
|
|
29
|
+
it to the canonical canvas, and writes three files next to the photo: the
|
|
30
|
+
rectified canvas, an overlay with the reference ink in red, and a JSON
|
|
31
|
+
breakdown. That is the whole "process a new eval" path for a drawing made by
|
|
32
|
+
any robot, any policy, any hardware.
|
|
33
|
+
|
|
34
|
+
The full benchmark, in the built-in mock plotter world:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
sacpaint run --policy sacpaint_trace --embodiment sacpaint_plotter --no-rerun --no-prompt # oracle, composite ~0.98
|
|
38
|
+
sacpaint run --policy sacpaint_idle --embodiment sacpaint_plotter --no-rerun --no-prompt # floor, 0.00
|
|
39
|
+
sacpaint export logs --out submission --label oracle # publishable bundle
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
With a frontier model driving the same mock world (needs `pip install inspect-robots-agent` and a key):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ANTHROPIC_API_KEY=... sacpaint run --policy agent --model anthropic/claude-fable-5 --embodiment sacpaint_plotter -- -P images=on_demand
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
No API key? One flag runs the same policy through a Claude subscription (the
|
|
49
|
+
Claude Code CLI, logged in on the machine) for development runs. Scores from
|
|
50
|
+
this path are labelled `wire=claude-code-cli` and are not leaderboard-comparable;
|
|
51
|
+
see [docs/subscription.md](docs/subscription.md).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
sacpaint run --subscription --model haiku --policy agent --embodiment sacpaint_plotter --max-llm-calls 40 --no-rerun --no-prompt
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## The prompt
|
|
58
|
+
|
|
59
|
+
Fixed. Do not tune it per model.
|
|
60
|
+
|
|
61
|
+
> Draw the reference image on the canvas with the pen. You may look at the
|
|
62
|
+
> overhead camera to inspect your work and make corrections. Stop when you
|
|
63
|
+
> believe the drawing is complete.
|
|
64
|
+
|
|
65
|
+
The model receives the reference as an image stream named `reference` and the
|
|
66
|
+
canvas as a stream named `overhead`, because the agent policy attaches camera
|
|
67
|
+
frames to observations and the instruction cannot carry an image. Every
|
|
68
|
+
embodiment exposes those two streams.
|
|
69
|
+
|
|
70
|
+
## Scoring
|
|
71
|
+
|
|
72
|
+
All scorers are pure readers of the final canvas. Nothing calls a model. The
|
|
73
|
+
final canvas is the `observe_parked()` frame (pen lifted clear), rectified
|
|
74
|
+
unless the embodiment marks it canonical, then thresholded to ink.
|
|
75
|
+
|
|
76
|
+
| Scorer | Weight | What it measures |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `landmark_geometry` | 0.45 | Per landmark: precision × recall of ink inside its box at 1% of the canvas diagonal (2.5 mm) after centroid alignment (presence), times a centroid-offset term (position). Plus relations (tower over dome, same x, horizon above tower), gated on both landmarks being present. |
|
|
79
|
+
| `structure` | 0.30 | Precision × recall of all ink within 2% of the diagonal (5 mm) of reference ink. Product, not F1, because a dense scribble recalls everything. |
|
|
80
|
+
| `discipline` | 0.15 | 1 − fraction of ink farther than 2.5% of the diagonal (6 mm) from any reference ink, scaled down past 4× the reference's ink. Blank canvas scores 0. |
|
|
81
|
+
| `efficiency` | 0.10 | 1 − steps/max_steps for a declared finish; scaled by `structure` in the composite so finishing a bad drawing fast earns nothing. |
|
|
82
|
+
| `composite` | | The leaderboard number. |
|
|
83
|
+
|
|
84
|
+
Calibration on synthetic canvases:
|
|
85
|
+
|
|
86
|
+
| Canvas | composite | landmark | structure | discipline |
|
|
87
|
+
|---|---|---|---|---|
|
|
88
|
+
| perfect trace | 0.98 | 1.00 | 1.00 | 1.00 |
|
|
89
|
+
| perfect trace photographed at an angle, plain sheet, rectified | 0.97 | 0.97 | 1.00 | 1.00 |
|
|
90
|
+
| hand wobble, σ = 1 mm | 0.97 | 0.97 | 1.00 | 1.00 |
|
|
91
|
+
| whole drawing shifted 5 mm | 0.77 | 0.63 | 1.00 | 0.69 |
|
|
92
|
+
| top half only (bridge, no dome) | 0.53 | 0.38 | 0.55 | 1.00 |
|
|
93
|
+
| random scribble, 30 / 60 / 400 lines | 0.30 / 0.42 / 0.33 | 0.32 / 0.47 / 0.33 | 0.28 / 0.41 / 0.43 | 0.33 / 0.35 / 0.09 |
|
|
94
|
+
| blank | 0.00 | 0.00 | 0.00 | 0.00 |
|
|
95
|
+
|
|
96
|
+
Through the CLI with the framework's default guardrails, 3 epochs: oracle
|
|
97
|
+
`sacpaint_trace` composite 0.984, `sacpaint_idle` 0.000.
|
|
98
|
+
|
|
99
|
+
Known properties: global registration counts (a 5 mm offset is a placement
|
|
100
|
+
error, by design); a dense random scribble still collects about 0.3 to 0.4
|
|
101
|
+
because the reference covers much of the canvas; the tower, dome and road
|
|
102
|
+
dominate through their weights.
|
|
103
|
+
|
|
104
|
+
## Tracks
|
|
105
|
+
|
|
106
|
+
| Track | Memory across episodes | Camera | Measures |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| Cold | none | `-P images=on_demand`, never called | raw open-loop competence |
|
|
109
|
+
| Closed loop (default) | none | throughout | self-correction within one drawing |
|
|
110
|
+
| Learning | prior attempts via `inspect-robots summarize` + `-P prior_learnings=` | throughout | improvement across 5 canvases |
|
|
111
|
+
|
|
112
|
+
The learning track reuses the framework's own `summarize` / `prior_learnings`
|
|
113
|
+
mechanism, so the "memory" is an auditable markdown file with a recorded hash.
|
|
114
|
+
Report initial, final, best-of-5, and slope per attempt.
|
|
115
|
+
|
|
116
|
+
## Your own reference in two commands
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
sacpaint new mytown --canvas 210x297 # writes ~/.sacpaint/references/mytown.spec.json + a preview PNG
|
|
120
|
+
sacpaint run --task sacpaint/mytown --policy sacpaint_trace --embodiment sacpaint_plotter -- -E reference=mytown
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
A reference is a JSON file of polylines grouped by landmark (millimetres,
|
|
124
|
+
origin bottom-left, y up), optional landmark weights and boxes, relations
|
|
125
|
+
(`above`, `left_of`, `same_x`), and tolerances. Every spec in
|
|
126
|
+
`~/.sacpaint/references/` registers as the task `sacpaint/<name>` the moment
|
|
127
|
+
the package is imported. The built-in spec is at
|
|
128
|
+
`src/sacpaint/assets/sacramento-line-v0.spec.json`; copy it, edit it, done.
|
|
129
|
+
|
|
130
|
+
## Real robots
|
|
131
|
+
|
|
132
|
+
Any embodiment that exposes this contract runs the benchmark unchanged:
|
|
133
|
+
|
|
134
|
+
- action space `eef_abs_pose`, dims `(x, y, z)` in metres in the canvas frame
|
|
135
|
+
(x right, y up the sheet, z above the paper; the pen marks at z ≤ 0.002 m,
|
|
136
|
+
travels at z ≥ 0.005 m);
|
|
137
|
+
- images `overhead` and `reference`, state `eef_pos` (3,);
|
|
138
|
+
- `supported_target_kinds` includes `reference_drawing`;
|
|
139
|
+
- `observe_parked()` lifts the pen clear and returns a fresh observation;
|
|
140
|
+
- corners of the sheet in the overhead frame, if known, as
|
|
141
|
+
`observation.extra["canvas_corners"]` (four `[x, y]` pairs, 0..1, TL TR BR BL);
|
|
142
|
+
otherwise the scorer finds the sheet itself.
|
|
143
|
+
|
|
144
|
+
| Body | How |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Mock plotter (built in) | `--embodiment sacpaint_plotter`, options `-E reference=NAME -E photo_mode=sheet` |
|
|
147
|
+
| OpenCastor + SO-ARM101 with signed receipts | [docs/opencastor.md](docs/opencastor.md): `--embodiment opencastor` |
|
|
148
|
+
| iPhone as the overhead camera and operator microphone | the OpenCastor iOS app's Eval mode, see [docs/opencastor.md](docs/opencastor.md) |
|
|
149
|
+
| Any other arm | implement the contract above; `inspect-robots-so101` (LeRobot, joint space) is a fallback body that needs the agent's `move_joints` |
|
|
150
|
+
|
|
151
|
+
## Publishing a run the way robocurve does
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
sacpaint export logs --out submission --label opus
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
produces the layout of robocurve's published run datasets (clapboardbench):
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
submission/
|
|
161
|
+
├── README.md # provenance, reference hash, how to recompute
|
|
162
|
+
├── <log>.json # raw EvalLog: config, git rev, versions, scores, transcript
|
|
163
|
+
├── runs/README.md # index table: model, policy, embodiment, status, composite, steps
|
|
164
|
+
├── runs/<label>-<n>.md # one page per run: metadata, scores per epoch, per-landmark table,
|
|
165
|
+
│ # final canvas, the model's note for every tool call
|
|
166
|
+
├── html/ # inspect-robots view reports (frames the model saw)
|
|
167
|
+
├── canvases/ # the rectified final canvas and score JSON per trial
|
|
168
|
+
├── videos/ # inspect-robots video (when ffmpeg is installed)
|
|
169
|
+
└── reference.png, reference.sha256, rubric.json
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`sacpaint worldevals-entry` prints the `Benchmark(...)` block for a
|
|
173
|
+
[WorldEvals](https://github.com/robocurve/worldevals) catalog pull request.
|
|
174
|
+
|
|
175
|
+
## Status
|
|
176
|
+
|
|
177
|
+
| Piece | State |
|
|
178
|
+
|---|---|
|
|
179
|
+
| Task `sacpaint/line-v0`, five scorers, three epochs | done, registered via entry points |
|
|
180
|
+
| Mock plotter + oracle/idle policies | done; the whole stack runs with no hardware |
|
|
181
|
+
| Marker-free rectification (given corners, ArUco, plain sheet) | done, tested under perspective |
|
|
182
|
+
| `sacpaint score / new / run / export / worldevals-entry` | done |
|
|
183
|
+
| `--policy agent` (frontier LLM) | works against the mock; needs a key or the subscription shim |
|
|
184
|
+
| OpenCastor / SO-ARM101 embodiment | see docs/opencastor.md |
|
|
185
|
+
| Hardware runs | not yet |
|
|
186
|
+
| WorldEvals catalog entry | after the first real-robot log |
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
git clone https://github.com/craigm26/sacpaint && cd sacpaint
|
|
192
|
+
uv venv && source .venv/bin/activate && uv pip install -e ".[dev,agent]"
|
|
193
|
+
pytest
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Regenerate the built-in assets (only when the reference itself changes; it re-versions the task):
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
python -c "from sacpaint.reference import write_assets; write_assets('src/sacpaint/assets')"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
MIT.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sacpaint"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Sacramento PaintBench: draw a fixed Sacramento skyline reference from camera feedback, scored by landmark geometry. An Inspect Robots benchmark."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Craig Merry" }]
|
|
13
|
+
keywords = ["robotics", "benchmark", "inspect-robots", "evaluation", "vision-language-action"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"inspect-robots>=0.58.0",
|
|
22
|
+
"numpy>=1.26",
|
|
23
|
+
"opencv-python-headless>=4.9",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/craigm26/sacpaint"
|
|
28
|
+
Issues = "https://github.com/craigm26/sacpaint/issues"
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = ["pytest>=8", "ruff>=0.5", "build", "twine"]
|
|
32
|
+
agent = ["inspect-robots-agent>=0.26.0"]
|
|
33
|
+
opencastor = [] # stdlib only; kept as a stable install target
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
sacpaint = "sacpaint.cli:main"
|
|
37
|
+
sacpaint-claude-shim = "sacpaint.claude_shim.__main__:main"
|
|
38
|
+
|
|
39
|
+
[project.entry-points."inspect_robots.tasks"]
|
|
40
|
+
"sacpaint/line-v0" = "sacpaint.tasks:line_v0"
|
|
41
|
+
|
|
42
|
+
[project.entry-points."inspect_robots.embodiments"]
|
|
43
|
+
sacpaint_plotter = "sacpaint.mock:plotter_embodiment"
|
|
44
|
+
opencastor = "sacpaint.opencastor.embodiment:opencastor_embodiment"
|
|
45
|
+
|
|
46
|
+
[project.entry-points."inspect_robots.policies"]
|
|
47
|
+
sacpaint_trace = "sacpaint.mock:trace_policy"
|
|
48
|
+
sacpaint_idle = "sacpaint.mock:idle_policy"
|
|
49
|
+
|
|
50
|
+
[project.entry-points."inspect_robots.scorers"]
|
|
51
|
+
sacpaint_landmarks = "sacpaint.scorers:landmark_geometry"
|
|
52
|
+
sacpaint_structure = "sacpaint.scorers:structure"
|
|
53
|
+
sacpaint_discipline = "sacpaint.scorers:discipline"
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.packages.find]
|
|
56
|
+
where = ["src"]
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.package-data]
|
|
59
|
+
sacpaint = ["assets/*.png", "assets/*.json"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 140
|
sacpaint-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Sacramento PaintBench: an Inspect Robots benchmark.
|
|
2
|
+
|
|
3
|
+
The task is one sentence: draw the fixed reference image on the canvas, using
|
|
4
|
+
the camera to inspect and correct, and stop when done. The built-in reference
|
|
5
|
+
is a deliberately simplified Sacramento composition (Tower Bridge above the
|
|
6
|
+
Capitol dome, joined by the Capitol Mall); any ``.spec.json`` of strokes makes
|
|
7
|
+
another. Scoring is geometric, not aesthetic: each landmark must be present,
|
|
8
|
+
in the right place, in the right relation to the others.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from sacpaint.reference import DEFAULT_REFERENCE, ReferenceSpec, available, canonical_size, get_spec, load_rubric, reference_image
|
|
12
|
+
from sacpaint.scorers import composite, discipline, efficiency, landmark_geometry, score_canvas, structure
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DEFAULT_REFERENCE",
|
|
16
|
+
"ReferenceSpec",
|
|
17
|
+
"available",
|
|
18
|
+
"canonical_size",
|
|
19
|
+
"composite",
|
|
20
|
+
"discipline",
|
|
21
|
+
"efficiency",
|
|
22
|
+
"get_spec",
|
|
23
|
+
"landmark_geometry",
|
|
24
|
+
"load_rubric",
|
|
25
|
+
"reference_image",
|
|
26
|
+
"score_canvas",
|
|
27
|
+
"structure",
|
|
28
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"canvas_mm": [
|
|
3
|
+
150.0,
|
|
4
|
+
200.0
|
|
5
|
+
],
|
|
6
|
+
"discipline_band_frac": 0.025,
|
|
7
|
+
"ink_threshold": 128,
|
|
8
|
+
"landmark_tolerance_frac": 0.01,
|
|
9
|
+
"landmarks": {
|
|
10
|
+
"buildings": {
|
|
11
|
+
"bbox": [
|
|
12
|
+
0.0133,
|
|
13
|
+
0.335,
|
|
14
|
+
0.9867,
|
|
15
|
+
0.765
|
|
16
|
+
],
|
|
17
|
+
"weight": 1
|
|
18
|
+
},
|
|
19
|
+
"capitol_dome": {
|
|
20
|
+
"bbox": [
|
|
21
|
+
0.2467,
|
|
22
|
+
0.66,
|
|
23
|
+
0.7533,
|
|
24
|
+
0.865
|
|
25
|
+
],
|
|
26
|
+
"weight": 3
|
|
27
|
+
},
|
|
28
|
+
"cupola": {
|
|
29
|
+
"bbox": [
|
|
30
|
+
0.43,
|
|
31
|
+
0.52,
|
|
32
|
+
0.57,
|
|
33
|
+
0.69
|
|
34
|
+
],
|
|
35
|
+
"weight": 1
|
|
36
|
+
},
|
|
37
|
+
"horizon": {
|
|
38
|
+
"bbox": [
|
|
39
|
+
0.0,
|
|
40
|
+
0.235,
|
|
41
|
+
1.0,
|
|
42
|
+
0.265
|
|
43
|
+
],
|
|
44
|
+
"weight": 1
|
|
45
|
+
},
|
|
46
|
+
"road": {
|
|
47
|
+
"bbox": [
|
|
48
|
+
0.3533,
|
|
49
|
+
0.4725,
|
|
50
|
+
0.6467,
|
|
51
|
+
0.665
|
|
52
|
+
],
|
|
53
|
+
"weight": 2
|
|
54
|
+
},
|
|
55
|
+
"tower_bridge": {
|
|
56
|
+
"bbox": [
|
|
57
|
+
0.38,
|
|
58
|
+
0.24,
|
|
59
|
+
0.62,
|
|
60
|
+
0.5025
|
|
61
|
+
],
|
|
62
|
+
"weight": 3
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"px_per_mm": 4.0,
|
|
66
|
+
"relations": [
|
|
67
|
+
{
|
|
68
|
+
"a": "tower_bridge",
|
|
69
|
+
"b": "capitol_dome",
|
|
70
|
+
"kind": "same_x",
|
|
71
|
+
"tolerance": 0.05
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"a": "tower_bridge",
|
|
75
|
+
"b": "capitol_dome",
|
|
76
|
+
"kind": "above"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"a": "cupola",
|
|
80
|
+
"b": "capitol_dome",
|
|
81
|
+
"kind": "above"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"a": "horizon",
|
|
85
|
+
"b": "tower_bridge",
|
|
86
|
+
"kind": "above"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"structure_tolerance_frac": 0.02,
|
|
90
|
+
"version": "sacramento-line-v0",
|
|
91
|
+
"weights": {
|
|
92
|
+
"discipline": 0.15,
|
|
93
|
+
"efficiency": 0.1,
|
|
94
|
+
"landmark_geometry": 0.45,
|
|
95
|
+
"structure": 0.3
|
|
96
|
+
}
|
|
97
|
+
}
|
|
Binary file
|