kitchenbench 0.3.0__py3-none-any.whl
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.
- kitchenbench/CLAUDE.md +60 -0
- kitchenbench/__init__.py +91 -0
- kitchenbench/distributions.py +123 -0
- kitchenbench/embodiment.py +138 -0
- kitchenbench/instances.py +87 -0
- kitchenbench/policies.py +96 -0
- kitchenbench/py.typed +0 -0
- kitchenbench/scoring.py +36 -0
- kitchenbench/specs.py +714 -0
- kitchenbench/tasks.py +169 -0
- kitchenbench-0.3.0.dist-info/METADATA +273 -0
- kitchenbench-0.3.0.dist-info/RECORD +15 -0
- kitchenbench-0.3.0.dist-info/WHEEL +4 -0
- kitchenbench-0.3.0.dist-info/entry_points.txt +19 -0
- kitchenbench-0.3.0.dist-info/licenses/LICENSE +21 -0
kitchenbench/tasks.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""The 10 KitchenBench tasks, generated from :data:`kitchenbench.specs.SPECS`.
|
|
2
|
+
|
|
3
|
+
Each task instance (a stochastic setup + goal) becomes one Inspect Robots ``Scene``;
|
|
4
|
+
each task runs ``K_REALIZATIONS`` (5) realizations per instance via
|
|
5
|
+
``Epochs(count=5, reducer="mean")``, so the per-scene reduced ``task_success`` is
|
|
6
|
+
the methodology's instance success probability P̂[Yᵢ=1]. With ``K_INSTANCES`` (5)
|
|
7
|
+
instances per task that is 5 scenes x 5 epochs per task.
|
|
8
|
+
|
|
9
|
+
Each task is registered with Inspect Robots under ``kitchenbench/<key>`` (the slash
|
|
10
|
+
namespaces KitchenBench within WorldEvals). The entry-point name, the ``@task``
|
|
11
|
+
name, and the returned ``Task.name`` are all identical.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import asdict
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from inspect_robots import Epochs, Scene, Target, Task, episode_length, task
|
|
21
|
+
from inspect_robots.rollout import derive_seed
|
|
22
|
+
|
|
23
|
+
from kitchenbench.instances import K_INSTANCES, K_REALIZATIONS, Realization
|
|
24
|
+
from kitchenbench.scoring import task_success
|
|
25
|
+
from kitchenbench.specs import SPEC_BY_KEY, TaskSpec
|
|
26
|
+
|
|
27
|
+
_SLUG_RE = re.compile(r"[^a-z0-9]+")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _slug(text: str) -> str:
|
|
31
|
+
return _SLUG_RE.sub("-", text.lower()).strip("-")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _validation_dict(spec_validation: Any) -> dict[str, Any]:
|
|
35
|
+
"""``asdict`` a Validation, casting rating tuples to lists for clean JSON."""
|
|
36
|
+
return {k: (list(v) if isinstance(v, tuple) else v) for k, v in asdict(spec_validation).items()}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_scenes(spec: TaskSpec) -> list[Scene]:
|
|
40
|
+
"""Build one Scene per task instance (5 per task)."""
|
|
41
|
+
scenes: list[Scene] = []
|
|
42
|
+
for index, inst in enumerate(spec.instances):
|
|
43
|
+
# The displayed instruction is the realization of epoch 0 under the default
|
|
44
|
+
# eval(seed=0), so it equals the first actual rollout's instruction.
|
|
45
|
+
canonical = inst.realize(derive_seed(0, index, 0))
|
|
46
|
+
scenes.append(
|
|
47
|
+
Scene(
|
|
48
|
+
id=_slug(inst.instance_id),
|
|
49
|
+
instruction=canonical.instruction,
|
|
50
|
+
target=Target(kind=inst.target_kind, spec=dict(inst.static)),
|
|
51
|
+
init_seed=index,
|
|
52
|
+
metadata={
|
|
53
|
+
"benchmark": "kitchenbench",
|
|
54
|
+
"task": spec.key,
|
|
55
|
+
"category": spec.category,
|
|
56
|
+
"bimanual": spec.bimanual,
|
|
57
|
+
"version": spec.version,
|
|
58
|
+
"instance_id": inst.instance_id,
|
|
59
|
+
"instance_index": index,
|
|
60
|
+
"setup": inst.setup_spec(),
|
|
61
|
+
"language_vars": list(inst.language_vars),
|
|
62
|
+
"validation": _validation_dict(inst.validation),
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
return scenes
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def realize_scene(scene: Scene, seed: int | None) -> Realization:
|
|
70
|
+
"""Recover the task instance behind a Scene and realize it for ``seed``.
|
|
71
|
+
|
|
72
|
+
The seam a real embodiment / operator tool uses to get the concrete setup for
|
|
73
|
+
a given realization. Recovery is by ``instance_id`` (stable across releases),
|
|
74
|
+
not by position — a scene logged under an older task set either resolves to
|
|
75
|
+
the same instance or fails loudly, never silently realizes a different one.
|
|
76
|
+
``seed=None`` (direct ``reset(scene)`` calls) is guarded to ``0`` for
|
|
77
|
+
determinism.
|
|
78
|
+
"""
|
|
79
|
+
key = str(scene.metadata["task"])
|
|
80
|
+
instance_id = str(scene.metadata["instance_id"])
|
|
81
|
+
for inst in SPEC_BY_KEY[key].instances:
|
|
82
|
+
if inst.instance_id == instance_id:
|
|
83
|
+
return inst.realize(seed if seed is not None else 0)
|
|
84
|
+
raise LookupError(f"no instance {instance_id!r} in task {key!r} (removed or renamed?)")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def make_task(spec: TaskSpec) -> Task:
|
|
88
|
+
"""Assemble a Inspect Robots :class:`~inspect_robots.Task` from a :class:`TaskSpec`."""
|
|
89
|
+
return Task(
|
|
90
|
+
name=f"kitchenbench/{spec.key}",
|
|
91
|
+
scenes=build_scenes(spec),
|
|
92
|
+
scorer=[task_success(), episode_length()],
|
|
93
|
+
max_steps=spec.max_steps,
|
|
94
|
+
epochs=Epochs(count=K_REALIZATIONS, reducer="mean"),
|
|
95
|
+
metadata={
|
|
96
|
+
"title": spec.title,
|
|
97
|
+
"category": spec.category,
|
|
98
|
+
"bimanual": spec.bimanual,
|
|
99
|
+
"version": spec.version,
|
|
100
|
+
"description": spec.description,
|
|
101
|
+
"k_instances": K_INSTANCES,
|
|
102
|
+
"k_realizations": K_REALIZATIONS,
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@task("kitchenbench/place_cutlery")
|
|
108
|
+
def place_cutlery() -> Task:
|
|
109
|
+
return make_task(SPEC_BY_KEY["place_cutlery"])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@task("kitchenbench/stack")
|
|
113
|
+
def stack() -> Task:
|
|
114
|
+
return make_task(SPEC_BY_KEY["stack"])
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@task("kitchenbench/place_in_rack")
|
|
118
|
+
def place_in_rack() -> Task:
|
|
119
|
+
return make_task(SPEC_BY_KEY["place_in_rack"])
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@task("kitchenbench/pour_pasta")
|
|
123
|
+
def pour_pasta() -> Task:
|
|
124
|
+
return make_task(SPEC_BY_KEY["pour_pasta"])
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@task("kitchenbench/open_container")
|
|
128
|
+
def open_container() -> Task:
|
|
129
|
+
return make_task(SPEC_BY_KEY["open_container"])
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@task("kitchenbench/fold_cloth")
|
|
133
|
+
def fold_cloth() -> Task:
|
|
134
|
+
return make_task(SPEC_BY_KEY["fold_cloth"])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@task("kitchenbench/seal_container")
|
|
138
|
+
def seal_container() -> Task:
|
|
139
|
+
return make_task(SPEC_BY_KEY["seal_container"])
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@task("kitchenbench/handoff")
|
|
143
|
+
def handoff() -> Task:
|
|
144
|
+
return make_task(SPEC_BY_KEY["handoff"])
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@task("kitchenbench/sort_cutlery")
|
|
148
|
+
def sort_cutlery() -> Task:
|
|
149
|
+
return make_task(SPEC_BY_KEY["sort_cutlery"])
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@task("kitchenbench/scoop_pasta")
|
|
153
|
+
def scoop_pasta() -> Task:
|
|
154
|
+
return make_task(SPEC_BY_KEY["scoop_pasta"])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
#: All task factories, keyed by their bare key (used by ``__init__`` + tests).
|
|
158
|
+
TASK_FACTORIES = {
|
|
159
|
+
"place_cutlery": place_cutlery,
|
|
160
|
+
"stack": stack,
|
|
161
|
+
"place_in_rack": place_in_rack,
|
|
162
|
+
"pour_pasta": pour_pasta,
|
|
163
|
+
"open_container": open_container,
|
|
164
|
+
"fold_cloth": fold_cloth,
|
|
165
|
+
"seal_container": seal_container,
|
|
166
|
+
"handoff": handoff,
|
|
167
|
+
"sort_cutlery": sort_cutlery,
|
|
168
|
+
"scoop_pasta": scoop_pasta,
|
|
169
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kitchenbench
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A bimanual kitchen-manipulation benchmark for VLA models, built on Inspect Robots.
|
|
5
|
+
Project-URL: Homepage, https://github.com/robocurve/kitchenbench
|
|
6
|
+
Project-URL: Collection, https://github.com/robocurve/worldevals
|
|
7
|
+
Project-URL: Framework, https://github.com/robocurve/inspect-robots
|
|
8
|
+
Author: RoboCurve
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: benchmark,bimanual,kitchen,manipulation,robotics,vla
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: inspect-robots>=0.3
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
23
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
<div align="center">
|
|
30
|
+
|
|
31
|
+
# 🍳 KitchenBench
|
|
32
|
+
|
|
33
|
+
**A bimanual kitchen-manipulation benchmark for VLA models.**
|
|
34
|
+
|
|
35
|
+
Built on [Inspect Robots](https://github.com/robocurve/inspect-robots) · part of
|
|
36
|
+
[WorldEvals](https://github.com/robocurve/worldevals), the "Inspect Evals for robotics".
|
|
37
|
+
|
|
38
|
+
[](https://github.com/robocurve/kitchenbench/actions/workflows/ci.yml)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
[](https://github.com/robocurve/kitchenbench/actions/workflows/ci.yml)
|
|
41
|
+
[](https://github.com/robocurve/inspect-robots)
|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
KitchenBench is **10 kitchen-manipulation tasks** expressed as Inspect Robots `Task`s —
|
|
46
|
+
embodiment-agnostic, so you run them against *any* compatible policy/embodiment.
|
|
47
|
+
The set emphasizes **bimanual coordination**: pouring, lid removal, folding,
|
|
48
|
+
part-mating, a pure two-arm handover, and tool-mediated scooping, alongside
|
|
49
|
+
classic pick-place / stacking / slotted insertion and a multi-instance sort.
|
|
50
|
+
|
|
51
|
+
It ships a **dependency-free mock kitchen** so the whole suite runs in CI, and is
|
|
52
|
+
designed to point straight at real hardware — e.g. **YAM bimanual arms** driven by
|
|
53
|
+
**MolmoAct2**.
|
|
54
|
+
|
|
55
|
+
## The tasks
|
|
56
|
+
|
|
57
|
+
| Task (`--task`) | Goal | Bimanual | Category |
|
|
58
|
+
|---|---|:--:|---|
|
|
59
|
+
| `kitchenbench/place_cutlery` | place the {cutlery} on the {dishware} | | pick-place |
|
|
60
|
+
| `kitchenbench/stack` | stack the cups / bowls / plates | | stacking |
|
|
61
|
+
| `kitchenbench/place_in_rack` | place the {dishware} into the dish rack | | insertion |
|
|
62
|
+
| `kitchenbench/pour_pasta` | pour the dry pasta into the {vessel} | ✅ | granular |
|
|
63
|
+
| `kitchenbench/open_container` | open the {container} | ✅ | articulated |
|
|
64
|
+
| `kitchenbench/fold_cloth` | fold the {cloth} | ✅ | deformable |
|
|
65
|
+
| `kitchenbench/seal_container` | seal the {container} with its lid | ✅ | mating |
|
|
66
|
+
| `kitchenbench/handoff` | hand off the {item} from one arm to the other | ✅ | coordination |
|
|
67
|
+
| `kitchenbench/sort_cutlery` | sort the cutlery into the correct tray compartments | | classification |
|
|
68
|
+
| `kitchenbench/scoop_pasta` | scoop the {pasta} with the {tool} and transfer it to the container | ✅ | granular+tool |
|
|
69
|
+
|
|
70
|
+
## Task instances & realizations
|
|
71
|
+
|
|
72
|
+
KitchenBench follows the
|
|
73
|
+
[physical-automation methodology](https://github.com/jeqcho/physical-automation-methodology-docs).
|
|
74
|
+
The key ideas, top-down:
|
|
75
|
+
|
|
76
|
+
- A **task** (e.g. `pour_pasta`) is a set of **task instances**.
|
|
77
|
+
- A **task instance** is one concrete *scenario written as a distribution*: a
|
|
78
|
+
**stochastic setup** (named random variables, each with a distribution) plus a
|
|
79
|
+
**goal** (a natural-language success criterion that may reference the sampled
|
|
80
|
+
variables). It is *not* a single fixed scene — it is a recipe for generating many.
|
|
81
|
+
- A **realization** is one sample of that recipe: draw every random variable from
|
|
82
|
+
its distribution to get one concrete environment (and a concrete goal sentence).
|
|
83
|
+
- Running a `(policy, embodiment)` pair on `K_realizations` realizations and
|
|
84
|
+
averaging the binary successes estimates the **instance success probability**
|
|
85
|
+
P̂[Yᵢ = 1].
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
task pour_pasta
|
|
89
|
+
├─ instance 1 (a distribution) ──realize──▶ 5 concrete environments ──▶ P̂₁
|
|
90
|
+
├─ instance 2 (a distribution) ──realize──▶ 5 concrete environments ──▶ P̂₂
|
|
91
|
+
│ … 5 instances total …
|
|
92
|
+
└─ instance 5 (a distribution) ──realize──▶ 5 concrete environments ──▶ P̂₅
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
KitchenBench uses the methodology's recommended defaults: **5 instances per task**
|
|
96
|
+
(`K_INSTANCES`) and **5 realizations per instance** (`K_REALIZATIONS`).
|
|
97
|
+
|
|
98
|
+
### A worked example
|
|
99
|
+
|
|
100
|
+
This is one of `pour_pasta`'s five instances (from
|
|
101
|
+
[`specs.py`](src/kitchenbench/specs.py), lightly reformatted):
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
TaskInstance(
|
|
105
|
+
instance_id="pour_pasta/measuring-cup-to-bowl",
|
|
106
|
+
goal="pour the dry pasta into the {vessel}", # {vessel} is sampled
|
|
107
|
+
setup={
|
|
108
|
+
"vessel": Categorical(("bowl", "cup", "pot")),
|
|
109
|
+
"fill_g": Uniform(80, 200), # grams of pasta
|
|
110
|
+
"pour_height_cm": Uniform(8, 15),
|
|
111
|
+
"vessel_x_cm": Normal(0.0, 3.0), # placement jitter (cm)
|
|
112
|
+
"vessel_y_cm": Normal(0.0, 3.0),
|
|
113
|
+
},
|
|
114
|
+
language_vars=("vessel",),
|
|
115
|
+
target_kind="pour_into",
|
|
116
|
+
static={"substance": "dry_pasta"},
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Realizing it** with different seeds samples those distributions into concrete
|
|
121
|
+
environments an operator can physically arrange (and a goal to give the VLA)
|
|
122
|
+
(numbers rounded here for readability):
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
realize(seed=0) realize(seed=2)
|
|
126
|
+
Goal: pour the dry pasta into the bowl Goal: pour the dry pasta into the cup
|
|
127
|
+
Setup: Setup:
|
|
128
|
+
vessel = bowl vessel = cup
|
|
129
|
+
fill_g = 156 fill_g = 111
|
|
130
|
+
pour_height_cm = 9.9 pour_height_cm = 10.1
|
|
131
|
+
vessel_x_cm = +0.3 vessel_x_cm = -7.3
|
|
132
|
+
vessel_y_cm = -1.6 vessel_y_cm = +5.4
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Inspect and realize instances from Python:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from kitchenbench import SPEC_BY_KEY
|
|
139
|
+
|
|
140
|
+
inst = SPEC_BY_KEY["pour_pasta"].instances[0]
|
|
141
|
+
inst.setup_spec()
|
|
142
|
+
# {'fill_g': 'Uniform[80, 200]', 'pour_height_cm': 'Uniform[8, 15]',
|
|
143
|
+
# 'vessel': 'Categorical({bowl, cup, pot})', 'vessel_x_cm': 'N(0, 3²)', ...}
|
|
144
|
+
|
|
145
|
+
r = inst.realize(seed=0)
|
|
146
|
+
r.instruction # 'pour the dry pasta into the bowl'
|
|
147
|
+
r.values # {'vessel': 'bowl', 'fill_g': 156.43…, 'pour_height_cm': 9.88…, …} (JSON-native)
|
|
148
|
+
r.setup_lines # ('fill_g = 156.43…', 'pour_height_cm = 9.88…', 'vessel = bowl', …)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### How it maps to a run
|
|
152
|
+
|
|
153
|
+
Each instance becomes one Inspect Robots `Scene`; the 5 realizations are the 5 **epochs**
|
|
154
|
+
(`Epochs(count=5, reducer="mean")`), each seeded independently. Because the reducer
|
|
155
|
+
is the mean, **each scene's reduced `task_success` is the instance success
|
|
156
|
+
probability P̂[Yᵢ = 1]** — exactly the methodology's estimator:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from inspect_robots import eval
|
|
160
|
+
(log,) = eval("kitchenbench/pour_pasta", "kitchen_scripted", "kitchen")
|
|
161
|
+
for s in log.samples:
|
|
162
|
+
print(s.scene_id, s.reduced["task_success"]) # one P̂ per instance
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
On real hardware an embodiment (or operator tool) calls `realize_scene(scene,
|
|
166
|
+
seed)` to get the concrete setup to arrange — `Realization.setup_lines` is the
|
|
167
|
+
"arrange this" checklist, and `Realization.instruction` is the goal fed to the VLA.
|
|
168
|
+
|
|
169
|
+
**Distribution types** (in [`distributions.py`](src/kitchenbench/distributions.py)):
|
|
170
|
+
`Uniform(a, b)` continuous · `Categorical((…), weights=None)` over a finite set ·
|
|
171
|
+
`Normal(μ, σ)` Gaussian · `Constant(v)` fixed. Every sample is a builtin
|
|
172
|
+
`float`/`int`/`str` (JSON-native), and `Categorical` preserves value types (an `int`
|
|
173
|
+
category samples back as an `int`).
|
|
174
|
+
|
|
175
|
+
> **Validation status — read before trusting the numbers.** The shipped instances
|
|
176
|
+
> are **AI-authored drafts** (`Validation(source="opus-draft")`, `validated=False`).
|
|
177
|
+
> The methodology's `K_i = 5` is the count *after* human validation — 3 experts
|
|
178
|
+
> rating each instance on representativeness **and** quality, accepted only if both
|
|
179
|
+
> are ≥ 4. Run that commissioning pipeline before relying on the instances; we do
|
|
180
|
+
> **not** fabricate ratings. Also note `eval()`'s task-level
|
|
181
|
+
> `metrics["task_success"]` is the *mean of P̂ over instances* — a convenience
|
|
182
|
+
> aggregate, **not** a methodology output (the methodology sorts the per-instance P̂
|
|
183
|
+
> into quantiles and fits the pTQ / automation-halvings curves).
|
|
184
|
+
|
|
185
|
+
## Install
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Inspect Robots isn't on PyPI yet, so install both from GitHub (uv recommended):
|
|
189
|
+
uv pip install "inspect-robots @ git+https://github.com/robocurve/inspect-robots@v0.3.0"
|
|
190
|
+
uv pip install "kitchenbench @ git+https://github.com/robocurve/kitchenbench"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Run it (mock kitchen, no hardware)
|
|
194
|
+
|
|
195
|
+
KitchenBench registers a dependency-free mock embodiment (`kitchen`) and policies
|
|
196
|
+
(`kitchen_scripted` / `kitchen_random` / `kitchen_noop`) via entry points:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
inspect-robots list tasks # see all kitchenbench/* tasks
|
|
200
|
+
inspect-robots run --task kitchenbench/pour_pasta --policy kitchen_scripted --embodiment kitchen
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Or in Python:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from inspect_robots import eval
|
|
207
|
+
|
|
208
|
+
(log,) = eval("kitchenbench/open_container", "kitchen_scripted", "kitchen")
|
|
209
|
+
# Per-instance success probability P̂[Yᵢ=1] lives in each sample's reduced score:
|
|
210
|
+
for s in log.samples:
|
|
211
|
+
print(s.scene_id, s.reduced["task_success"])
|
|
212
|
+
# log.results.metrics["task_success"] is the mean of P̂ over instances — a
|
|
213
|
+
# convenience aggregate, NOT a methodology quantity (the methodology sorts P̂ into
|
|
214
|
+
# quantiles and fits pTQ / automation-halvings; out of scope here).
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The mock is abstract (it models *progress toward the scene goal*, like Inspect Robots's
|
|
218
|
+
`CubePick`) — its job is to exercise the pipeline and give you a template. **In the
|
|
219
|
+
mock, success depends only on the seeded goal direction, so the sampled setup
|
|
220
|
+
distributions have no causal effect** (P̂ is degenerately 1.0 for the scripted
|
|
221
|
+
oracle); the distribution *content* only bites on a real embodiment. The **value is
|
|
222
|
+
the task definitions**, which run unchanged on a real robot.
|
|
223
|
+
|
|
224
|
+
## Run it on real hardware (YAM arms + MolmoAct2)
|
|
225
|
+
|
|
226
|
+
KitchenBench tasks are embodiment-agnostic. To evaluate on real **YAM bimanual
|
|
227
|
+
arms** with **MolmoAct2**, provide two Inspect Robots components (e.g. in your own
|
|
228
|
+
adapter package such as `robocurve/embodiments`):
|
|
229
|
+
|
|
230
|
+
- a **`Policy`** wrapping MolmoAct2: `act(observation) -> ActionChunk` (the
|
|
231
|
+
scene's `instruction` is fed to the VLA verbatim);
|
|
232
|
+
- an **`Embodiment`** for the YAM arms: `reset`/`step`/`close`, declaring its
|
|
233
|
+
action space (e.g. two 7-DoF arms + grippers) and cameras. Because there is no
|
|
234
|
+
privileged success oracle, the embodiment should turn the **operator's
|
|
235
|
+
confirmation** at episode end into `StepResult(terminated=True,
|
|
236
|
+
termination_reason="success")` (or set `record.operator_judgement`) —
|
|
237
|
+
KitchenBench's `task_success` scorer reads either. Declare the `"self_paced"`
|
|
238
|
+
capability and pace the control loop inside `step()`.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
inspect-robots run --task kitchenbench/pour_pasta --policy molmoact2 --embodiment yam_arms
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Inspect Robots checks `(policy, embodiment)` compatibility (action dims, semantics,
|
|
245
|
+
camera/state keys) before any motion and writes an immutable `EvalLog`.
|
|
246
|
+
|
|
247
|
+
## Development
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
uv venv && uv pip install -e ".[dev]" # inspect_robots resolved from the v0.3.0 tag
|
|
251
|
+
uv run pre-commit install
|
|
252
|
+
uv run pytest --cov # 100% coverage required
|
|
253
|
+
uv run ruff check . && uv run mypy
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Citation
|
|
257
|
+
|
|
258
|
+
If you use KitchenBench in your research, please cite it:
|
|
259
|
+
|
|
260
|
+
```bibtex
|
|
261
|
+
@software{kitchenbench,
|
|
262
|
+
author = {Robocurve},
|
|
263
|
+
title = {KitchenBench: A bimanual kitchen-manipulation benchmark for VLA models},
|
|
264
|
+
year = {2026},
|
|
265
|
+
url = {https://github.com/robocurve/kitchenbench},
|
|
266
|
+
version = {0.3.0},
|
|
267
|
+
license = {MIT}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
kitchenbench/CLAUDE.md,sha256=m0osDadTTON6oxz4TojcB7678wYLOYEKl29YpChkjYw,4891
|
|
2
|
+
kitchenbench/__init__.py,sha256=AVrD5TzUPYAC4ZRcSS03fM9KbGQftlWAsNLCFlhHL-Q,2166
|
|
3
|
+
kitchenbench/distributions.py,sha256=GFcOTgdqnSUWLGEhegtNo_dlmjpYRx1yHVTLThL_Mt8,3992
|
|
4
|
+
kitchenbench/embodiment.py,sha256=zLVlFRrNAFnN8_1ethQsR_KzVejbZFK1f5DP-k6AoJQ,5327
|
|
5
|
+
kitchenbench/instances.py,sha256=tYyuyG3Dqwmiv4o-KLYxqw71AVd23jIKW7Tox_TSnn0,3352
|
|
6
|
+
kitchenbench/policies.py,sha256=ecA8R8ITDX8Rio9Fl7lx6I-YvERjqohFli-rUXhI4ZQ,3398
|
|
7
|
+
kitchenbench/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
kitchenbench/scoring.py,sha256=qvBjN1HYII9Zer_JMTjaOMDVqOE-H6THAlFbTnpa6U8,1454
|
|
9
|
+
kitchenbench/specs.py,sha256=iZmQPGvpmOsFBkEHtclZYYu4w1dJLueovQRqQtfxXhA,22984
|
|
10
|
+
kitchenbench/tasks.py,sha256=JdeQ00aMn7zbjlFs52yg-yRGkoKdyueeNVrgVtHGXWk,5833
|
|
11
|
+
kitchenbench-0.3.0.dist-info/METADATA,sha256=EMptRr_HscTknBdjDZKmwKZ4knVxdP2sY7yVXlPtG-o,12294
|
|
12
|
+
kitchenbench-0.3.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
13
|
+
kitchenbench-0.3.0.dist-info/entry_points.txt,sha256=g2yDnRT9agBRkx_3P28FbkrI8mp77UuyvNV-S_0fJfU,887
|
|
14
|
+
kitchenbench-0.3.0.dist-info/licenses/LICENSE,sha256=NaimrZjfDyMR93d5rbHzWsYQ4UrnVsxgIXZui2UHFjU,1066
|
|
15
|
+
kitchenbench-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[inspect_robots.embodiments]
|
|
2
|
+
kitchen = kitchenbench.embodiment:KitchenEmbodiment
|
|
3
|
+
|
|
4
|
+
[inspect_robots.policies]
|
|
5
|
+
kitchen_noop = kitchenbench.policies:NoopKitchenPolicy
|
|
6
|
+
kitchen_random = kitchenbench.policies:RandomKitchenPolicy
|
|
7
|
+
kitchen_scripted = kitchenbench.policies:ScriptedKitchenPolicy
|
|
8
|
+
|
|
9
|
+
[inspect_robots.tasks]
|
|
10
|
+
kitchenbench/fold_cloth = kitchenbench.tasks:fold_cloth
|
|
11
|
+
kitchenbench/handoff = kitchenbench.tasks:handoff
|
|
12
|
+
kitchenbench/open_container = kitchenbench.tasks:open_container
|
|
13
|
+
kitchenbench/place_cutlery = kitchenbench.tasks:place_cutlery
|
|
14
|
+
kitchenbench/place_in_rack = kitchenbench.tasks:place_in_rack
|
|
15
|
+
kitchenbench/pour_pasta = kitchenbench.tasks:pour_pasta
|
|
16
|
+
kitchenbench/scoop_pasta = kitchenbench.tasks:scoop_pasta
|
|
17
|
+
kitchenbench/seal_container = kitchenbench.tasks:seal_container
|
|
18
|
+
kitchenbench/sort_cutlery = kitchenbench.tasks:sort_cutlery
|
|
19
|
+
kitchenbench/stack = kitchenbench.tasks:stack
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 robocurve
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|