benchloop-client 0.2.22__tar.gz → 0.2.23__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.
Files changed (57) hide show
  1. benchloop_client-0.2.23/PKG-INFO +294 -0
  2. benchloop_client-0.2.23/README.md +282 -0
  3. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/pyproject.toml +1 -1
  4. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/cli.py +2 -0
  5. benchloop_client-0.2.23/src/aliyun/bmcli/commands/brm.py +440 -0
  6. benchloop_client-0.2.23/tests/test_cmd_brm.py +451 -0
  7. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/uv.lock +89 -89
  8. benchloop_client-0.2.22/PKG-INFO +0 -144
  9. benchloop_client-0.2.22/README.md +0 -132
  10. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/.gitignore +0 -0
  11. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/.python-version +0 -0
  12. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/RELEASE.md +0 -0
  13. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/benchloop-cli-design.md +0 -0
  14. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/bmcli_examples.sh +0 -0
  15. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/__init__.py +0 -0
  16. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/__main__.py +0 -0
  17. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/client.py +0 -0
  18. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/__init__.py +0 -0
  19. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/_common.py +0 -0
  20. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/ap_cmd.py +0 -0
  21. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/artifacts.py +0 -0
  22. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/group_data.py +0 -0
  23. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/jobs.py +0 -0
  24. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/metadata.py +0 -0
  25. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/metrics.py +0 -0
  26. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/overview.py +0 -0
  27. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/pull.py +0 -0
  28. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/report.py +0 -0
  29. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/tasks.py +0 -0
  30. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/commands/trajectory.py +0 -0
  31. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/config.py +0 -0
  32. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/exit_codes.py +0 -0
  33. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/ids.py +0 -0
  34. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/output.py +0 -0
  35. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/runtime.py +0 -0
  36. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/src/aliyun/bmcli/version_check.py +0 -0
  37. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/__init__.py +0 -0
  38. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/conftest.py +0 -0
  39. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cli_options.py +0 -0
  40. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_client.py +0 -0
  41. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_ap_cmd.py +0 -0
  42. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_artifacts.py +0 -0
  43. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_metadata.py +0 -0
  44. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_metrics.py +0 -0
  45. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_overview.py +0 -0
  46. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_pull.py +0 -0
  47. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_report.py +0 -0
  48. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_tasks_jobs.py +0 -0
  49. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_cmd_trajectory.py +0 -0
  50. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_config.py +0 -0
  51. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_exit_code_behavior.py +0 -0
  52. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_exit_codes.py +0 -0
  53. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_group_data.py +0 -0
  54. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_ids.py +0 -0
  55. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_runtime.py +0 -0
  56. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_transfer_progress.py +0 -0
  57. {benchloop_client-0.2.22 → benchloop_client-0.2.23}/tests/test_version_check.py +0 -0
@@ -0,0 +1,294 @@
1
+ Metadata-Version: 2.5
2
+ Name: benchloop_client
3
+ Version: 0.2.23
4
+ Summary: Benchloop CLI — command line tool for benchmark analysis data (metrics, trajectory, reports, artifacts).
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: httpx>=0.25.2
7
+ Requires-Dist: tomli>=1.1.0; python_version < '3.13'
8
+ Requires-Dist: typer>=0.9.4
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8.0; extra == 'dev'
11
+ Description-Content-Type: text/markdown
12
+
13
+ # benchloop-client (`bl`, `bmcli`)
14
+
15
+ Command line tool for fetching and downloading benchmark analysis data
16
+ (run metrics, trajectory, metadata, pre-analysis reports, task/job lists,
17
+ and artifacts) by `group_id` / `job_id` / download `task_id`, and for
18
+ submitting Behavior Reward Model (`brm`) analysis tasks.
19
+
20
+ > PyPI package name is `benchloop_client`; the installed commands are `bl` and `bmcli`.
21
+
22
+ ## Install package
23
+
24
+ ```
25
+ pip install benchloop_client
26
+
27
+ bl --api-key $BENCH_LOOP_API_KEY overview get -j $JOB_ID
28
+ bl --api-key $BENCH_LOOP_API_KEY metrics get -j $JOB_ID
29
+
30
+ # Option 2: config ~/.benchloop/config.toml by cli, and run bl without --api-key option:
31
+ bl config set --api-key $BENCHLOOP_API_KEY --host https://benchmark-analysis.alibaba-inc.com
32
+
33
+ bl overview get -j $JOB_ID
34
+ bl metrics get -j $JOB_ID
35
+
36
+ ```
37
+
38
+ The CLI checks PyPI for a newer release in the background at most once every
39
+ 24 hours. Update notices include the current/latest versions and the upgrade
40
+ command. Notices are written only to stderr, so JSON output remains pipeable.
41
+
42
+ ## Install and run from source
43
+
44
+ ```bash
45
+ # with uv (recommended)
46
+ uv sync
47
+ uv run bl --help
48
+
49
+ # or editable install with pip
50
+ python -m venv .venv && source .venv/bin/activate
51
+ pip install -e .
52
+ bl --help
53
+ ```
54
+
55
+ ## Auth & config
56
+
57
+ Authentication uses a personal api key sent as the `X-API-Key` header.
58
+
59
+ Precedence: CLI flag > env var > `~/.benchloop/config.toml` > default.
60
+
61
+ - Env: `BENCHLOOP_API_KEY` (or `BENCHMARK_API_KEY`), `BENCHLOOP_HOST`, `BENCHLOOP_OUTPUT_DIR`
62
+ - Default host (prod): `https://benchmark-analysis.alibaba-inc.com`
63
+ - Pre host: `https://pre-benchmark-analysis.alibaba-inc.com`
64
+
65
+ ```bash
66
+ bl config set --api-key $BENCHLOOP_API_KEY --host https://benchmark-analysis.alibaba-inc.com
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ Two-segment `bl <resource> <action>` form. `get` prints a JSON array to
72
+ stdout (pipeable to `jq`); `download` writes the standard file layout.
73
+ Global options such as `--api-key`, `--host`, `--timeout`, `--retry`,
74
+ `--concurrency`, `--quiet`, and `--verbose` must be placed before the resource
75
+ name. Use `--concurrency 1` for a per-file byte progress bar, or a higher value
76
+ for concurrent requests/downloads.
77
+
78
+ ```bash
79
+ bl metrics get -j J123 | jq '.[0]'
80
+ bl metrics download -g G1 --scope all -o ./out
81
+ bl --concurrency 10 trajectory download -g G1,G2 -o ./out
82
+ bl --concurrency 10 trajectory download --task-id TASK_ID -o ./out
83
+ bl metadata download --task-id TASK_ID -o ./out
84
+ bl report download -j J123 -o ./out
85
+ bl report get -g G123
86
+ bl report download -g G123 -o ./out
87
+ bl jobs get -g G1 --all # get supports --all (auto-paginate)
88
+ bl jobs download -g G1 # download is always full
89
+ bl artifacts download -g G1 -o ./out
90
+ bl artifacts download --task-id TASK_ID -o ./out
91
+ bl pull -g G1 --with metrics,trajectory,jobs,artifacts -o ./out
92
+ bl brm run -g G1
93
+ bl brm run -g G1 --steps 10,20 --sample-size 512 --scoring-limit 32
94
+ bl brm get --task-id UID
95
+ bl brm get --task-id UID --scope overall
96
+ bl brm get --task-id UID --scope detail
97
+ bl brm get --task-id UID --scope status
98
+ bl brm status --task-id UID
99
+ bl brm list -g G1
100
+ ```
101
+
102
+ Group downloads always auto-paginate and include every Job in the Group. In
103
+ particular, `trajectory`, detailed `metrics`, `report`, and `artifacts` enumerate
104
+ the complete Job list instead of relying on the backend Group endpoints that
105
+ return at most 1000 Jobs. Existing aggregate files keep their original paths:
106
+
107
+ ```text
108
+ out/G1/trajectory.json
109
+ out/G1/detail_metrics.json
110
+ out/G1_report.md
111
+ out/G1/J123_report.md
112
+ out/G1/J123/<artifact-name>
113
+ out/TASK_ID_trajectories.zip
114
+ out/TASK_ID_metadata.zip
115
+ out/TASK_ID/J123.tgz
116
+ ```
117
+
118
+ `report download -g` writes both the Group summary report and each Job report.
119
+ `pull -g` uses the same complete download behavior for every selected resource.
120
+ Use `--skip-existing` to resume file-based downloads after an interruption.
121
+
122
+ IDs accept repeated flags, comma-separated values, and `-` to read from stdin.
123
+ `-g` and `-j` are mutually exclusive in a single command. For trajectory and
124
+ artifact downloads, `--task-id` is a third mutually exclusive input mode.
125
+
126
+ ### Download trajectories by task ID
127
+
128
+ The benchmark analysis Jobs page can turn the current filters into a trajectory
129
+ download task. Copy the command shown by the page and run it locally:
130
+
131
+ ```bash
132
+ bl trajectory download --task-id TASK_ID -o ./out
133
+ ```
134
+
135
+ The CLI fetches the task's complete job ID list, downloads job trajectories with
136
+ fixed concurrency, and prints count/ETA progress to stderr. It creates
137
+ `./out/TASK_ID_trajectories.zip` with this layout:
138
+
139
+ ```text
140
+ trajectories/<jobId>_trajectory.json
141
+ metadata/<jobId>.json
142
+ manifest.json
143
+ ```
144
+
145
+ Metadata is fetched from `/openapi/jobs/{job_id}/meta` and archived alongside
146
+ the trajectory. If trajectory download succeeds but metadata fails, the
147
+ trajectory is still archived, the metadata error is recorded in
148
+ `manifest.metadata_failures`, and the command exits with code `1`. If only some
149
+ jobs fail, the successful files are still archived. If every job fails, no empty
150
+ ZIP is created.
151
+
152
+ ### Download metadata by task ID
153
+
154
+ Use `metadata download` when you only need per-job metadata for a download task:
155
+
156
+ ```bash
157
+ bl metadata download --task-id TASK_ID -o ./out
158
+ ```
159
+
160
+ The CLI creates `./out/TASK_ID_metadata.zip` with this layout:
161
+
162
+ ```text
163
+ metadata/<jobId>.json
164
+ manifest.json
165
+ ```
166
+
167
+ Partial metadata failures are recorded in `manifest.failures`; successful
168
+ metadata files are still archived and the command exits with code `1`.
169
+
170
+ ### Download artifacts
171
+
172
+ Artifacts can be downloaded by Job, Group, or download task:
173
+
174
+ ```bash
175
+ bl artifacts download -j J123 -o ./out
176
+ bl artifacts download -g G1 -o ./out
177
+ bl artifacts download --task-id TASK_ID -o ./out
178
+ bl artifacts download -g G1 --include "*.tgz" --exclude "debug-*" -o ./out
179
+ bl artifacts download -g G1 --list-only
180
+ ```
181
+
182
+ For Job and Group mode, the CLI still lists artifacts first via
183
+ `/openapi/jobs/{job_id}/artifacts`, but binary download is routed through the
184
+ backend proxy endpoint `/openapi/jobs/{job_id}/artifacts/download` instead of
185
+ directly using OSS presigned URLs. In task mode, every job from the task
186
+ snapshot is downloaded as `./out/TASK_ID/<jobId>.tgz`.
187
+
188
+ When an artifact is missing, the backend may return either HTTP 404 or an HTML
189
+ error page. The CLI treats both as a missing artifact and writes a valid empty
190
+ `.tgz` placeholder instead of saving the error page, so downstream batch
191
+ extraction can continue.
192
+
193
+ Artifact downloads show byte-level progress. On a TTY the progress is refreshed
194
+ in place; in CI or redirected stderr it falls back to periodic `[progress]`
195
+ lines. `--quiet` suppresses progress output.
196
+
197
+ ### Submit a Behavior Reward Model (`brm`) task
198
+
199
+ `brm run` creates a Behavior analysis task for exactly one Group. The CLI first
200
+ calls `GET /openapi/behavior/steps?group_id=...` to detect whether the Group
201
+ has step sampling, then posts `POST /openapi/behavior/tasks`.
202
+
203
+ ```bash
204
+ # No-step groups (all_jobs sampling): omit --steps
205
+ bl brm run -g G1
206
+
207
+ # Step groups: --steps is required (comma-separated or repeatable)
208
+ bl brm run -g G1 --steps 10,20
209
+ bl brm run -g G1 --steps 10 --steps 20 --sample-size 256 --scoring-limit 64
210
+
211
+ # Pipe the task id
212
+ bl brm run -g G1 --steps 10,20 | jq '.[0].uid'
213
+ ```
214
+
215
+ Defaults: `--sample-size 512` (step mode only), `--scoring-limit 32` (always
216
+ sent). In step mode, omitting `--steps` is a usage error and the available
217
+ steps are listed on stderr. Passing `--steps` on a no-step Group is also a
218
+ usage error.
219
+
220
+ On success, stderr logs a short confirmation and stdout prints a JSON array
221
+ (one object) that includes at least `uid`, plus `group_id` and
222
+ `samplingMode` (`step` or `all_jobs`). Use `.[0].uid` (not necessarily
223
+ `task_id`) for follow-up `brm status` / `brm get` calls.
224
+
225
+ ### Query a Behavior Reward Model (`brm`) task
226
+
227
+ `brm get` queries task status and/or analysis by Behavior task uid (from
228
+ `brm run`). Multiple `--task-id` values are supported (repeatable or
229
+ comma-separated); stdout is always a JSON array, one element per id, matching
230
+ `metrics get -j ...`.
231
+
232
+ ```bash
233
+ # Default --scope all (status + overall + detail); samplingMode only here
234
+ bl brm get --task-id UID
235
+ bl brm get --task-id UID --scope all | jq '.[0] | {status, overall, detail, samplingMode}'
236
+
237
+ # overall: analysis.summary + categoryScores
238
+ bl brm get --task-id UID --scope overall | jq '.[0] | {summary, categoryScores}'
239
+
240
+ # detail: analysis.behaviorPatterns
241
+ bl brm get --task-id UID --scope detail | jq '.[0].behaviorPatterns'
242
+
243
+ # status only; `brm status` is equivalent
244
+ bl brm get --task-id UID --scope status
245
+ bl brm status --task-id UID
246
+ bl brm status --task-id UID1,UID2
247
+ ```
248
+
249
+ `--scope` values:
250
+
251
+ | scope | source | payload |
252
+ | --- | --- | --- |
253
+ | `overall` | `GET .../analysis` | `summary`, `categoryScores` |
254
+ | `detail` | `GET .../analysis` | `behaviorPatterns` |
255
+ | `status` | `GET .../tasks/{uid}` | task status / progress |
256
+ | `all` (default) | both endpoints | nested `status`, `overall`, `detail`, plus top-level `samplingMode` |
257
+
258
+ `brm status` always uses the status endpoint (same as `brm get --scope status`).
259
+
260
+ ### List Behavior Reward Model (`brm`) tasks by Group
261
+
262
+ `brm list` calls `GET /openapi/behavior/tasks?group_id=...` and prints a JSON
263
+ array (one element per `-g`), matching `jobs get -g ...`:
264
+
265
+ ```bash
266
+ bl brm list -g G1
267
+ bl brm list -g G1,G2
268
+ bl brm list -g G1 | jq '.[0].tasks[].uid'
269
+ ```
270
+
271
+ Each element looks like `{ "group_id", "total", "tasks": [ ... ] }`. Task
272
+ objects keep the backend fields and also set `task_id` from `uid` when present.
273
+
274
+ ## Exit codes
275
+
276
+ | code | meaning |
277
+ | ---- | ------- |
278
+ | 0 | all success |
279
+ | 1 | partial failure (some ids failed) |
280
+ | 2 | usage / parameter error |
281
+ | 3 | auth failure (HTTP 401) |
282
+ | 4 | network / timeout |
283
+
284
+ stdout carries data only; logs and progress go to stderr. `--quiet` suppresses
285
+ non-error logs, `--verbose` adds debug logs.
286
+
287
+
288
+ ## Release new package
289
+
290
+ ```
291
+ uv build
292
+ twine check dist/benchloop_client-*.whl
293
+ twine upload dist/benchloop_client-*.whl --verbose
294
+ ```
@@ -0,0 +1,282 @@
1
+ # benchloop-client (`bl`, `bmcli`)
2
+
3
+ Command line tool for fetching and downloading benchmark analysis data
4
+ (run metrics, trajectory, metadata, pre-analysis reports, task/job lists,
5
+ and artifacts) by `group_id` / `job_id` / download `task_id`, and for
6
+ submitting Behavior Reward Model (`brm`) analysis tasks.
7
+
8
+ > PyPI package name is `benchloop_client`; the installed commands are `bl` and `bmcli`.
9
+
10
+ ## Install package
11
+
12
+ ```
13
+ pip install benchloop_client
14
+
15
+ bl --api-key $BENCH_LOOP_API_KEY overview get -j $JOB_ID
16
+ bl --api-key $BENCH_LOOP_API_KEY metrics get -j $JOB_ID
17
+
18
+ # Option 2: config ~/.benchloop/config.toml by cli, and run bl without --api-key option:
19
+ bl config set --api-key $BENCHLOOP_API_KEY --host https://benchmark-analysis.alibaba-inc.com
20
+
21
+ bl overview get -j $JOB_ID
22
+ bl metrics get -j $JOB_ID
23
+
24
+ ```
25
+
26
+ The CLI checks PyPI for a newer release in the background at most once every
27
+ 24 hours. Update notices include the current/latest versions and the upgrade
28
+ command. Notices are written only to stderr, so JSON output remains pipeable.
29
+
30
+ ## Install and run from source
31
+
32
+ ```bash
33
+ # with uv (recommended)
34
+ uv sync
35
+ uv run bl --help
36
+
37
+ # or editable install with pip
38
+ python -m venv .venv && source .venv/bin/activate
39
+ pip install -e .
40
+ bl --help
41
+ ```
42
+
43
+ ## Auth & config
44
+
45
+ Authentication uses a personal api key sent as the `X-API-Key` header.
46
+
47
+ Precedence: CLI flag > env var > `~/.benchloop/config.toml` > default.
48
+
49
+ - Env: `BENCHLOOP_API_KEY` (or `BENCHMARK_API_KEY`), `BENCHLOOP_HOST`, `BENCHLOOP_OUTPUT_DIR`
50
+ - Default host (prod): `https://benchmark-analysis.alibaba-inc.com`
51
+ - Pre host: `https://pre-benchmark-analysis.alibaba-inc.com`
52
+
53
+ ```bash
54
+ bl config set --api-key $BENCHLOOP_API_KEY --host https://benchmark-analysis.alibaba-inc.com
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ Two-segment `bl <resource> <action>` form. `get` prints a JSON array to
60
+ stdout (pipeable to `jq`); `download` writes the standard file layout.
61
+ Global options such as `--api-key`, `--host`, `--timeout`, `--retry`,
62
+ `--concurrency`, `--quiet`, and `--verbose` must be placed before the resource
63
+ name. Use `--concurrency 1` for a per-file byte progress bar, or a higher value
64
+ for concurrent requests/downloads.
65
+
66
+ ```bash
67
+ bl metrics get -j J123 | jq '.[0]'
68
+ bl metrics download -g G1 --scope all -o ./out
69
+ bl --concurrency 10 trajectory download -g G1,G2 -o ./out
70
+ bl --concurrency 10 trajectory download --task-id TASK_ID -o ./out
71
+ bl metadata download --task-id TASK_ID -o ./out
72
+ bl report download -j J123 -o ./out
73
+ bl report get -g G123
74
+ bl report download -g G123 -o ./out
75
+ bl jobs get -g G1 --all # get supports --all (auto-paginate)
76
+ bl jobs download -g G1 # download is always full
77
+ bl artifacts download -g G1 -o ./out
78
+ bl artifacts download --task-id TASK_ID -o ./out
79
+ bl pull -g G1 --with metrics,trajectory,jobs,artifacts -o ./out
80
+ bl brm run -g G1
81
+ bl brm run -g G1 --steps 10,20 --sample-size 512 --scoring-limit 32
82
+ bl brm get --task-id UID
83
+ bl brm get --task-id UID --scope overall
84
+ bl brm get --task-id UID --scope detail
85
+ bl brm get --task-id UID --scope status
86
+ bl brm status --task-id UID
87
+ bl brm list -g G1
88
+ ```
89
+
90
+ Group downloads always auto-paginate and include every Job in the Group. In
91
+ particular, `trajectory`, detailed `metrics`, `report`, and `artifacts` enumerate
92
+ the complete Job list instead of relying on the backend Group endpoints that
93
+ return at most 1000 Jobs. Existing aggregate files keep their original paths:
94
+
95
+ ```text
96
+ out/G1/trajectory.json
97
+ out/G1/detail_metrics.json
98
+ out/G1_report.md
99
+ out/G1/J123_report.md
100
+ out/G1/J123/<artifact-name>
101
+ out/TASK_ID_trajectories.zip
102
+ out/TASK_ID_metadata.zip
103
+ out/TASK_ID/J123.tgz
104
+ ```
105
+
106
+ `report download -g` writes both the Group summary report and each Job report.
107
+ `pull -g` uses the same complete download behavior for every selected resource.
108
+ Use `--skip-existing` to resume file-based downloads after an interruption.
109
+
110
+ IDs accept repeated flags, comma-separated values, and `-` to read from stdin.
111
+ `-g` and `-j` are mutually exclusive in a single command. For trajectory and
112
+ artifact downloads, `--task-id` is a third mutually exclusive input mode.
113
+
114
+ ### Download trajectories by task ID
115
+
116
+ The benchmark analysis Jobs page can turn the current filters into a trajectory
117
+ download task. Copy the command shown by the page and run it locally:
118
+
119
+ ```bash
120
+ bl trajectory download --task-id TASK_ID -o ./out
121
+ ```
122
+
123
+ The CLI fetches the task's complete job ID list, downloads job trajectories with
124
+ fixed concurrency, and prints count/ETA progress to stderr. It creates
125
+ `./out/TASK_ID_trajectories.zip` with this layout:
126
+
127
+ ```text
128
+ trajectories/<jobId>_trajectory.json
129
+ metadata/<jobId>.json
130
+ manifest.json
131
+ ```
132
+
133
+ Metadata is fetched from `/openapi/jobs/{job_id}/meta` and archived alongside
134
+ the trajectory. If trajectory download succeeds but metadata fails, the
135
+ trajectory is still archived, the metadata error is recorded in
136
+ `manifest.metadata_failures`, and the command exits with code `1`. If only some
137
+ jobs fail, the successful files are still archived. If every job fails, no empty
138
+ ZIP is created.
139
+
140
+ ### Download metadata by task ID
141
+
142
+ Use `metadata download` when you only need per-job metadata for a download task:
143
+
144
+ ```bash
145
+ bl metadata download --task-id TASK_ID -o ./out
146
+ ```
147
+
148
+ The CLI creates `./out/TASK_ID_metadata.zip` with this layout:
149
+
150
+ ```text
151
+ metadata/<jobId>.json
152
+ manifest.json
153
+ ```
154
+
155
+ Partial metadata failures are recorded in `manifest.failures`; successful
156
+ metadata files are still archived and the command exits with code `1`.
157
+
158
+ ### Download artifacts
159
+
160
+ Artifacts can be downloaded by Job, Group, or download task:
161
+
162
+ ```bash
163
+ bl artifacts download -j J123 -o ./out
164
+ bl artifacts download -g G1 -o ./out
165
+ bl artifacts download --task-id TASK_ID -o ./out
166
+ bl artifacts download -g G1 --include "*.tgz" --exclude "debug-*" -o ./out
167
+ bl artifacts download -g G1 --list-only
168
+ ```
169
+
170
+ For Job and Group mode, the CLI still lists artifacts first via
171
+ `/openapi/jobs/{job_id}/artifacts`, but binary download is routed through the
172
+ backend proxy endpoint `/openapi/jobs/{job_id}/artifacts/download` instead of
173
+ directly using OSS presigned URLs. In task mode, every job from the task
174
+ snapshot is downloaded as `./out/TASK_ID/<jobId>.tgz`.
175
+
176
+ When an artifact is missing, the backend may return either HTTP 404 or an HTML
177
+ error page. The CLI treats both as a missing artifact and writes a valid empty
178
+ `.tgz` placeholder instead of saving the error page, so downstream batch
179
+ extraction can continue.
180
+
181
+ Artifact downloads show byte-level progress. On a TTY the progress is refreshed
182
+ in place; in CI or redirected stderr it falls back to periodic `[progress]`
183
+ lines. `--quiet` suppresses progress output.
184
+
185
+ ### Submit a Behavior Reward Model (`brm`) task
186
+
187
+ `brm run` creates a Behavior analysis task for exactly one Group. The CLI first
188
+ calls `GET /openapi/behavior/steps?group_id=...` to detect whether the Group
189
+ has step sampling, then posts `POST /openapi/behavior/tasks`.
190
+
191
+ ```bash
192
+ # No-step groups (all_jobs sampling): omit --steps
193
+ bl brm run -g G1
194
+
195
+ # Step groups: --steps is required (comma-separated or repeatable)
196
+ bl brm run -g G1 --steps 10,20
197
+ bl brm run -g G1 --steps 10 --steps 20 --sample-size 256 --scoring-limit 64
198
+
199
+ # Pipe the task id
200
+ bl brm run -g G1 --steps 10,20 | jq '.[0].uid'
201
+ ```
202
+
203
+ Defaults: `--sample-size 512` (step mode only), `--scoring-limit 32` (always
204
+ sent). In step mode, omitting `--steps` is a usage error and the available
205
+ steps are listed on stderr. Passing `--steps` on a no-step Group is also a
206
+ usage error.
207
+
208
+ On success, stderr logs a short confirmation and stdout prints a JSON array
209
+ (one object) that includes at least `uid`, plus `group_id` and
210
+ `samplingMode` (`step` or `all_jobs`). Use `.[0].uid` (not necessarily
211
+ `task_id`) for follow-up `brm status` / `brm get` calls.
212
+
213
+ ### Query a Behavior Reward Model (`brm`) task
214
+
215
+ `brm get` queries task status and/or analysis by Behavior task uid (from
216
+ `brm run`). Multiple `--task-id` values are supported (repeatable or
217
+ comma-separated); stdout is always a JSON array, one element per id, matching
218
+ `metrics get -j ...`.
219
+
220
+ ```bash
221
+ # Default --scope all (status + overall + detail); samplingMode only here
222
+ bl brm get --task-id UID
223
+ bl brm get --task-id UID --scope all | jq '.[0] | {status, overall, detail, samplingMode}'
224
+
225
+ # overall: analysis.summary + categoryScores
226
+ bl brm get --task-id UID --scope overall | jq '.[0] | {summary, categoryScores}'
227
+
228
+ # detail: analysis.behaviorPatterns
229
+ bl brm get --task-id UID --scope detail | jq '.[0].behaviorPatterns'
230
+
231
+ # status only; `brm status` is equivalent
232
+ bl brm get --task-id UID --scope status
233
+ bl brm status --task-id UID
234
+ bl brm status --task-id UID1,UID2
235
+ ```
236
+
237
+ `--scope` values:
238
+
239
+ | scope | source | payload |
240
+ | --- | --- | --- |
241
+ | `overall` | `GET .../analysis` | `summary`, `categoryScores` |
242
+ | `detail` | `GET .../analysis` | `behaviorPatterns` |
243
+ | `status` | `GET .../tasks/{uid}` | task status / progress |
244
+ | `all` (default) | both endpoints | nested `status`, `overall`, `detail`, plus top-level `samplingMode` |
245
+
246
+ `brm status` always uses the status endpoint (same as `brm get --scope status`).
247
+
248
+ ### List Behavior Reward Model (`brm`) tasks by Group
249
+
250
+ `brm list` calls `GET /openapi/behavior/tasks?group_id=...` and prints a JSON
251
+ array (one element per `-g`), matching `jobs get -g ...`:
252
+
253
+ ```bash
254
+ bl brm list -g G1
255
+ bl brm list -g G1,G2
256
+ bl brm list -g G1 | jq '.[0].tasks[].uid'
257
+ ```
258
+
259
+ Each element looks like `{ "group_id", "total", "tasks": [ ... ] }`. Task
260
+ objects keep the backend fields and also set `task_id` from `uid` when present.
261
+
262
+ ## Exit codes
263
+
264
+ | code | meaning |
265
+ | ---- | ------- |
266
+ | 0 | all success |
267
+ | 1 | partial failure (some ids failed) |
268
+ | 2 | usage / parameter error |
269
+ | 3 | auth failure (HTTP 401) |
270
+ | 4 | network / timeout |
271
+
272
+ stdout carries data only; logs and progress go to stderr. `--quiet` suppresses
273
+ non-error logs, `--verbose` adds debug logs.
274
+
275
+
276
+ ## Release new package
277
+
278
+ ```
279
+ uv build
280
+ twine check dist/benchloop_client-*.whl
281
+ twine upload dist/benchloop_client-*.whl --verbose
282
+ ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "benchloop_client"
7
- version = "0.2.22"
7
+ version = "0.2.23"
8
8
  description = "Benchloop CLI — command line tool for benchmark analysis data (metrics, trajectory, reports, artifacts)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -10,6 +10,7 @@ from aliyun.bmcli import __version__, output, version_check
10
10
  from aliyun.bmcli.commands import (
11
11
  ap_cmd as ap_cmd_mod,
12
12
  artifacts as artifacts_cmd,
13
+ brm as brm_cmd,
13
14
  jobs as jobs_cmd,
14
15
  metadata as metadata_cmd,
15
16
  metrics as metrics_cmd,
@@ -137,6 +138,7 @@ app.add_typer(tasks_cmd.app, name="tasks")
137
138
  app.add_typer(jobs_cmd.app, name="jobs")
138
139
  app.add_typer(artifacts_cmd.app, name="artifacts")
139
140
  app.add_typer(metadata_cmd.app, name="metadata")
141
+ app.add_typer(brm_cmd.app, name="brm")
140
142
  app.command(
141
143
  "ap-cmd",
142
144
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},