Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ jobs:
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
- name: Install from uv.lock (dev extras)
run: uv sync --locked --python 3.11 --extra dev
- name: Install from uv.lock (dev + docs extras)
run: uv sync --locked --python 3.11 --extra dev --extra docs
- name: Ruff (lint)
run: uv run ruff check .
- name: Ruff (format check)
run: uv run ruff format --check .
- name: Mypy (strict)
run: uv run mypy
# Gated here because docs.yml's build job is not in ci-ok's needs list:
# a dead link that fails the strict docs build must not be mergeable.
- name: Docs build (strict)
run: uv run mkdocs build --strict

test:
name: test (${{ matrix.os }}, py${{ matrix.python-version }})
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Docs

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

# Allow the deploy job to publish to GitHub Pages.
permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
name: build docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # hatch-vcs needs full history/tags for versioning
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
- name: Install from uv.lock (docs extras)
run: uv sync --locked --python 3.12 --extra docs
- name: Build site (strict)
run: uv run mkdocs build --strict
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: site

deploy:
name: deploy to GitHub Pages
# Only deploy from the default branch; PRs just build.
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url || steps.retry1.outputs.page_url || steps.retry2.outputs.page_url }}
steps:
# The Pages API intermittently rejects deployments with a transient
# "Deployment failed, try again later" — retry twice with backoff.
- name: Deploy
id: deployment
continue-on-error: true
uses: actions/deploy-pages@v4
- name: Wait before retry
if: steps.deployment.outcome == 'failure'
run: sleep 30
- name: Deploy (retry 1)
id: retry1
if: steps.deployment.outcome == 'failure'
continue-on-error: true
uses: actions/deploy-pages@v4
- name: Wait before second retry
if: steps.deployment.outcome == 'failure' && steps.retry1.outcome == 'failure'
run: sleep 60
- name: Deploy (retry 2)
id: retry2
if: steps.deployment.outcome == 'failure' && steps.retry1.outcome == 'failure'
uses: actions/deploy-pages@v4
11 changes: 10 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,16 @@ success probability **P̂[Yᵢ=1]**. Instances are AI-authored drafts
task-instance/distribution model (read before changing the task set, the
distributions, or the 5×5 defaults).
- `reference/` — read-only local copy of the methodology PDFs (gitignored).
- `README.md` — the task table + how to run on the mock and on YAM/MolmoAct2.
- `README.md` — the task table (goal/bimanual/category) + how to run on the
mock and on YAM/MolmoAct2. The full "Varies over" task detail lives on the
docs site, not here.
- `docs/` + `mkdocs.yml` — the docs site
(https://robocurve.github.io/kitchenbench/, mkdocs-material, deployed from
main by `.github/workflows/docs.yml`; Pages source is "GitHub Actions").
`docs/index.md` holds the full task table including the "Varies over"
column; `tests/test_docs_table.py` is its drift guard against `specs.py`.
The strict docs build runs inside ci.yml's quality job (so it gates merges
via `ci-ok`) as well as in docs.yml. Docs deps are the `docs` extra.

## Working here (important gotchas)

Expand Down
40 changes: 19 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Built on [Inspect Robots](https://github.com/robocurve/inspect-robots) · part o

![Status: alpha](https://img.shields.io/badge/status-alpha-blue)
[![CI](https://github.com/robocurve/kitchenbench/actions/workflows/ci.yml/badge.svg)](https://github.com/robocurve/kitchenbench/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/badge/docs-task%20reference-indigo)](https://robocurve.github.io/kitchenbench/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/robocurve/kitchenbench/actions/workflows/ci.yml)
[![Built on Inspect Robots](https://img.shields.io/badge/built%20on-Inspect%20Robots-indigo)](https://github.com/robocurve/inspect-robots)
Expand All @@ -31,27 +32,24 @@ MolmoAct2).

## The tasks

| Task (`--task`) | Goal | Varies over | Bimanual | Category |
|---|---|---|:--:|---|
| `kitchenbench/place_cutlery` | place the {cutlery} on the {dishware} | {cutlery}: spoon, fork, knife · {dishware}: plate, bowl, napkin · placement, approach angle, clutter | | pick-place |
| `kitchenbench/stack` | stack the cups / bowls / plates | stacked item: cups, bowls, plates · count: 2 to 5 · sizes, spacing, placement jitter | | stacking |
| `kitchenbench/place_in_rack` | place the {dishware} into the dish rack | {dishware}: plate, bowl, cup · slot, rack pose, approach tilt, slot width, friction | | insertion |
| `kitchenbench/pour_pasta` | pour the dry pasta into the {vessel} | {vessel}: bowl, cup, pot · fill amount, pour height and angle, placement | ✅ | granular |
| `kitchenbench/open_container` | open the {container} | {container}: jar, bottle, food container · lid torque, cap turns, tilt, size | ✅ | articulated |
| `kitchenbench/fold_cloth` | fold the {cloth} | {cloth}: dish towel, napkin, cloth · size, fold count, crumple, rotation | ✅ | deformable |
| `kitchenbench/seal_container` | seal the {container} with its lid | {container}: food container, pot, jar · lid offset and yaw, press force, thread turns, fit clearance | ✅ | mating |
| `kitchenbench/handoff` | hand off the {item} from one arm to the other | {item}: utensil, cup, produce item · handoff height and position, fill level, fragility, tool length | ✅ | coordination |
| `kitchenbench/sort_cutlery` | sort the cutlery into the correct tray compartments | spoons, forks, knives · piece count: 3 to 10 · pile spread and overlap, tray pose | | classification |
| `kitchenbench/scoop_pasta` | scoop about {fill_target_g} g of the {pasta} with the {tool} and transfer it to the container | {pasta}: penne, rigatoni · {tool}: spoon, measuring cup · {fill_target_g}: 20 to 160 · pile depth, container distance | ✅ | granular+tool |

The "Varies over" column is the union over the task's 5 instances. Each
`{placeholder}` lists every value the goal sentence can name (an individual
instance samples from a subset of them and fixes the rest in its goal text);
the trailing items summarize the remaining setup variables (placement jitter,
sizes, forces, and the like) the instances also sample, without listing every
one. The full per-instance distributions live in
[`specs.py`](src/kitchenbench/specs.py), and `tests/test_readme_table.py`
keeps this table in sync with them.
| Task (`--task`) | Goal | Bimanual | Category |
|---|---|:--:|---|
| `kitchenbench/place_cutlery` | place the {cutlery} on the {dishware} | | pick-place |
| `kitchenbench/stack` | stack the cups / bowls / plates | | stacking |
| `kitchenbench/place_in_rack` | place the {dishware} into the dish rack | | insertion |
| `kitchenbench/pour_pasta` | pour the dry pasta into the {vessel} | ✅ | granular |
| `kitchenbench/open_container` | open the {container} | ✅ | articulated |
| `kitchenbench/fold_cloth` | fold the {cloth} | ✅ | deformable |
| `kitchenbench/seal_container` | seal the {container} with its lid | ✅ | mating |
| `kitchenbench/handoff` | hand off the {item} from one arm to the other | ✅ | coordination |
| `kitchenbench/sort_cutlery` | sort the cutlery into the correct tray compartments | | classification |
| `kitchenbench/scoop_pasta` | scoop about {fill_target_g} g of the {pasta} with the {tool} and transfer it to the container | ✅ | granular+tool |

> [!NOTE]
> Every value each task varies over (the objects a `{placeholder}` can name,
> counts, forces, placement jitter) is documented in the task reference on the
> [KitchenBench docs site](https://robocurve.github.io/kitchenbench/). The
> per-instance distributions live in [`specs.py`](src/kitchenbench/specs.py).

## Quick start

Expand Down
47 changes: 47 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# KitchenBench

KitchenBench is 10 kitchen-manipulation tasks expressed as
[Inspect Robots](https://github.com/robocurve/inspect-robots) `Task`s. They are
embodiment-agnostic, so you run them against any compatible policy/embodiment
pair. The set emphasizes bimanual coordination: pouring, lid removal, folding,
part-mating, a pure two-arm handover, and tool-mediated scooping, alongside
classic pick-place / stacking / slotted insertion and a multi-instance sort.

This page is the reference for what each task asks and everything it varies
over. For installation, the quick start, and running on real hardware or in
simulation, see the
[README on GitHub](https://github.com/robocurve/kitchenbench#readme).

## The tasks

| Task (`--task`) | Goal | Varies over | Bimanual | Category |
|---|---|---|:--:|---|
| `kitchenbench/place_cutlery` | place the {cutlery} on the {dishware} | {cutlery}: spoon, fork, knife · {dishware}: plate, bowl, napkin · placement, approach angle, clutter | | pick-place |
| `kitchenbench/stack` | stack the cups / bowls / plates | stacked item: cups, bowls, plates · count: 2 to 5 · sizes, spacing, placement jitter | | stacking |
| `kitchenbench/place_in_rack` | place the {dishware} into the dish rack | {dishware}: plate, bowl, cup · slot, rack pose, approach tilt, slot width, friction | | insertion |
| `kitchenbench/pour_pasta` | pour the dry pasta into the {vessel} | {vessel}: bowl, cup, pot · fill amount, pour height and angle, placement | ✅ | granular |
| `kitchenbench/open_container` | open the {container} | {container}: jar, bottle, food container · lid torque, cap turns, tilt, size | ✅ | articulated |
| `kitchenbench/fold_cloth` | fold the {cloth} | {cloth}: dish towel, napkin, cloth · size, fold count, crumple, rotation | ✅ | deformable |
| `kitchenbench/seal_container` | seal the {container} with its lid | {container}: food container, pot, jar · lid offset and yaw, press force, thread turns, fit clearance | ✅ | mating |
| `kitchenbench/handoff` | hand off the {item} from one arm to the other | {item}: utensil, cup, produce item · handoff height and position, fill level, fragility, tool length | ✅ | coordination |
| `kitchenbench/sort_cutlery` | sort the cutlery into the correct tray compartments | spoons, forks, knives · piece count: 3 to 10 · pile spread and overlap, tray pose | | classification |
| `kitchenbench/scoop_pasta` | scoop about {fill_target_g} g of the {pasta} with the {tool} and transfer it to the container | {pasta}: penne, rigatoni · {tool}: spoon, measuring cup · {fill_target_g}: 20 to 160 · pile depth, container distance | ✅ | granular+tool |

The "Varies over" column is the union over the task's 5 instances. Each
`{placeholder}` lists every value the goal sentence can name (an individual
instance samples from a subset of them and fixes the rest in its goal text);
the trailing items summarize the remaining setup variables (placement jitter,
sizes, forces, and the like) the instances also sample, without listing every
one. The full per-instance distributions live in
[`specs.py`](https://github.com/robocurve/kitchenbench/blob/main/src/kitchenbench/specs.py),
and `tests/test_docs_table.py` keeps this table in sync with them.

## Where things live

- [GitHub repository](https://github.com/robocurve/kitchenbench): source,
quick start, hardware and simulation guides
- [PyPI package](https://pypi.org/project/kitchenbench/): `pip install kitchenbench`
- [WorldEvals](https://github.com/robocurve/worldevals): the benchmark
collection KitchenBench belongs to
- [Physical-automation methodology](https://github.com/jeqcho/physical-automation-methodology-docs):
the instance/realization model the tasks follow
57 changes: 57 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
site_name: KitchenBench
site_description: >-
A bimanual kitchen-manipulation benchmark for VLA models, built on
Inspect Robots.
site_url: https://robocurve.github.io/kitchenbench/
repo_url: https://github.com/robocurve/kitchenbench
repo_name: robocurve/kitchenbench
edit_uri: edit/main/docs/
copyright: Copyright © KitchenBench contributors

theme:
name: material
icon:
logo: material/silverware-fork-knife
repo: fontawesome/brands/github
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/weather-night
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/weather-sunny
name: Switch to light mode
features:
- navigation.instant
- navigation.tracking
- navigation.top
- navigation.footer
- toc.follow
- search.suggest
- search.highlight
- content.code.copy
- content.action.edit

markdown_extensions:
- admonition
- attr_list
- tables
- toc:
permalink: true
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.inlinehilite
- pymdownx.superfences

plugins:
- search

nav:
- Home: index.md
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ dev = [
"mypy>=1.11",
"pre-commit>=3.5",
]
docs = [
# <2: MkDocs 2.0 removes the plugin/theming systems with no migration path
# (see the Material team's 2026-02 notice); a lock upgrade must not pull it.
"mkdocs>=1.6,<2",
"mkdocs-material>=9.5",
]

[project.urls]
Homepage = "https://github.com/robocurve/kitchenbench"
Documentation = "https://robocurve.github.io/kitchenbench/"
Collection = "https://github.com/robocurve/worldevals"
Framework = "https://github.com/robocurve/inspect-robots"

Expand Down
19 changes: 10 additions & 9 deletions tests/test_readme_table.py → tests/test_docs_table.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""The README task table must stay in sync with the specs.
"""The docs-site task table must stay in sync with the specs.

The table's "Varies over" column shows, for every goal placeholder, the
concrete values the specs sample (the union over the task's 5 instances).
This guard keeps the table honest when instances are revised: every
categorical value a language var can take must appear in that task's row.
The table's "Varies over" column (docs/index.md, the docs home page) shows,
for every goal placeholder, the concrete values the specs sample (the union
over the task's 5 instances). This guard keeps the table honest when
instances are revised: every categorical value a language var can take must
appear in that task's row.
"""

from pathlib import Path
Expand All @@ -13,21 +14,21 @@
from kitchenbench import SPEC_BY_KEY
from kitchenbench.distributions import Categorical

README = Path(__file__).parent.parent / "README.md"
DOCS_HOME = Path(__file__).parent.parent / "docs" / "index.md"


def _table_row(key: str) -> str:
rows = [
line
for line in README.read_text().splitlines()
for line in DOCS_HOME.read_text().splitlines()
if line.startswith(f"| `kitchenbench/{key}`")
]
assert len(rows) == 1, f"expected exactly one task-table row for {key}"
return rows[0]


@pytest.mark.parametrize("key", SPEC_BY_KEY)
def test_readme_row_names_every_language_var_value(key: str) -> None:
def test_docs_row_names_every_language_var_value(key: str) -> None:
row = _table_row(key)
for inst in SPEC_BY_KEY[key].instances:
for var in inst.language_vars:
Expand All @@ -36,6 +37,6 @@ def test_readme_row_names_every_language_var_value(key: str) -> None:
continue
for value in dist.values:
assert str(value) in row, (
f"README row for {key} does not mention {var}={value!r} "
f"docs/index.md row for {key} does not mention {var}={value!r} "
f"(sampled by {inst.instance_id})"
)
Loading
Loading