positronic-evals 0.1.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.
- positronic_evals-0.1.0/.gitignore +167 -0
- positronic_evals-0.1.0/PKG-INFO +108 -0
- positronic_evals-0.1.0/README.md +97 -0
- positronic_evals-0.1.0/positronic_evals/__init__.py +0 -0
- positronic_evals-0.1.0/positronic_evals/evals.py +107 -0
- positronic_evals-0.1.0/positronic_evals/labels.py +589 -0
- positronic_evals-0.1.0/positronic_evals/molmo_spaces.py +18 -0
- positronic_evals-0.1.0/positronic_evals/py.typed +0 -0
- positronic_evals-0.1.0/positronic_evals/right_view_blocked.json +190 -0
- positronic_evals-0.1.0/positronic_evals/scoring.py +108 -0
- positronic_evals-0.1.0/positronic_evals/slug.py +57 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000000/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000000/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000000/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000001/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000001/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000001/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000002/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000002/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000002/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000003/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000003/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000003/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000004/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000004/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000004/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000005/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000005/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000005/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000006/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000006/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000006/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000007/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000007/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000007/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000008/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000008/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000008/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000009/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000009/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000009/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000010/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000010/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000010/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000011/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000011/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000011/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000012/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000012/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000012/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000013/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000013/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000013/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000014/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000014/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000014/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000015/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000015/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000015/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000016/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000016/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000016/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000017/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000017/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000017/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000018/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000018/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000018/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000019/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000019/robot_state.q.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/molmo_sweep/000000000000/000000000019/static.json +68 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000000/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000000/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000000/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000001/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000001/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000001/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000002/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000002/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000002/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000003/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000003/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000003/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000004/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000004/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000004/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000005/meta.json +5 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000005/static.json +76 -0
- positronic_evals-0.1.0/positronic_evals/tests/fixtures/robolab_sweep/000000000000/000000000005/subtask.parquet +0 -0
- positronic_evals-0.1.0/positronic_evals/tests/test_evals.py +94 -0
- positronic_evals-0.1.0/positronic_evals/tests/test_labels.py +701 -0
- positronic_evals-0.1.0/positronic_evals/tests/test_molmo_spaces.py +22 -0
- positronic_evals-0.1.0/positronic_evals/tests/test_scoring.py +228 -0
- positronic_evals-0.1.0/positronic_evals/tests/test_slug.py +67 -0
- positronic_evals-0.1.0/pyproject.toml +27 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
|
|
112
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
113
|
+
__pypackages__/
|
|
114
|
+
|
|
115
|
+
# Celery stuff
|
|
116
|
+
celerybeat-schedule
|
|
117
|
+
celerybeat.pid
|
|
118
|
+
|
|
119
|
+
# SageMath parsed files
|
|
120
|
+
*.sage.py
|
|
121
|
+
|
|
122
|
+
# Environments
|
|
123
|
+
.env
|
|
124
|
+
.venv
|
|
125
|
+
.venv-typecheck
|
|
126
|
+
env/
|
|
127
|
+
venv/
|
|
128
|
+
ENV/
|
|
129
|
+
env.bak/
|
|
130
|
+
venv.bak/
|
|
131
|
+
|
|
132
|
+
# Spyder project settings
|
|
133
|
+
.spyderproject
|
|
134
|
+
.spyproject
|
|
135
|
+
|
|
136
|
+
# Rope project settings
|
|
137
|
+
.ropeproject
|
|
138
|
+
|
|
139
|
+
# mkdocs documentation
|
|
140
|
+
/site
|
|
141
|
+
|
|
142
|
+
# mypy
|
|
143
|
+
.mypy_cache/
|
|
144
|
+
.dmypy.json
|
|
145
|
+
dmypy.json
|
|
146
|
+
|
|
147
|
+
# Pyre type checker
|
|
148
|
+
.pyre/
|
|
149
|
+
|
|
150
|
+
# pytype static type analyzer
|
|
151
|
+
.pytype/
|
|
152
|
+
|
|
153
|
+
# Cython debug symbols
|
|
154
|
+
cython_debug/
|
|
155
|
+
|
|
156
|
+
# PyCharm
|
|
157
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
158
|
+
# be added to the global gitignore or merged into this project gitignore. For a PyCharm
|
|
159
|
+
# project, it is generally recommended to exclude comment-based line additions.
|
|
160
|
+
.idea/
|
|
161
|
+
|
|
162
|
+
# VSCode
|
|
163
|
+
.vscode/
|
|
164
|
+
*.code-workspace
|
|
165
|
+
|
|
166
|
+
# Environment variables
|
|
167
|
+
.env*
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: positronic-evals
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The public evals of the Positronic platform: their definitions, labelers, scoring rules and episode lists
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: positronic-eval-vocabulary==0.4.0
|
|
8
|
+
Requires-Dist: pyarrow
|
|
9
|
+
Requires-Dist: pydantic>=2.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# positronic-evals
|
|
13
|
+
|
|
14
|
+
The definitions of the public evals, the labelers that read a recorded episode, the rules that score
|
|
15
|
+
a run, and MolmoSpaces' committed episode lists.
|
|
16
|
+
|
|
17
|
+
`positronic` runs these evals on your machine, `positronic-platform-client` names them on the wire,
|
|
18
|
+
and a service that scores a run reads them. Each of them installs this distribution. It depends on
|
|
19
|
+
`pydantic`, `pyarrow` and `positronic-eval-vocabulary`, and on no robotics stack and no client, so a
|
|
20
|
+
service installs it alone:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv add "positronic-evals @ git+https://github.com/Positronic-Robotics/positronic@<tag or commit>#subdirectory=evals"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`positronic_evals.slug` carries an `IntEnum` on the wire as its lowercase name.
|
|
27
|
+
|
|
28
|
+
## Public evals
|
|
29
|
+
|
|
30
|
+
`positronic_evals.evals` carries the definitions of the public evals. A definition pins what the
|
|
31
|
+
platform runs under the eval's name: the positronic eval config and its arguments, the trials, the
|
|
32
|
+
time limit of one run, its scoring, and the positronic commit that the platform runs the config at.
|
|
33
|
+
`public_eval(name)` returns one definition, and `PUBLIC_EVALS` names them all. The platform also
|
|
34
|
+
offers evals that it does not publish, and `public_eval` refuses their names.
|
|
35
|
+
|
|
36
|
+
`positronic eval run` with `--policy` takes a public eval's name as `--eval`, and runs that eval on
|
|
37
|
+
your machine. The run uses the positronic you installed. It warns when that positronic is not the
|
|
38
|
+
commit the definition names, because the two runs can then differ. A local run does not stop at the
|
|
39
|
+
time limit.
|
|
40
|
+
|
|
41
|
+
## Labels and scores
|
|
42
|
+
|
|
43
|
+
`positronic_evals.labels` reads what happened in a recorded episode. A labeler reads one episode
|
|
44
|
+
directory and gives its `Label`: the episode's id and task, its end, and a list of timed events. An
|
|
45
|
+
event is a stage that the arm reached, a count of items delivered, a count of subtasks achieved, or
|
|
46
|
+
one of the three end events: `Success`, `Fail` and `Safety`. The earliest end event gives the
|
|
47
|
+
`outcome`. A label with no end event ran out of time. The label's other views, such as
|
|
48
|
+
`stage_seconds` and `placements`, are properties computed from its events. `Label.until(seconds)`
|
|
49
|
+
gives the label as it stood at that time.
|
|
50
|
+
|
|
51
|
+
`positronic_evals.scoring` states what a label is worth. An eval's `Scoring` names its labelers in
|
|
52
|
+
order of precedence, and its primary metric. `label(episode, scoring.labelers)` gives a pair: the
|
|
53
|
+
first labeler that labels the episode, and its label. It gives None when no labeler labels the
|
|
54
|
+
episode. `score(scoring, labels)` takes the label of each episode, or None. For the episode
|
|
55
|
+
directories `episodes` of one run:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from positronic_evals.evals import EvalRef, public_eval
|
|
59
|
+
from positronic_evals.labels import label
|
|
60
|
+
from positronic_evals.scoring import score
|
|
61
|
+
|
|
62
|
+
scoring = public_eval(EvalRef('molmo.franka_pick_mini')).scoring
|
|
63
|
+
picked = [label(episode, scoring.labelers) for episode in episodes]
|
|
64
|
+
scores = score(scoring, [None if given is None else given[1] for given in picked])
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`score` gives `Scores`, which holds three numbers. `primary` is the mean episode score, or the mean
|
|
68
|
+
of the task means where the metric sets `per_task`, and a board ranks on it. `episodes` counts every
|
|
69
|
+
episode, and `unscored` counts the episodes that no labeler labelled. `primary` does not count those
|
|
70
|
+
as failures. No file holds a score: a score is a function of the labels.
|
|
71
|
+
|
|
72
|
+
`PUBLIC_LABELERS` holds the labelers that this package publishes, `molmo` and `robolab`. A caller with
|
|
73
|
+
labelers of its own passes `label` a table that holds them beside `PUBLIC_LABELERS`. The MolmoSpaces
|
|
74
|
+
labeler takes the end of a trial from the episode's signal files: the time from the first sample to
|
|
75
|
+
the last, on the world clock. That time can exceed the trial by up to one control step.
|
|
76
|
+
|
|
77
|
+
The RoboLab labeler reads the end of a trial in the same way. Its task is the task id that RoboLab
|
|
78
|
+
names, and not the instruction. It also reads the `subtask` signal, which RoboLab records at each
|
|
79
|
+
step, and gives a `SubtaskAchieved` event, with RoboLab's score, for each change of the subtasks
|
|
80
|
+
completed. A change of the score alone is not an event. A RoboLab eval scores a success 1.0, and any
|
|
81
|
+
other episode the score at its last change of the count, or 0 where it completed no subtask. Where
|
|
82
|
+
RoboLab grades progress within a subtask, this score does not count that progress:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from positronic_evals.labels import ROBOLAB_LABELER
|
|
86
|
+
from positronic_evals.scoring import EpisodeScore, MeanEpisodeScore, Scoring
|
|
87
|
+
|
|
88
|
+
robolab = Scoring(
|
|
89
|
+
labelers=(ROBOLAB_LABELER,), primary=MeanEpisodeScore(episode_score=EpisodeScore(use_subtask_score=True))
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## MolmoSpaces episode lists
|
|
94
|
+
|
|
95
|
+
`positronic_evals.molmo_spaces` holds the episode lists that an offline sweep builds, by benchmark
|
|
96
|
+
path below the asset packs' `benchmarks` directory. `RIGHT_VIEW_BLOCKED_PATH` lists the episodes where
|
|
97
|
+
scene geometry hides the start grasp site from the right shoulder camera. The MolmoSpaces sim server
|
|
98
|
+
leaves those episodes out of an open selection and refuses a pinned one. No public eval pins one.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from positronic_evals.molmo_spaces import RIGHT_VIEW_BLOCKED_PATH, read_episode_list
|
|
102
|
+
|
|
103
|
+
blocked = read_episode_list(RIGHT_VIEW_BLOCKED_PATH)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A benchmark that a list does not name is unswept. Rebuild a list when the pinned MolmoSpaces revision
|
|
107
|
+
or the asset packs change. A new list goes beside `right_view_blocked.json`, with its path constant in
|
|
108
|
+
`positronic_evals.molmo_spaces`, and `read_episode_list` reads it.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# positronic-evals
|
|
2
|
+
|
|
3
|
+
The definitions of the public evals, the labelers that read a recorded episode, the rules that score
|
|
4
|
+
a run, and MolmoSpaces' committed episode lists.
|
|
5
|
+
|
|
6
|
+
`positronic` runs these evals on your machine, `positronic-platform-client` names them on the wire,
|
|
7
|
+
and a service that scores a run reads them. Each of them installs this distribution. It depends on
|
|
8
|
+
`pydantic`, `pyarrow` and `positronic-eval-vocabulary`, and on no robotics stack and no client, so a
|
|
9
|
+
service installs it alone:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv add "positronic-evals @ git+https://github.com/Positronic-Robotics/positronic@<tag or commit>#subdirectory=evals"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`positronic_evals.slug` carries an `IntEnum` on the wire as its lowercase name.
|
|
16
|
+
|
|
17
|
+
## Public evals
|
|
18
|
+
|
|
19
|
+
`positronic_evals.evals` carries the definitions of the public evals. A definition pins what the
|
|
20
|
+
platform runs under the eval's name: the positronic eval config and its arguments, the trials, the
|
|
21
|
+
time limit of one run, its scoring, and the positronic commit that the platform runs the config at.
|
|
22
|
+
`public_eval(name)` returns one definition, and `PUBLIC_EVALS` names them all. The platform also
|
|
23
|
+
offers evals that it does not publish, and `public_eval` refuses their names.
|
|
24
|
+
|
|
25
|
+
`positronic eval run` with `--policy` takes a public eval's name as `--eval`, and runs that eval on
|
|
26
|
+
your machine. The run uses the positronic you installed. It warns when that positronic is not the
|
|
27
|
+
commit the definition names, because the two runs can then differ. A local run does not stop at the
|
|
28
|
+
time limit.
|
|
29
|
+
|
|
30
|
+
## Labels and scores
|
|
31
|
+
|
|
32
|
+
`positronic_evals.labels` reads what happened in a recorded episode. A labeler reads one episode
|
|
33
|
+
directory and gives its `Label`: the episode's id and task, its end, and a list of timed events. An
|
|
34
|
+
event is a stage that the arm reached, a count of items delivered, a count of subtasks achieved, or
|
|
35
|
+
one of the three end events: `Success`, `Fail` and `Safety`. The earliest end event gives the
|
|
36
|
+
`outcome`. A label with no end event ran out of time. The label's other views, such as
|
|
37
|
+
`stage_seconds` and `placements`, are properties computed from its events. `Label.until(seconds)`
|
|
38
|
+
gives the label as it stood at that time.
|
|
39
|
+
|
|
40
|
+
`positronic_evals.scoring` states what a label is worth. An eval's `Scoring` names its labelers in
|
|
41
|
+
order of precedence, and its primary metric. `label(episode, scoring.labelers)` gives a pair: the
|
|
42
|
+
first labeler that labels the episode, and its label. It gives None when no labeler labels the
|
|
43
|
+
episode. `score(scoring, labels)` takes the label of each episode, or None. For the episode
|
|
44
|
+
directories `episodes` of one run:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from positronic_evals.evals import EvalRef, public_eval
|
|
48
|
+
from positronic_evals.labels import label
|
|
49
|
+
from positronic_evals.scoring import score
|
|
50
|
+
|
|
51
|
+
scoring = public_eval(EvalRef('molmo.franka_pick_mini')).scoring
|
|
52
|
+
picked = [label(episode, scoring.labelers) for episode in episodes]
|
|
53
|
+
scores = score(scoring, [None if given is None else given[1] for given in picked])
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`score` gives `Scores`, which holds three numbers. `primary` is the mean episode score, or the mean
|
|
57
|
+
of the task means where the metric sets `per_task`, and a board ranks on it. `episodes` counts every
|
|
58
|
+
episode, and `unscored` counts the episodes that no labeler labelled. `primary` does not count those
|
|
59
|
+
as failures. No file holds a score: a score is a function of the labels.
|
|
60
|
+
|
|
61
|
+
`PUBLIC_LABELERS` holds the labelers that this package publishes, `molmo` and `robolab`. A caller with
|
|
62
|
+
labelers of its own passes `label` a table that holds them beside `PUBLIC_LABELERS`. The MolmoSpaces
|
|
63
|
+
labeler takes the end of a trial from the episode's signal files: the time from the first sample to
|
|
64
|
+
the last, on the world clock. That time can exceed the trial by up to one control step.
|
|
65
|
+
|
|
66
|
+
The RoboLab labeler reads the end of a trial in the same way. Its task is the task id that RoboLab
|
|
67
|
+
names, and not the instruction. It also reads the `subtask` signal, which RoboLab records at each
|
|
68
|
+
step, and gives a `SubtaskAchieved` event, with RoboLab's score, for each change of the subtasks
|
|
69
|
+
completed. A change of the score alone is not an event. A RoboLab eval scores a success 1.0, and any
|
|
70
|
+
other episode the score at its last change of the count, or 0 where it completed no subtask. Where
|
|
71
|
+
RoboLab grades progress within a subtask, this score does not count that progress:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from positronic_evals.labels import ROBOLAB_LABELER
|
|
75
|
+
from positronic_evals.scoring import EpisodeScore, MeanEpisodeScore, Scoring
|
|
76
|
+
|
|
77
|
+
robolab = Scoring(
|
|
78
|
+
labelers=(ROBOLAB_LABELER,), primary=MeanEpisodeScore(episode_score=EpisodeScore(use_subtask_score=True))
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## MolmoSpaces episode lists
|
|
83
|
+
|
|
84
|
+
`positronic_evals.molmo_spaces` holds the episode lists that an offline sweep builds, by benchmark
|
|
85
|
+
path below the asset packs' `benchmarks` directory. `RIGHT_VIEW_BLOCKED_PATH` lists the episodes where
|
|
86
|
+
scene geometry hides the start grasp site from the right shoulder camera. The MolmoSpaces sim server
|
|
87
|
+
leaves those episodes out of an open selection and refuses a pinned one. No public eval pins one.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from positronic_evals.molmo_spaces import RIGHT_VIEW_BLOCKED_PATH, read_episode_list
|
|
91
|
+
|
|
92
|
+
blocked = read_episode_list(RIGHT_VIEW_BLOCKED_PATH)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A benchmark that a list does not name is unswept. Rebuild a list when the pinned MolmoSpaces revision
|
|
96
|
+
or the asset packs change. A new list goes beside `right_view_blocked.json`, with its path constant in
|
|
97
|
+
`positronic_evals.molmo_spaces`, and `read_episode_list` reads it.
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""The one name a caller picks a run by, and the definitions of the public evals.
|
|
2
|
+
|
|
3
|
+
A name is a task suite AND the embodiment that runs it, so there is no second axis to get wrong.
|
|
4
|
+
The platform owns the set, so a name that this package does not define still reaches the platform.
|
|
5
|
+
|
|
6
|
+
The platform holds the definitions of the evals it does not publish.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field, GetCoreSchemaHandler, JsonValue, PositiveInt
|
|
12
|
+
from pydantic_core import core_schema
|
|
13
|
+
|
|
14
|
+
from positronic_evals.labels import MOLMO_LABELER
|
|
15
|
+
from positronic_evals.scoring import MeanEpisodeScore, Scoring
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EvalRef(str):
|
|
19
|
+
"""The name of an eval the platform offers, as a validated `str`."""
|
|
20
|
+
|
|
21
|
+
__slots__ = ()
|
|
22
|
+
|
|
23
|
+
def __new__(cls, value: str) -> EvalRef:
|
|
24
|
+
if not value or any(c.isspace() for c in value):
|
|
25
|
+
raise ValueError(f'not an eval name: {value!r}')
|
|
26
|
+
return super().__new__(cls, value)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def __get_pydantic_core_schema__(cls, source: type, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
|
|
30
|
+
return core_schema.no_info_after_validator_function(cls, core_schema.str_schema())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_DEFINITION_CONFIG = ConfigDict(extra='forbid', frozen=True)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EvalTask(BaseModel):
|
|
37
|
+
"""One task of an eval, and its trials in run order."""
|
|
38
|
+
|
|
39
|
+
model_config = _DEFINITION_CONFIG
|
|
40
|
+
|
|
41
|
+
name: str = Field(min_length=1)
|
|
42
|
+
# Each trial's initial condition, in the keys of the config that runs it.
|
|
43
|
+
trials: list[dict[str, JsonValue]] = Field(min_length=1)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class EvalDefinition(BaseModel):
|
|
47
|
+
"""What a run of a named eval runs."""
|
|
48
|
+
|
|
49
|
+
model_config = _DEFINITION_CONFIG
|
|
50
|
+
|
|
51
|
+
# The positronic eval config, as `positronic eval run --eval=` takes it.
|
|
52
|
+
config: str = Field(pattern=r'^\.[A-Za-z_][A-Za-z0-9_.]*$')
|
|
53
|
+
# The config's `--eval.<name>=` overrides, which expand to `tasks`.
|
|
54
|
+
args: dict[str, JsonValue]
|
|
55
|
+
tasks: list[EvalTask] = Field(min_length=1)
|
|
56
|
+
# The wall-clock limit of one platform run. A local run does not stop at it.
|
|
57
|
+
time_limit_s: PositiveInt
|
|
58
|
+
scoring: Scoring
|
|
59
|
+
# The positronic commit the platform runs the config at.
|
|
60
|
+
positronic_revision: str = Field(pattern=r'^[0-9a-f]{40}$')
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# The MolmoSpaces evals: the config they name, its task and trial key as positronic spells them
|
|
64
|
+
# (`positronic/simulator/molmo_spaces/keys.py`), their scoring, and the positronic commit the platform's sim
|
|
65
|
+
# image holds. A run ranks on its success rate.
|
|
66
|
+
MOLMO_CONFIG = '.sim.molmo.benchmarks'
|
|
67
|
+
MOLMO_TASK = 'franka_pick_droid_mini'
|
|
68
|
+
MOLMO_EPISODE_INDEX_KEY = 'molmo.episode_index'
|
|
69
|
+
MOLMO_SCORING = Scoring(labelers=(MOLMO_LABELER,), primary=MeanEpisodeScore())
|
|
70
|
+
MOLMO_POSITRONIC_REVISION = 'a9e13e8a1fbe624ff62b49e25628b39859619986'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _franka_pick_mini(episodes: list[int]) -> EvalDefinition:
|
|
74
|
+
"""`episodes` of the one benchmark the MolmoSpaces evals run, once each."""
|
|
75
|
+
return EvalDefinition(
|
|
76
|
+
config=MOLMO_CONFIG,
|
|
77
|
+
# Each key must name a parameter of positronic's `benchmarks` config.
|
|
78
|
+
args={
|
|
79
|
+
'suite': 'molmospaces-bench-v2',
|
|
80
|
+
'scene_dataset': 'procthor-10k',
|
|
81
|
+
'task_config': 'FrankaPickDroidMiniBench',
|
|
82
|
+
'benchmark': 'FrankaPickDroidMiniBench_json_benchmark_20251231',
|
|
83
|
+
'episodes': list(episodes),
|
|
84
|
+
'trial_count': 1,
|
|
85
|
+
},
|
|
86
|
+
tasks=[EvalTask(name=MOLMO_TASK, trials=[{MOLMO_EPISODE_INDEX_KEY: i} for i in episodes])],
|
|
87
|
+
time_limit_s=7200,
|
|
88
|
+
scoring=MOLMO_SCORING,
|
|
89
|
+
positronic_revision=MOLMO_POSITRONIC_REVISION,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_PUBLIC_EVALS: dict[EvalRef, EvalDefinition] = {
|
|
94
|
+
# Episode 6 hides the start grasp site from the right shoulder camera; the sim server refuses it.
|
|
95
|
+
EvalRef('molmo.franka_pick_mini'): _franka_pick_mini([*range(6), *range(7, 21)]),
|
|
96
|
+
EvalRef('molmo.franka_pick_mini_smoke'): _franka_pick_mini([0, 1, 2, 3, 4]),
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
PUBLIC_EVALS: tuple[EvalRef, ...] = tuple(_PUBLIC_EVALS)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def public_eval(name: EvalRef) -> EvalDefinition:
|
|
103
|
+
"""The definition of the public eval `name`, as a copy, or LookupError naming the public evals."""
|
|
104
|
+
definition = _PUBLIC_EVALS.get(name)
|
|
105
|
+
if definition is None:
|
|
106
|
+
raise LookupError(f'{name!r} is not a public eval; the public evals are {", ".join(PUBLIC_EVALS)}')
|
|
107
|
+
return definition.model_copy(deep=True)
|