bohrin 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.
- bohrin-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +71 -0
- bohrin-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- bohrin-0.1.0/.github/ISSUE_TEMPLATE/false_positive.yml +79 -0
- bohrin-0.1.0/.github/pull_request_template.md +21 -0
- bohrin-0.1.0/.github/workflows/ci.yml +92 -0
- bohrin-0.1.0/.github/workflows/release.yml +99 -0
- bohrin-0.1.0/.gitignore +35 -0
- bohrin-0.1.0/CHANGELOG.md +67 -0
- bohrin-0.1.0/CODE_OF_CONDUCT.md +132 -0
- bohrin-0.1.0/CONTRIBUTING.md +100 -0
- bohrin-0.1.0/LICENSE +202 -0
- bohrin-0.1.0/NOTICE +4 -0
- bohrin-0.1.0/PKG-INFO +250 -0
- bohrin-0.1.0/README.md +191 -0
- bohrin-0.1.0/SECURITY.md +55 -0
- bohrin-0.1.0/pyproject.toml +216 -0
- bohrin-0.1.0/scripts/hub_smoke.py +129 -0
- bohrin-0.1.0/src/bohrin/__init__.py +37 -0
- bohrin-0.1.0/src/bohrin/_arrays.py +26 -0
- bohrin-0.1.0/src/bohrin/_compat.py +39 -0
- bohrin-0.1.0/src/bohrin/_plugins.py +39 -0
- bohrin-0.1.0/src/bohrin/adapters/__init__.py +21 -0
- bohrin-0.1.0/src/bohrin/adapters/_arraysource.py +198 -0
- bohrin-0.1.0/src/bohrin/adapters/_mapping.py +213 -0
- bohrin-0.1.0/src/bohrin/adapters/_video.py +157 -0
- bohrin-0.1.0/src/bohrin/adapters/base.py +89 -0
- bohrin-0.1.0/src/bohrin/adapters/hdf5.py +205 -0
- bohrin-0.1.0/src/bohrin/adapters/lerobot.py +499 -0
- bohrin-0.1.0/src/bohrin/adapters/numpy_dir.py +107 -0
- bohrin-0.1.0/src/bohrin/adapters/registry.py +97 -0
- bohrin-0.1.0/src/bohrin/adapters/rlds.py +224 -0
- bohrin-0.1.0/src/bohrin/adapters/zarr_replay.py +134 -0
- bohrin-0.1.0/src/bohrin/analysis/__init__.py +45 -0
- bohrin-0.1.0/src/bohrin/analysis/confident_learning.py +72 -0
- bohrin-0.1.0/src/bohrin/analysis/embeddings.py +135 -0
- bohrin-0.1.0/src/bohrin/analysis/neighbors.py +171 -0
- bohrin-0.1.0/src/bohrin/analysis/robust.py +70 -0
- bohrin-0.1.0/src/bohrin/analysis/shapes.py +186 -0
- bohrin-0.1.0/src/bohrin/analysis/twosample.py +110 -0
- bohrin-0.1.0/src/bohrin/api.py +86 -0
- bohrin-0.1.0/src/bohrin/bench/__init__.py +18 -0
- bohrin-0.1.0/src/bohrin/bench/harness.py +166 -0
- bohrin-0.1.0/src/bohrin/calibrate/__init__.py +7 -0
- bohrin-0.1.0/src/bohrin/calibrate/collect.py +131 -0
- bohrin-0.1.0/src/bohrin/calibrate/conformal.py +111 -0
- bohrin-0.1.0/src/bohrin/calibrate/corpus.py +203 -0
- bohrin-0.1.0/src/bohrin/calibrate/dynamics_model.py +177 -0
- bohrin-0.1.0/src/bohrin/calibrate/fdr.py +68 -0
- bohrin-0.1.0/src/bohrin/calibrate/gate.py +229 -0
- bohrin-0.1.0/src/bohrin/cli.py +422 -0
- bohrin-0.1.0/src/bohrin/config.py +114 -0
- bohrin-0.1.0/src/bohrin/detectors/__init__.py +8 -0
- bohrin-0.1.0/src/bohrin/detectors/_common.py +166 -0
- bohrin-0.1.0/src/bohrin/detectors/base.py +118 -0
- bohrin-0.1.0/src/bohrin/detectors/causal.py +189 -0
- bohrin-0.1.0/src/bohrin/detectors/consistency.py +212 -0
- bohrin-0.1.0/src/bohrin/detectors/coverage.py +364 -0
- bohrin-0.1.0/src/bohrin/detectors/dynamics.py +207 -0
- bohrin-0.1.0/src/bohrin/detectors/integrity.py +475 -0
- bohrin-0.1.0/src/bohrin/detectors/kinematics.py +467 -0
- bohrin-0.1.0/src/bohrin/detectors/label.py +182 -0
- bohrin-0.1.0/src/bohrin/detectors/multimodality.py +200 -0
- bohrin-0.1.0/src/bohrin/detectors/policy_data.py +344 -0
- bohrin-0.1.0/src/bohrin/detectors/registry.py +63 -0
- bohrin-0.1.0/src/bohrin/detectors/scale.py +200 -0
- bohrin-0.1.0/src/bohrin/detectors/smoothness.py +177 -0
- bohrin-0.1.0/src/bohrin/detectors/stats.py +235 -0
- bohrin-0.1.0/src/bohrin/detectors/temporal.py +378 -0
- bohrin-0.1.0/src/bohrin/detectors/vision.py +418 -0
- bohrin-0.1.0/src/bohrin/encoders/__init__.py +31 -0
- bohrin-0.1.0/src/bohrin/encoders/base.py +35 -0
- bohrin-0.1.0/src/bohrin/encoders/dino.py +82 -0
- bohrin-0.1.0/src/bohrin/encoders/tiled.py +49 -0
- bohrin-0.1.0/src/bohrin/engine.py +173 -0
- bohrin-0.1.0/src/bohrin/hub.py +111 -0
- bohrin-0.1.0/src/bohrin/ir/__init__.py +51 -0
- bohrin-0.1.0/src/bohrin/ir/episode.py +133 -0
- bohrin-0.1.0/src/bohrin/ir/schema.py +180 -0
- bohrin-0.1.0/src/bohrin/policy/__init__.py +9 -0
- bohrin-0.1.0/src/bohrin/policy/loader.py +321 -0
- bohrin-0.1.0/src/bohrin/policy/target.py +56 -0
- bohrin-0.1.0/src/bohrin/profile/__init__.py +14 -0
- bohrin-0.1.0/src/bohrin/profile/action_space.py +132 -0
- bohrin-0.1.0/src/bohrin/profile/dataset_profile.py +224 -0
- bohrin-0.1.0/src/bohrin/profile/episode_reservoir.py +130 -0
- bohrin-0.1.0/src/bohrin/profile/online.py +206 -0
- bohrin-0.1.0/src/bohrin/py.typed +0 -0
- bohrin-0.1.0/src/bohrin/report/__init__.py +31 -0
- bohrin-0.1.0/src/bohrin/report/base.py +22 -0
- bohrin-0.1.0/src/bohrin/report/html.py +201 -0
- bohrin-0.1.0/src/bohrin/report/messages.py +134 -0
- bohrin-0.1.0/src/bohrin/report/model.py +222 -0
- bohrin-0.1.0/src/bohrin/report/sarif.py +231 -0
- bohrin-0.1.0/src/bohrin/report/tty.py +101 -0
- bohrin-0.1.0/src/bohrin/synth/__init__.py +21 -0
- bohrin-0.1.0/src/bohrin/synth/pipeline.py +198 -0
- bohrin-0.1.0/src/bohrin/version.py +17 -0
- bohrin-0.1.0/tests/_synth.py +897 -0
- bohrin-0.1.0/tests/test_accuracy_research.py +116 -0
- bohrin-0.1.0/tests/test_adapters_p3.py +246 -0
- bohrin-0.1.0/tests/test_advanced.py +134 -0
- bohrin-0.1.0/tests/test_bench_harness.py +125 -0
- bohrin-0.1.0/tests/test_benchmark.py +498 -0
- bohrin-0.1.0/tests/test_calibration_gate.py +472 -0
- bohrin-0.1.0/tests/test_catalogue_honesty.py +124 -0
- bohrin-0.1.0/tests/test_cli.py +91 -0
- bohrin-0.1.0/tests/test_cli_errors.py +137 -0
- bohrin-0.1.0/tests/test_config.py +48 -0
- bohrin-0.1.0/tests/test_corpus_p4.py +85 -0
- bohrin-0.1.0/tests/test_corrupt_data_robustness.py +199 -0
- bohrin-0.1.0/tests/test_determinism.py +17 -0
- bohrin-0.1.0/tests/test_dod.py +174 -0
- bohrin-0.1.0/tests/test_episode_reservoir.py +169 -0
- bohrin-0.1.0/tests/test_faults.py +168 -0
- bohrin-0.1.0/tests/test_faults_p2.py +238 -0
- bohrin-0.1.0/tests/test_faults_p3.py +236 -0
- bohrin-0.1.0/tests/test_hub.py +97 -0
- bohrin-0.1.0/tests/test_lag_conventions.py +298 -0
- bohrin-0.1.0/tests/test_lerobot.py +228 -0
- bohrin-0.1.0/tests/test_optional_backends.py +287 -0
- bohrin-0.1.0/tests/test_pipeline.py +39 -0
- bohrin-0.1.0/tests/test_policy_p3.py +193 -0
- bohrin-0.1.0/tests/test_profile.py +39 -0
- bohrin-0.1.0/tests/test_registry.py +73 -0
- bohrin-0.1.0/tests/test_rlds_adapter.py +305 -0
- bohrin-0.1.0/tests/test_roundtrip.py +34 -0
- bohrin-0.1.0/tests/test_score.py +257 -0
- bohrin-0.1.0/tests/test_streaming_scale.py +213 -0
- bohrin-0.1.0/tests/test_triage.py +66 -0
- bohrin-0.1.0/tests/test_trust_p4.py +202 -0
- bohrin-0.1.0/tests/test_video_lerobot.py +313 -0
- bohrin-0.1.0/uv.lock +3930 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Bohrin crashed, failed to read a dataset, or did something clearly wrong.
|
|
3
|
+
title: "[bug] "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for reporting. The three version fields below are required because without
|
|
10
|
+
them almost every bug report is unactionable — we cannot tell a fixed bug from a
|
|
11
|
+
live one, or a format difference from a code difference.
|
|
12
|
+
|
|
13
|
+
**If bohrin flagged something that is actually fine**, that is a false positive, not
|
|
14
|
+
a bug — please use the *False positive* template instead. It goes into detector
|
|
15
|
+
calibration rather than the bug queue.
|
|
16
|
+
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: what-happened
|
|
19
|
+
attributes:
|
|
20
|
+
label: What happened?
|
|
21
|
+
description: What did you run, what did you expect, and what did you get instead?
|
|
22
|
+
placeholder: |
|
|
23
|
+
I ran `bohrin scan ./my_data` and it exited with a traceback instead of a report.
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
|
|
27
|
+
- type: input
|
|
28
|
+
id: dataset
|
|
29
|
+
attributes:
|
|
30
|
+
label: Dataset
|
|
31
|
+
description: >
|
|
32
|
+
The Hugging Face repo id if it is public (e.g. `lerobot/pusht`) — that lets us
|
|
33
|
+
reproduce it exactly. If it is private, describe the format and layout instead.
|
|
34
|
+
placeholder: "lerobot/pusht — or: private LeRobot v2.1, 340 episodes, SO-101, 2 cameras"
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: input
|
|
39
|
+
id: bohrin-version
|
|
40
|
+
attributes:
|
|
41
|
+
label: Bohrin version
|
|
42
|
+
description: "Output of `bohrin --version`"
|
|
43
|
+
placeholder: "bohrin 0.1.0"
|
|
44
|
+
validations:
|
|
45
|
+
required: true
|
|
46
|
+
|
|
47
|
+
- type: input
|
|
48
|
+
id: python-version
|
|
49
|
+
attributes:
|
|
50
|
+
label: Python version and OS
|
|
51
|
+
description: "Output of `python --version`, plus your OS"
|
|
52
|
+
placeholder: "Python 3.11.9, Ubuntu 22.04"
|
|
53
|
+
validations:
|
|
54
|
+
required: true
|
|
55
|
+
|
|
56
|
+
- type: textarea
|
|
57
|
+
id: output
|
|
58
|
+
attributes:
|
|
59
|
+
label: Full output
|
|
60
|
+
description: >
|
|
61
|
+
The complete terminal output, including any traceback. This is automatically
|
|
62
|
+
formatted as code, so no backticks needed.
|
|
63
|
+
render: shell
|
|
64
|
+
|
|
65
|
+
- type: checkboxes
|
|
66
|
+
id: checks
|
|
67
|
+
attributes:
|
|
68
|
+
label: Before submitting
|
|
69
|
+
options:
|
|
70
|
+
- label: I checked that no existing issue already reports this.
|
|
71
|
+
required: true
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Security vulnerability
|
|
4
|
+
url: https://github.com/prabhu-gopal/bohrin/security/advisories/new
|
|
5
|
+
about: Please report security issues privately, never as a public issue. See SECURITY.md.
|
|
6
|
+
- name: Unsupported dataset format
|
|
7
|
+
url: https://github.com/prabhu-gopal/bohrin/issues/new?labels=format-request&title=%5Bformat%5D+
|
|
8
|
+
about: Want bohrin to read a format it does not support yet? Tell us which, and how common it is.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: False positive
|
|
2
|
+
description: Bohrin flagged something on your data that is actually fine.
|
|
3
|
+
title: "[false positive] "
|
|
4
|
+
labels: ["false-positive", "calibration"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
**This is the most valuable report you can send us.**
|
|
10
|
+
|
|
11
|
+
Bohrin's only real asset is that its findings are trustworthy. A detector that
|
|
12
|
+
cries wolf is worse than no detector, and we cannot find those on synthetic data —
|
|
13
|
+
we need yours. Every report here goes directly into threshold calibration.
|
|
14
|
+
|
|
15
|
+
You do **not** need to share your dataset. The detector id, the numbers bohrin
|
|
16
|
+
reported, and your explanation of why it is wrong is enough to act on.
|
|
17
|
+
|
|
18
|
+
- type: input
|
|
19
|
+
id: detector-id
|
|
20
|
+
attributes:
|
|
21
|
+
label: Detector ID
|
|
22
|
+
description: >
|
|
23
|
+
The bracketed id printed with the finding, e.g. `[stats.dead_dimension]`. Run
|
|
24
|
+
`bohrin explain <id>` to see what it thinks it is measuring.
|
|
25
|
+
placeholder: "stats.jitter_outlier"
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: the-finding
|
|
31
|
+
attributes:
|
|
32
|
+
label: The finding, as bohrin printed it
|
|
33
|
+
description: Copy the whole block, including the measured value and the episode count.
|
|
34
|
+
render: shell
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: why-wrong
|
|
40
|
+
attributes:
|
|
41
|
+
label: Why is this wrong?
|
|
42
|
+
description: >
|
|
43
|
+
What does bohrin not know about your setup? This is the part we cannot guess.
|
|
44
|
+
placeholder: |
|
|
45
|
+
Dimension 6 is a binary gripper open/close, so its variance is supposed to be near
|
|
46
|
+
zero between grasps. It is not a dead dimension — it is doing its job.
|
|
47
|
+
validations:
|
|
48
|
+
required: true
|
|
49
|
+
|
|
50
|
+
- type: textarea
|
|
51
|
+
id: dataset-context
|
|
52
|
+
attributes:
|
|
53
|
+
label: Dataset context
|
|
54
|
+
description: >
|
|
55
|
+
Robot/embodiment, control rate, action space (joint positions? EEF deltas?),
|
|
56
|
+
episode count, and the Hugging Face repo id if it is public.
|
|
57
|
+
placeholder: |
|
|
58
|
+
SO-101, 30 Hz, 6-DoF joint positions + binary gripper, 220 episodes, private.
|
|
59
|
+
validations:
|
|
60
|
+
required: true
|
|
61
|
+
|
|
62
|
+
- type: input
|
|
63
|
+
id: bohrin-version
|
|
64
|
+
attributes:
|
|
65
|
+
label: Bohrin version
|
|
66
|
+
description: "Output of `bohrin --version`"
|
|
67
|
+
placeholder: "bohrin 0.1.0"
|
|
68
|
+
validations:
|
|
69
|
+
required: true
|
|
70
|
+
|
|
71
|
+
- type: textarea
|
|
72
|
+
id: json
|
|
73
|
+
attributes:
|
|
74
|
+
label: Report JSON (optional, but very helpful)
|
|
75
|
+
description: >
|
|
76
|
+
`bohrin scan <path> --json report.json` gives us the exact measured values and
|
|
77
|
+
thresholds. Attach it or paste the relevant finding. It contains no raw data from
|
|
78
|
+
your dataset — only statistics.
|
|
79
|
+
render: json
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## What does this change?
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. The "why" matters more than the "what". -->
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
<!-- What problem does this solve? Link the issue if there is one. -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Commits are signed off (`git commit -s`) — see [CONTRIBUTING.md](../CONTRIBUTING.md)
|
|
12
|
+
- [ ] `ruff check .`, `mypy`, and `pytest` pass locally
|
|
13
|
+
- [ ] Added a test that fails before this change and passes after
|
|
14
|
+
- [ ] Updated `CHANGELOG.md` under `## [Unreleased]` if this is user-visible
|
|
15
|
+
|
|
16
|
+
### If this adds or changes a detector
|
|
17
|
+
|
|
18
|
+
- [ ] The mechanism sentence says *why* the defect degrades a trained policy
|
|
19
|
+
- [ ] Linked evidence that the defect is real (issue, paper, or training run)
|
|
20
|
+
- [ ] Added a fault-injection scenario to the benchmark
|
|
21
|
+
- [ ] Measured recall/precision are in the PR description
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: py${{ matrix.python-version }} · ${{ matrix.os }}
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
os: [ubuntu-latest, macos-latest]
|
|
24
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v5
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Install the project
|
|
34
|
+
run: uv sync --extra dev --python ${{ matrix.python-version }}
|
|
35
|
+
|
|
36
|
+
- name: Lint
|
|
37
|
+
run: uv run ruff check .
|
|
38
|
+
|
|
39
|
+
- name: Format check
|
|
40
|
+
run: uv run ruff format --check .
|
|
41
|
+
|
|
42
|
+
- name: Type check
|
|
43
|
+
run: uv run mypy
|
|
44
|
+
|
|
45
|
+
- name: Test
|
|
46
|
+
# The suite is fully synthetic: no network, no GPU, no dataset downloads.
|
|
47
|
+
run: uv run pytest -q
|
|
48
|
+
|
|
49
|
+
# The CLI is what users actually touch, and a working test suite has never once caught a
|
|
50
|
+
# broken entry point or a missing runtime dependency. This installs the built wheel into a
|
|
51
|
+
# clean environment and runs the binary, which is the only way to catch that.
|
|
52
|
+
smoke:
|
|
53
|
+
name: installed-wheel smoke test
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
|
|
58
|
+
- name: Install uv
|
|
59
|
+
uses: astral-sh/setup-uv@v5
|
|
60
|
+
|
|
61
|
+
- name: Build the wheel
|
|
62
|
+
run: uv build
|
|
63
|
+
|
|
64
|
+
- name: Verify the wheel contains the package
|
|
65
|
+
run: |
|
|
66
|
+
ls -R dist/
|
|
67
|
+
python -c "
|
|
68
|
+
import zipfile, glob, sys
|
|
69
|
+
wheel = glob.glob('dist/*.whl')[0]
|
|
70
|
+
names = zipfile.ZipFile(wheel).namelist()
|
|
71
|
+
assert any(n.startswith('bohrin/') for n in names), 'no bohrin/ inside the wheel'
|
|
72
|
+
print(f'{wheel}: {len(names)} files, package present')
|
|
73
|
+
"
|
|
74
|
+
|
|
75
|
+
- name: Install into a clean environment and run it
|
|
76
|
+
run: |
|
|
77
|
+
python -m venv /tmp/fresh
|
|
78
|
+
/tmp/fresh/bin/pip install --quiet dist/*.whl
|
|
79
|
+
/tmp/fresh/bin/bohrin --version
|
|
80
|
+
/tmp/fresh/bin/bohrin --help
|
|
81
|
+
/tmp/fresh/bin/bohrin list-detectors | head -5
|
|
82
|
+
/tmp/fresh/bin/bohrin explain stats.dead_dimension
|
|
83
|
+
|
|
84
|
+
- name: Errors are messages, not tracebacks
|
|
85
|
+
run: |
|
|
86
|
+
set +e
|
|
87
|
+
out=$(/tmp/fresh/bin/bohrin scan ./definitely-not-a-dataset 2>&1)
|
|
88
|
+
code=$?
|
|
89
|
+
set -e
|
|
90
|
+
echo "$out"
|
|
91
|
+
[ "$code" -eq 2 ] || { echo "expected exit 2 for a usage error, got $code"; exit 1; }
|
|
92
|
+
case "$out" in *Traceback*) echo "a traceback reached the user"; exit 1 ;; esac
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Tag-triggered publish. Never publish from a laptop: the tag is the only thing that can
|
|
4
|
+
# ship a version, so what is on PyPI always corresponds to a commit anyone can check out.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
test_pypi:
|
|
11
|
+
description: "Publish to TestPyPI instead of PyPI"
|
|
12
|
+
type: boolean
|
|
13
|
+
default: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
name: Build distributions
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v5
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Show what is in the distributions
|
|
32
|
+
# If `bohrin/` is not inside the wheel, pyproject.toml is wrong and the upload
|
|
33
|
+
# would silently ship an empty package.
|
|
34
|
+
run: |
|
|
35
|
+
ls -R dist/
|
|
36
|
+
python -m zipfile -l dist/*.whl | head -20
|
|
37
|
+
|
|
38
|
+
- uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
publish-testpypi:
|
|
44
|
+
name: Publish to TestPyPI
|
|
45
|
+
needs: build
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
if: github.event_name == 'workflow_dispatch' && inputs.test_pypi
|
|
48
|
+
environment:
|
|
49
|
+
name: testpypi
|
|
50
|
+
url: https://test.pypi.org/p/bohrin
|
|
51
|
+
permissions:
|
|
52
|
+
# Required for Trusted Publishing: this is what lets uv mint a short-lived OIDC
|
|
53
|
+
# token instead of us storing a long-lived PyPI API token in GitHub Secrets.
|
|
54
|
+
id-token: write
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/download-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: dist
|
|
59
|
+
path: dist/
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v5
|
|
62
|
+
- name: Publish
|
|
63
|
+
run: uv publish --trusted-publishing always --publish-url https://test.pypi.org/legacy/
|
|
64
|
+
|
|
65
|
+
publish-pypi:
|
|
66
|
+
name: Publish to PyPI
|
|
67
|
+
needs: build
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
70
|
+
environment:
|
|
71
|
+
name: pypi
|
|
72
|
+
url: https://pypi.org/p/bohrin
|
|
73
|
+
permissions:
|
|
74
|
+
id-token: write
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/download-artifact@v4
|
|
77
|
+
with:
|
|
78
|
+
name: dist
|
|
79
|
+
path: dist/
|
|
80
|
+
- name: Install uv
|
|
81
|
+
uses: astral-sh/setup-uv@v5
|
|
82
|
+
- name: Publish
|
|
83
|
+
run: uv publish --trusted-publishing always
|
|
84
|
+
|
|
85
|
+
github-release:
|
|
86
|
+
name: Attach distributions to the GitHub release
|
|
87
|
+
needs: publish-pypi
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
permissions:
|
|
90
|
+
contents: write
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
- uses: softprops/action-gh-release@v2
|
|
97
|
+
with:
|
|
98
|
+
files: dist/*
|
|
99
|
+
generate_release_notes: true
|
bohrin-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.so
|
|
9
|
+
|
|
10
|
+
# Environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
.env
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
coverage.xml
|
|
22
|
+
|
|
23
|
+
# Editors / OS
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
.DS_Store
|
|
27
|
+
|
|
28
|
+
# Bohrin scan artifacts (never commit real reports)
|
|
29
|
+
*_report.html
|
|
30
|
+
*_report.json
|
|
31
|
+
bohrin-report.*
|
|
32
|
+
|
|
33
|
+
# Internal design docs: business strategy, roadmap, licensing/moat reasoning.
|
|
34
|
+
# Kept locally for reference, deliberately never pushed to the public repo.
|
|
35
|
+
/docs/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
The **report schema** is versioned separately from the package — see `schema_version` in
|
|
8
|
+
any `--json` output. It changes only when the serialized report shape changes.
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [0.1.0] — unreleased
|
|
13
|
+
|
|
14
|
+
First public release.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `bohrin scan <path>` — analyze a robot-learning dataset and report the defects that hurt
|
|
19
|
+
training, with severity, affected episodes, the measured value, the mechanism, and a fix.
|
|
20
|
+
- **Hugging Face Hub support**: `bohrin scan lerobot/pusht` fetches `meta/` and `data/`
|
|
21
|
+
(never video) and scans the local snapshot. This is the only network call in the tool.
|
|
22
|
+
- **Formats**: LeRobot v2.1 and v3.0 (autodetected), RLDS/Open-X, robomimic and raw HDF5,
|
|
23
|
+
Zarr replay buffers, NumPy directories.
|
|
24
|
+
- **Machine-readable output**: `--json` (versioned `schema_version`), `--sarif` (SARIF
|
|
25
|
+
2.1.0 for GitHub code scanning), and a self-contained `--html` report.
|
|
26
|
+
- **CI gating**: `--ci --fail-on <severity>` exits non-zero only when you ask it to.
|
|
27
|
+
- **Calibration**: `bohrin calibrate` builds a conformal FDR corpus from your own
|
|
28
|
+
known-good data, after which `--fpr` governs the covered detector gates.
|
|
29
|
+
- `bohrin list-detectors`, `bohrin explain <detector-id>`, and `bohrin init`.
|
|
30
|
+
- Adapters and detectors are plugins discovered through entry points — the same mechanism
|
|
31
|
+
the built-ins use.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- `dynamics.inverse_residual` reported HIGH unconditionally, whatever the measured extent.
|
|
36
|
+
Across 20 curated public LeRobot datasets it fired at HIGH on 19 of them, with flagged
|
|
37
|
+
fractions spanning 0.94% to 80.8% all reported identically. Severity now scales on extent
|
|
38
|
+
(≥20% of transitions) **or** magnitude (a residual as large as the signal itself, which is
|
|
39
|
+
physically unexplainable however rare). HIGH rate on real data: 95% → 50%,
|
|
40
|
+
measured with `scripts/hub_smoke.py`.
|
|
41
|
+
- A clean scan printed "No findings" twice.
|
|
42
|
+
|
|
43
|
+
### Known limitations
|
|
44
|
+
|
|
45
|
+
- `smoothness.discontinuity_jump` and `integrity.declared_mismatch` report HIGH on 70% and
|
|
46
|
+
60% of curated public datasets respectively, which is far more likely to be a threshold
|
|
47
|
+
problem than a real epidemic. Documented in the README rather than silently shipped as
|
|
48
|
+
trustworthy; re-tuning them needs data we do not have yet.
|
|
49
|
+
|
|
50
|
+
### Deliberately not included
|
|
51
|
+
|
|
52
|
+
- **No 0-100 health score.** An aggregate number implies a calibration against real
|
|
53
|
+
training outcomes that does not exist yet — nothing here measures how much each defect
|
|
54
|
+
actually costs a trained policy. The report gives severity counts and ranked findings,
|
|
55
|
+
every one of which is individually defensible. The scoring function
|
|
56
|
+
(`bohrin.synth.pipeline.quality_score`) is still present and tested; the headline
|
|
57
|
+
returns when a corpus of training runs can back it.
|
|
58
|
+
|
|
59
|
+
### Notes
|
|
60
|
+
|
|
61
|
+
- Python 3.10 through 3.13 are supported and tested.
|
|
62
|
+
- No telemetry. Your data never leaves your machine.
|
|
63
|
+
- `schema_version` is `1.0` — the first published report contract. Nothing consumed an
|
|
64
|
+
earlier shape, since no version of bohrin was ever released.
|
|
65
|
+
|
|
66
|
+
[Unreleased]: https://github.com/prabhu-gopal/bohrin/compare/v0.1.0...HEAD
|
|
67
|
+
[0.1.0]: https://github.com/prabhu-gopal/bohrin/releases/tag/v0.1.0
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
+
community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
+
any kind
|
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
* Public or private harassment
|
|
34
|
+
* Publishing others' private information, such as a physical or email address,
|
|
35
|
+
without their explicit permission
|
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
+
decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
+
an individual is officially representing the community in public spaces.
|
|
55
|
+
Examples of representing our community include using an official e-mail address,
|
|
56
|
+
posting via an official social media account, or acting as an appointed
|
|
57
|
+
representative at an online or offline event.
|
|
58
|
+
|
|
59
|
+
## Enforcement
|
|
60
|
+
|
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
+
reported to the community leaders responsible for enforcement at
|
|
63
|
+
security@bohrin.com.
|
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
+
|
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
+
reporter of any incident.
|
|
68
|
+
|
|
69
|
+
## Enforcement Guidelines
|
|
70
|
+
|
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
+
|
|
74
|
+
### 1. Correction
|
|
75
|
+
|
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
+
unprofessional or unwelcome in the community.
|
|
78
|
+
|
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
|
82
|
+
|
|
83
|
+
### 2. Warning
|
|
84
|
+
|
|
85
|
+
**Community Impact**: A violation through a single incident or series of
|
|
86
|
+
actions.
|
|
87
|
+
|
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
+
ban.
|
|
94
|
+
|
|
95
|
+
### 3. Temporary Ban
|
|
96
|
+
|
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
|
98
|
+
sustained inappropriate behavior.
|
|
99
|
+
|
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
+
communication with the community for a specified period of time. No public or
|
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
+
Violating these terms may lead to a permanent ban.
|
|
105
|
+
|
|
106
|
+
### 4. Permanent Ban
|
|
107
|
+
|
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
+
|
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
+
community.
|
|
114
|
+
|
|
115
|
+
## Attribution
|
|
116
|
+
|
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
+
version 2.1, available at
|
|
119
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
+
|
|
121
|
+
Community Impact Guidelines were inspired by
|
|
122
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
+
|
|
124
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
+
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
+
|
|
128
|
+
[homepage]: https://www.contributor-covenant.org
|
|
129
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
+
[translations]: https://www.contributor-covenant.org/translations
|