batchwizard 0.4.0__tar.gz → 0.5.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.
- batchwizard-0.5.0/.github/workflows/ci.yml +52 -0
- {batchwizard-0.4.0 → batchwizard-0.5.0}/.github/workflows/publish.yml +8 -4
- batchwizard-0.5.0/PKG-INFO +205 -0
- batchwizard-0.5.0/README.md +175 -0
- batchwizard-0.5.0/docs/anthropic.md +78 -0
- batchwizard-0.5.0/docs/job-lifecycle.md +121 -0
- {batchwizard-0.4.0 → batchwizard-0.5.0}/pyproject.toml +4 -8
- {batchwizard-0.4.0 → batchwizard-0.5.0}/src/batchwizard/__init__.py +9 -1
- batchwizard-0.5.0/src/batchwizard/cli.py +648 -0
- batchwizard-0.5.0/src/batchwizard/config.py +90 -0
- batchwizard-0.5.0/src/batchwizard/models.py +155 -0
- batchwizard-0.5.0/src/batchwizard/processor.py +275 -0
- batchwizard-0.5.0/src/batchwizard/providers/__init__.py +33 -0
- batchwizard-0.5.0/src/batchwizard/providers/anthropic.py +331 -0
- batchwizard-0.5.0/src/batchwizard/providers/base.py +50 -0
- batchwizard-0.5.0/src/batchwizard/providers/openai.py +196 -0
- batchwizard-0.5.0/src/batchwizard/store.py +310 -0
- {batchwizard-0.4.0 → batchwizard-0.5.0}/src/batchwizard/ui.py +84 -29
- {batchwizard-0.4.0 → batchwizard-0.5.0}/src/batchwizard/utils.py +4 -4
- batchwizard-0.5.0/tests/test_anthropic_provider.py +427 -0
- batchwizard-0.5.0/tests/test_anthropic_sdk_contract.py +134 -0
- batchwizard-0.5.0/tests/test_cli.py +444 -0
- batchwizard-0.5.0/tests/test_config.py +40 -0
- batchwizard-0.5.0/tests/test_openai_provider.py +341 -0
- batchwizard-0.5.0/tests/test_openai_sdk_contract.py +112 -0
- batchwizard-0.5.0/tests/test_orchestrator.py +453 -0
- batchwizard-0.5.0/tests/test_store.py +201 -0
- batchwizard-0.5.0/tests/test_ui.py +50 -0
- batchwizard-0.5.0/uv.lock +1252 -0
- batchwizard-0.4.0/.github/workflows/ci.yml +0 -53
- batchwizard-0.4.0/PKG-INFO +0 -248
- batchwizard-0.4.0/README.md +0 -218
- batchwizard-0.4.0/src/batchwizard/cli.py +0 -344
- batchwizard-0.4.0/src/batchwizard/config.py +0 -55
- batchwizard-0.4.0/src/batchwizard/models.py +0 -61
- batchwizard-0.4.0/src/batchwizard/processor.py +0 -160
- batchwizard-0.4.0/src/batchwizard/providers/__init__.py +0 -19
- batchwizard-0.4.0/src/batchwizard/providers/base.py +0 -30
- batchwizard-0.4.0/src/batchwizard/providers/openai.py +0 -96
- batchwizard-0.4.0/src/batchwizard/store.py +0 -91
- batchwizard-0.4.0/tests/test_openai_provider.py +0 -141
- batchwizard-0.4.0/tests/test_orchestrator.py +0 -175
- batchwizard-0.4.0/tests/test_store.py +0 -69
- batchwizard-0.4.0/uv.lock +0 -627
- {batchwizard-0.4.0 → batchwizard-0.5.0}/.gitignore +0 -0
- {batchwizard-0.4.0 → batchwizard-0.5.0}/LICENSE +0 -0
- {batchwizard-0.4.0 → batchwizard-0.5.0}/tests/conftest.py +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7.0.0
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
- uses: astral-sh/setup-uv@v8.3.2
|
|
20
|
+
- run: uv sync --frozen --group dev
|
|
21
|
+
- run: uv run --frozen ruff check .
|
|
22
|
+
- run: uv run --frozen ruff format --check .
|
|
23
|
+
|
|
24
|
+
test:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
strategy:
|
|
27
|
+
matrix:
|
|
28
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v7.0.0
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 0
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- uses: astral-sh/setup-uv@v8.3.2
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
- run: uv sync --frozen --group dev
|
|
38
|
+
- run: uv run --frozen pytest tests/
|
|
39
|
+
|
|
40
|
+
build:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7.0.0
|
|
44
|
+
with:
|
|
45
|
+
fetch-depth: 0
|
|
46
|
+
persist-credentials: false
|
|
47
|
+
- uses: astral-sh/setup-uv@v8.3.2
|
|
48
|
+
- run: uv build
|
|
49
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
@@ -4,16 +4,20 @@ on:
|
|
|
4
4
|
release:
|
|
5
5
|
types: [published]
|
|
6
6
|
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
7
10
|
jobs:
|
|
8
11
|
build:
|
|
9
12
|
runs-on: ubuntu-latest
|
|
10
13
|
steps:
|
|
11
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v7.0.0
|
|
12
15
|
with:
|
|
13
16
|
fetch-depth: 0
|
|
14
|
-
|
|
17
|
+
persist-credentials: false
|
|
18
|
+
- uses: astral-sh/setup-uv@v8.3.2
|
|
15
19
|
- run: uv build
|
|
16
|
-
- uses: actions/upload-artifact@
|
|
20
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
17
21
|
with:
|
|
18
22
|
name: dist
|
|
19
23
|
path: dist/
|
|
@@ -26,7 +30,7 @@ jobs:
|
|
|
26
30
|
# Required for PyPI Trusted Publishing (OIDC) — no API token needed
|
|
27
31
|
id-token: write
|
|
28
32
|
steps:
|
|
29
|
-
- uses: actions/download-artifact@
|
|
33
|
+
- uses: actions/download-artifact@v8.0.1
|
|
30
34
|
with:
|
|
31
35
|
name: dist
|
|
32
36
|
path: dist/
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: batchwizard
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Durable batch orchestration across OpenAI and Anthropic
|
|
5
|
+
Project-URL: Homepage, https://github.com/cmakafui/batchwizard
|
|
6
|
+
Project-URL: Repository, https://github.com/cmakafui/batchwizard
|
|
7
|
+
Project-URL: Issues, https://github.com/cmakafui/batchwizard/issues
|
|
8
|
+
Author: Carl Kugblenu
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anthropic,async,batch,cli,openai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: anthropic[aiohttp]<1,>=0.117
|
|
22
|
+
Requires-Dist: loguru>=0.7
|
|
23
|
+
Requires-Dist: openai[aiohttp]<3,>=2.46
|
|
24
|
+
Requires-Dist: pydantic-settings>=2.3
|
|
25
|
+
Requires-Dist: pydantic>=2.8
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0
|
|
27
|
+
Requires-Dist: rich>=13.7
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# BatchWizard
|
|
32
|
+
|
|
33
|
+
BatchWizard is one durable CLI for OpenAI and Anthropic batch jobs. Submit
|
|
34
|
+
provider-native JSONL, close the terminal, and come back later: a local SQLite
|
|
35
|
+
manifest remembers what is running, what still needs to be downloaded, and what
|
|
36
|
+
needs your attention.
|
|
37
|
+
|
|
38
|
+
It deliberately does not translate prompts between APIs. OpenAI and Anthropic
|
|
39
|
+
have different request formats and capabilities; BatchWizard gives them one
|
|
40
|
+
operational lifecycle without pretending they are the same protocol.
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
Install BatchWizard as an isolated Python 3.11+ tool:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool install batchwizard
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Provide one or both standard API-key environment variables:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
export OPENAI_API_KEY="..."
|
|
54
|
+
export ANTHROPIC_API_KEY="..."
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Submit native JSONL to either provider:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
batchwizard submit openai.jsonl --endpoint /v1/responses
|
|
61
|
+
batchwizard submit --provider anthropic anthropic.jsonl
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The submit process can exit once the providers accept the work. One later
|
|
65
|
+
command resumes every actionable job, grouped by provider:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
batchwizard watch --output-directory ./results
|
|
69
|
+
batchwizard status --all
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`watch` is intentionally provider-free. The manifest records which adapter owns
|
|
73
|
+
each batch, so a single invocation can resume a mixture of OpenAI and Anthropic
|
|
74
|
+
jobs. Missing credentials or a temporary download failure for one provider do
|
|
75
|
+
not erase work or prevent other provider groups from advancing.
|
|
76
|
+
|
|
77
|
+
## Input formats
|
|
78
|
+
|
|
79
|
+
BatchWizard accepts each provider's native JSONL rather than maintaining a lossy
|
|
80
|
+
common prompt schema.
|
|
81
|
+
|
|
82
|
+
| | OpenAI | Anthropic |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| Select with | default, or `--provider openai` | `--provider anthropic` |
|
|
85
|
+
| JSONL row | `{"custom_id", "method", "url", "body"}` | `{"custom_id", "params"}` |
|
|
86
|
+
| Model location | `body.model` | `params.model` |
|
|
87
|
+
| Submission | file upload, then Batch creation | requests sent inline to Message Batches |
|
|
88
|
+
| Provider status | Batch `status` | Message Batch `processing_status` |
|
|
89
|
+
| Results | provider output and error files | unordered result stream split by outcome |
|
|
90
|
+
| Recovery after an uncertain submit | automatic intent matching through Batch metadata | manual attachment by batch ID |
|
|
91
|
+
|
|
92
|
+
### OpenAI
|
|
93
|
+
|
|
94
|
+
```jsonl
|
|
95
|
+
{"custom_id":"ticket-1","method":"POST","url":"/v1/responses","body":{"model":"gpt-5.4","input":"Classify this ticket as billing, technical, or other."}}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The endpoint passed to BatchWizard must match the `url` in every row:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
batchwizard submit openai.jsonl --endpoint /v1/responses
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`/v1/chat/completions` is the default. BatchWizard also accepts the Responses,
|
|
105
|
+
Embeddings, Completions, Moderations, Images, and Videos Batch API endpoints.
|
|
106
|
+
|
|
107
|
+
### Anthropic
|
|
108
|
+
|
|
109
|
+
```jsonl
|
|
110
|
+
{"custom_id":"ticket-1","params":{"model":"claude-opus-4-8","max_tokens":256,"messages":[{"role":"user","content":"Classify this ticket as billing, technical, or other."}]}}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
batchwizard submit --provider anthropic anthropic.jsonl
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
BatchWizard validates the batch envelope, `custom_id` syntax and uniqueness,
|
|
118
|
+
batch size, `max_tokens`, and the non-streaming requirement before submission.
|
|
119
|
+
Anthropic remains responsible for validating its evolving Messages parameter
|
|
120
|
+
surface. See [Anthropic Message Batches](docs/anthropic.md) for result routing,
|
|
121
|
+
prompt caching, cancellation, and retention behavior.
|
|
122
|
+
|
|
123
|
+
## Durable jobs and recovery
|
|
124
|
+
|
|
125
|
+
The manifest separates remote execution from local artifact collection. A
|
|
126
|
+
provider can report a batch complete while its result files are still pending
|
|
127
|
+
locally; that row remains actionable until collection succeeds or the provider
|
|
128
|
+
confirms that the artifacts are no longer available.
|
|
129
|
+
|
|
130
|
+
Submission is durable too. BatchWizard writes an intent before making provider
|
|
131
|
+
requests. If a connection fails after the remote service may have accepted the
|
|
132
|
+
batch, it keeps that intent instead of blindly submitting a duplicate:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
batchwizard reconcile
|
|
136
|
+
batchwizard reconcile INTENT
|
|
137
|
+
batchwizard reconcile INTENT --batch-id MSGBATCH_ID
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
OpenAI intents can be matched through Batch metadata. Anthropic does not expose
|
|
141
|
+
equivalent batch metadata, so its uncertain submissions are attached after the
|
|
142
|
+
matching batch is identified with `list-jobs`.
|
|
143
|
+
|
|
144
|
+
The lifecycle and SQLite migrations are documented in
|
|
145
|
+
[Job lifecycle](docs/job-lifecycle.md).
|
|
146
|
+
|
|
147
|
+
## Result files
|
|
148
|
+
|
|
149
|
+
Provider output stays JSONL and is written atomically:
|
|
150
|
+
|
|
151
|
+
- `<batch_id>_results.jsonl` contains successful rows.
|
|
152
|
+
- `<batch_id>_errors.jsonl` contains request-level failures when present.
|
|
153
|
+
|
|
154
|
+
Anthropic result rows can arrive in any order. BatchWizard preserves each raw row
|
|
155
|
+
and routes `succeeded` results to the results file and `errored`, `expired`, or
|
|
156
|
+
`canceled` results to the errors file.
|
|
157
|
+
|
|
158
|
+
## Credentials and configuration
|
|
159
|
+
|
|
160
|
+
Environment variables are the preferred way to supply credentials. Keys can
|
|
161
|
+
also be persisted for convenience:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
batchwizard configure --provider openai --set-key "$OPENAI_API_KEY"
|
|
165
|
+
batchwizard configure --provider anthropic --set-key "$ANTHROPIC_API_KEY"
|
|
166
|
+
batchwizard configure --show --provider openai
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Persisted keys are plaintext JSON. BatchWizard writes the configuration
|
|
170
|
+
atomically with owner-only file permissions, but it does not use the operating
|
|
171
|
+
system keychain in v0.5. Treat that file as a secret; keyring-backed storage is
|
|
172
|
+
deferred to v0.6.
|
|
173
|
+
|
|
174
|
+
## Commands
|
|
175
|
+
|
|
176
|
+
| Command | Purpose |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
| `submit` | Submit files and return after provider acceptance. |
|
|
179
|
+
| `watch` | Resume actionable jobs and collect terminal artifacts. |
|
|
180
|
+
| `process` | Submit, watch, and collect in one invocation. |
|
|
181
|
+
| `status` | Inspect the local SQLite manifest. |
|
|
182
|
+
| `reconcile` | Recover a submission with an uncertain provider outcome. |
|
|
183
|
+
| `list-jobs` | List recent jobs directly from one provider. |
|
|
184
|
+
| `cancel` | Request cancellation for a tracked or provider-native batch ID. |
|
|
185
|
+
| `download` | Collect result artifacts for one batch. |
|
|
186
|
+
| `configure` | Store, inspect, or reset local settings. |
|
|
187
|
+
|
|
188
|
+
Use `batchwizard <command> --help` for the exact arguments and options.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
uv sync --all-groups
|
|
194
|
+
uv run pytest
|
|
195
|
+
uv run ruff format --check .
|
|
196
|
+
uv run ruff check .
|
|
197
|
+
uv build
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The supported runtime matrix is Python 3.11 through 3.14. The lockfile is
|
|
201
|
+
committed; update it intentionally with `uv lock --upgrade-package <package>`.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# BatchWizard
|
|
2
|
+
|
|
3
|
+
BatchWizard is one durable CLI for OpenAI and Anthropic batch jobs. Submit
|
|
4
|
+
provider-native JSONL, close the terminal, and come back later: a local SQLite
|
|
5
|
+
manifest remembers what is running, what still needs to be downloaded, and what
|
|
6
|
+
needs your attention.
|
|
7
|
+
|
|
8
|
+
It deliberately does not translate prompts between APIs. OpenAI and Anthropic
|
|
9
|
+
have different request formats and capabilities; BatchWizard gives them one
|
|
10
|
+
operational lifecycle without pretending they are the same protocol.
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
Install BatchWizard as an isolated Python 3.11+ tool:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install batchwizard
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Provide one or both standard API-key environment variables:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export OPENAI_API_KEY="..."
|
|
24
|
+
export ANTHROPIC_API_KEY="..."
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Submit native JSONL to either provider:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
batchwizard submit openai.jsonl --endpoint /v1/responses
|
|
31
|
+
batchwizard submit --provider anthropic anthropic.jsonl
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The submit process can exit once the providers accept the work. One later
|
|
35
|
+
command resumes every actionable job, grouped by provider:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
batchwizard watch --output-directory ./results
|
|
39
|
+
batchwizard status --all
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`watch` is intentionally provider-free. The manifest records which adapter owns
|
|
43
|
+
each batch, so a single invocation can resume a mixture of OpenAI and Anthropic
|
|
44
|
+
jobs. Missing credentials or a temporary download failure for one provider do
|
|
45
|
+
not erase work or prevent other provider groups from advancing.
|
|
46
|
+
|
|
47
|
+
## Input formats
|
|
48
|
+
|
|
49
|
+
BatchWizard accepts each provider's native JSONL rather than maintaining a lossy
|
|
50
|
+
common prompt schema.
|
|
51
|
+
|
|
52
|
+
| | OpenAI | Anthropic |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Select with | default, or `--provider openai` | `--provider anthropic` |
|
|
55
|
+
| JSONL row | `{"custom_id", "method", "url", "body"}` | `{"custom_id", "params"}` |
|
|
56
|
+
| Model location | `body.model` | `params.model` |
|
|
57
|
+
| Submission | file upload, then Batch creation | requests sent inline to Message Batches |
|
|
58
|
+
| Provider status | Batch `status` | Message Batch `processing_status` |
|
|
59
|
+
| Results | provider output and error files | unordered result stream split by outcome |
|
|
60
|
+
| Recovery after an uncertain submit | automatic intent matching through Batch metadata | manual attachment by batch ID |
|
|
61
|
+
|
|
62
|
+
### OpenAI
|
|
63
|
+
|
|
64
|
+
```jsonl
|
|
65
|
+
{"custom_id":"ticket-1","method":"POST","url":"/v1/responses","body":{"model":"gpt-5.4","input":"Classify this ticket as billing, technical, or other."}}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The endpoint passed to BatchWizard must match the `url` in every row:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
batchwizard submit openai.jsonl --endpoint /v1/responses
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`/v1/chat/completions` is the default. BatchWizard also accepts the Responses,
|
|
75
|
+
Embeddings, Completions, Moderations, Images, and Videos Batch API endpoints.
|
|
76
|
+
|
|
77
|
+
### Anthropic
|
|
78
|
+
|
|
79
|
+
```jsonl
|
|
80
|
+
{"custom_id":"ticket-1","params":{"model":"claude-opus-4-8","max_tokens":256,"messages":[{"role":"user","content":"Classify this ticket as billing, technical, or other."}]}}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
batchwizard submit --provider anthropic anthropic.jsonl
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
BatchWizard validates the batch envelope, `custom_id` syntax and uniqueness,
|
|
88
|
+
batch size, `max_tokens`, and the non-streaming requirement before submission.
|
|
89
|
+
Anthropic remains responsible for validating its evolving Messages parameter
|
|
90
|
+
surface. See [Anthropic Message Batches](docs/anthropic.md) for result routing,
|
|
91
|
+
prompt caching, cancellation, and retention behavior.
|
|
92
|
+
|
|
93
|
+
## Durable jobs and recovery
|
|
94
|
+
|
|
95
|
+
The manifest separates remote execution from local artifact collection. A
|
|
96
|
+
provider can report a batch complete while its result files are still pending
|
|
97
|
+
locally; that row remains actionable until collection succeeds or the provider
|
|
98
|
+
confirms that the artifacts are no longer available.
|
|
99
|
+
|
|
100
|
+
Submission is durable too. BatchWizard writes an intent before making provider
|
|
101
|
+
requests. If a connection fails after the remote service may have accepted the
|
|
102
|
+
batch, it keeps that intent instead of blindly submitting a duplicate:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
batchwizard reconcile
|
|
106
|
+
batchwizard reconcile INTENT
|
|
107
|
+
batchwizard reconcile INTENT --batch-id MSGBATCH_ID
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
OpenAI intents can be matched through Batch metadata. Anthropic does not expose
|
|
111
|
+
equivalent batch metadata, so its uncertain submissions are attached after the
|
|
112
|
+
matching batch is identified with `list-jobs`.
|
|
113
|
+
|
|
114
|
+
The lifecycle and SQLite migrations are documented in
|
|
115
|
+
[Job lifecycle](docs/job-lifecycle.md).
|
|
116
|
+
|
|
117
|
+
## Result files
|
|
118
|
+
|
|
119
|
+
Provider output stays JSONL and is written atomically:
|
|
120
|
+
|
|
121
|
+
- `<batch_id>_results.jsonl` contains successful rows.
|
|
122
|
+
- `<batch_id>_errors.jsonl` contains request-level failures when present.
|
|
123
|
+
|
|
124
|
+
Anthropic result rows can arrive in any order. BatchWizard preserves each raw row
|
|
125
|
+
and routes `succeeded` results to the results file and `errored`, `expired`, or
|
|
126
|
+
`canceled` results to the errors file.
|
|
127
|
+
|
|
128
|
+
## Credentials and configuration
|
|
129
|
+
|
|
130
|
+
Environment variables are the preferred way to supply credentials. Keys can
|
|
131
|
+
also be persisted for convenience:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
batchwizard configure --provider openai --set-key "$OPENAI_API_KEY"
|
|
135
|
+
batchwizard configure --provider anthropic --set-key "$ANTHROPIC_API_KEY"
|
|
136
|
+
batchwizard configure --show --provider openai
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Persisted keys are plaintext JSON. BatchWizard writes the configuration
|
|
140
|
+
atomically with owner-only file permissions, but it does not use the operating
|
|
141
|
+
system keychain in v0.5. Treat that file as a secret; keyring-backed storage is
|
|
142
|
+
deferred to v0.6.
|
|
143
|
+
|
|
144
|
+
## Commands
|
|
145
|
+
|
|
146
|
+
| Command | Purpose |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `submit` | Submit files and return after provider acceptance. |
|
|
149
|
+
| `watch` | Resume actionable jobs and collect terminal artifacts. |
|
|
150
|
+
| `process` | Submit, watch, and collect in one invocation. |
|
|
151
|
+
| `status` | Inspect the local SQLite manifest. |
|
|
152
|
+
| `reconcile` | Recover a submission with an uncertain provider outcome. |
|
|
153
|
+
| `list-jobs` | List recent jobs directly from one provider. |
|
|
154
|
+
| `cancel` | Request cancellation for a tracked or provider-native batch ID. |
|
|
155
|
+
| `download` | Collect result artifacts for one batch. |
|
|
156
|
+
| `configure` | Store, inspect, or reset local settings. |
|
|
157
|
+
|
|
158
|
+
Use `batchwizard <command> --help` for the exact arguments and options.
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv sync --all-groups
|
|
164
|
+
uv run pytest
|
|
165
|
+
uv run ruff format --check .
|
|
166
|
+
uv run ruff check .
|
|
167
|
+
uv build
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The supported runtime matrix is Python 3.11 through 3.14. The lockfile is
|
|
171
|
+
committed; update it intentionally with `uv lock --upgrade-package <package>`.
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Anthropic Message Batches
|
|
2
|
+
|
|
3
|
+
BatchWizard uses the GA async Message Batches surface in the official Anthropic
|
|
4
|
+
Python SDK. Inputs and saved result rows stay provider-native; only lifecycle,
|
|
5
|
+
request counts, and local artifact locations are normalized.
|
|
6
|
+
|
|
7
|
+
## Input
|
|
8
|
+
|
|
9
|
+
Each nonempty UTF-8 JSONL line must contain a unique `custom_id` and a Messages
|
|
10
|
+
API `params` object:
|
|
11
|
+
|
|
12
|
+
```jsonl
|
|
13
|
+
{"custom_id":"ticket-42","params":{"model":"claude-opus-4-8","max_tokens":256,"messages":[{"role":"user","content":"Classify this ticket."}]}}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
batchwizard submit --provider anthropic input.jsonl
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
BatchWizard validates the batch envelope before making an HTTP request:
|
|
21
|
+
|
|
22
|
+
- 1–100,000 requests and a serialized request size no greater than 256 MB
|
|
23
|
+
- unique 1–64 character `custom_id` values containing only letters, numbers,
|
|
24
|
+
hyphens, and underscores
|
|
25
|
+
- object-valued `params` with `model`, `messages`, and `max_tokens >= 1`
|
|
26
|
+
- `stream` must not be `true`
|
|
27
|
+
|
|
28
|
+
The complete Messages parameter surface evolves independently. Anthropic
|
|
29
|
+
validates those parameters asynchronously and returns invalid rows as
|
|
30
|
+
`result.type == "errored"`; BatchWizard does not claim to reproduce that server
|
|
31
|
+
validator.
|
|
32
|
+
|
|
33
|
+
### Prompt caching
|
|
34
|
+
|
|
35
|
+
Message Batches and prompt caching can be combined, and their pricing discounts
|
|
36
|
+
stack. Put the shared prefix first and use identical `cache_control` breakpoints
|
|
37
|
+
across rows. Because rows run asynchronously and concurrently, cache hits are
|
|
38
|
+
best-effort; Anthropic recommends the 1-hour TTL for batches with shared context
|
|
39
|
+
that may process beyond the default 5-minute cache lifetime.
|
|
40
|
+
|
|
41
|
+
## Lifecycle and outcomes
|
|
42
|
+
|
|
43
|
+
Anthropic exposes three batch lifecycle statuses:
|
|
44
|
+
|
|
45
|
+
| Anthropic | BatchWizard |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `in_progress` | `running` |
|
|
48
|
+
| `canceling` | `cancelling` |
|
|
49
|
+
| `ended` | terminal `completed` |
|
|
50
|
+
|
|
51
|
+
`ended` is neutral: the batch may contain any mixture of `succeeded`, `errored`,
|
|
52
|
+
`canceled`, and `expired` requests. Those counters are recorded independently, so
|
|
53
|
+
an all-errored batch is not mislabeled as a provider-level failure.
|
|
54
|
+
|
|
55
|
+
Cancellation first returns `canceling`. The eventual batch status is `ended`, and
|
|
56
|
+
partial successful results may still exist.
|
|
57
|
+
|
|
58
|
+
## Results and retention
|
|
59
|
+
|
|
60
|
+
The SDK streams an unordered JSONL result sequence. BatchWizard preserves every
|
|
61
|
+
native object and routes by row result type:
|
|
62
|
+
|
|
63
|
+
- `succeeded` → `<batch_id>_results.jsonl`
|
|
64
|
+
- `errored`, `expired`, or `canceled` → `<batch_id>_errors.jsonl`
|
|
65
|
+
|
|
66
|
+
Both destination files are built as temporary files and exposed only after the
|
|
67
|
+
entire decoder finishes. A network or disk interruption leaves previous final
|
|
68
|
+
files untouched and keeps collection actionable. The decoder is explicitly
|
|
69
|
+
closed on both success and failure.
|
|
70
|
+
|
|
71
|
+
Anthropic retains results for 29 days from batch creation. Once the provider
|
|
72
|
+
reports them archived, collection becomes `unavailable` rather than retrying a
|
|
73
|
+
permanent loss forever.
|
|
74
|
+
|
|
75
|
+
See the current [Message Batches guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing),
|
|
76
|
+
[prompt caching guide](https://platform.claude.com/docs/en/build-with-claude/prompt-caching),
|
|
77
|
+
[Python SDK documentation](https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/python),
|
|
78
|
+
and [Batch API reference](https://platform.claude.com/docs/en/api/python/messages/batches).
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Job lifecycle
|
|
2
|
+
|
|
3
|
+
BatchWizard treats a provider batch and the CLI process watching it as separate
|
|
4
|
+
things. A submitted job remains in the SQLite manifest until its remote lifecycle
|
|
5
|
+
and local artifacts can be inspected independently.
|
|
6
|
+
|
|
7
|
+
## State dimensions
|
|
8
|
+
|
|
9
|
+
`JobRecord` deliberately records four different concerns:
|
|
10
|
+
|
|
11
|
+
| Concern | Manifest fields | Meaning |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Remote lifecycle | `state`, `provider_status` | The normalized lifecycle and the provider's exact status string |
|
|
14
|
+
| Request outcomes | `completed_count`, `failed_count`, `cancelled_count`, `expired_count`, `total_count` | Per-request results; a terminal job can contain a mixture |
|
|
15
|
+
| Artifact collection | `collection_state`, `output_path`, `error_path` | Whether all currently available provider artifacts are durable locally |
|
|
16
|
+
| Local health | `last_local_error`, `poll_failures` | Connectivity, authentication, or filesystem problems that must not rewrite remote truth |
|
|
17
|
+
|
|
18
|
+
The normalized remote states are:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
submitting -> pending -> running -> completed | failed | expired
|
|
22
|
+
-> cancelling -> cancelled
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Providers may skip states. An unknown provider status is preserved in
|
|
26
|
+
`provider_status`; BatchWizard does not guess whether it is terminal.
|
|
27
|
+
`submitting` is a durable local intent written before provider I/O. It has no
|
|
28
|
+
provider batch ID until submission is confirmed or reconciled.
|
|
29
|
+
|
|
30
|
+
Artifact collection progresses independently:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
not_ready -> pending -> collected
|
|
34
|
+
\-> failed -> pending (on the next retry)
|
|
35
|
+
\-> unavailable
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`collected` means the collection attempt completed successfully. A provider may
|
|
39
|
+
legitimately have no output or error file for a terminal job.
|
|
40
|
+
`unavailable` means the provider has permanently removed or archived artifacts;
|
|
41
|
+
it is terminal and is not retried.
|
|
42
|
+
|
|
43
|
+
## Actionable jobs
|
|
44
|
+
|
|
45
|
+
A job is actionable when either:
|
|
46
|
+
|
|
47
|
+
1. It is an unresolved `submitting` intent;
|
|
48
|
+
2. Its remote state is `pending`, `running`, or `cancelling`; or
|
|
49
|
+
3. Its remote state is terminal and artifact collection is `pending` or `failed`.
|
|
50
|
+
|
|
51
|
+
`batchwizard watch` operates on actionable jobs. This is why a temporary download
|
|
52
|
+
failure survives process exit and is retried by a later invocation.
|
|
53
|
+
|
|
54
|
+
## Failure invariants
|
|
55
|
+
|
|
56
|
+
- A polling, authentication, rate-limit, SDK, or network error is a local
|
|
57
|
+
observation failure. It never changes the remote job to `failed`.
|
|
58
|
+
- Retryable polling errors are attempted a bounded number of times in one watch
|
|
59
|
+
invocation. The job remains actionable after the watcher pauses.
|
|
60
|
+
- A provider-reported terminal status is stored before artifact collection begins.
|
|
61
|
+
- A submission intent is committed before provider I/O. An uncertain submission
|
|
62
|
+
outcome is never retried as a new batch automatically.
|
|
63
|
+
- Downloads are streamed to a temporary file in the destination directory and
|
|
64
|
+
atomically renamed only after the stream completes.
|
|
65
|
+
- A failed collection records `collection_state=failed` and remains actionable.
|
|
66
|
+
- A provider-confirmed retention loss records `collection_state=unavailable` and
|
|
67
|
+
stops retrying without pretending the artifacts were collected.
|
|
68
|
+
- Request-level errors do not change a successfully ended provider job into a
|
|
69
|
+
provider failure; their counts and error artifact are recorded separately.
|
|
70
|
+
- A cancellation request is not equivalent to cancellation completion. The
|
|
71
|
+
provider's immediate `cancelling` state remains active until confirmed terminal.
|
|
72
|
+
|
|
73
|
+
## Provider contract
|
|
74
|
+
|
|
75
|
+
A provider adapter owns submission mechanics and native statuses, but exposes a
|
|
76
|
+
small operational contract:
|
|
77
|
+
|
|
78
|
+
- Submit a provider-native input file.
|
|
79
|
+
- Return a normalized status snapshot while preserving the raw status.
|
|
80
|
+
- Fetch all available success and error artifacts idempotently.
|
|
81
|
+
- Request cancellation and return its immediate status.
|
|
82
|
+
- Return provider-neutral summaries for `list-jobs`.
|
|
83
|
+
|
|
84
|
+
Input and result payloads remain provider-native. BatchWizard normalizes lifecycle
|
|
85
|
+
and artifact locations, not model-specific request semantics.
|
|
86
|
+
|
|
87
|
+
OpenAI exposes job-level terminal statuses. Anthropic exposes the neutral terminal
|
|
88
|
+
status `ended` and independent `succeeded`, `errored`, `canceled`, and `expired`
|
|
89
|
+
request counts. BatchWizard maps `ended` to a terminal normalized lifecycle while
|
|
90
|
+
preserving those row outcomes; an all-errored Anthropic batch is not rewritten as
|
|
91
|
+
a remote provider failure.
|
|
92
|
+
|
|
93
|
+
## Schema migrations
|
|
94
|
+
|
|
95
|
+
The SQLite manifest uses `PRAGMA user_version`. Opening a v0.4 manifest migrates it
|
|
96
|
+
through schema versions 1, 2, and 3:
|
|
97
|
+
|
|
98
|
+
- Active jobs start with artifact state `not_ready`.
|
|
99
|
+
- Terminal jobs with an existing output or error path become `collected`.
|
|
100
|
+
- Terminal jobs without a known local path become `pending` so collection is
|
|
101
|
+
conservatively retried.
|
|
102
|
+
- Version 2 makes `(provider, batch_id)` the durable identity. Provider-native IDs
|
|
103
|
+
no longer have to be globally unique across different providers.
|
|
104
|
+
- Version 3 adds a unique submission intent, makes the provider batch ID and
|
|
105
|
+
endpoint nullable until confirmation, and preserves existing rows with stable
|
|
106
|
+
legacy intent IDs.
|
|
107
|
+
|
|
108
|
+
A manifest with a newer schema version is rejected rather than silently modified
|
|
109
|
+
by an older BatchWizard release.
|
|
110
|
+
|
|
111
|
+
## Submission reconciliation
|
|
112
|
+
|
|
113
|
+
Submission crosses the local manifest and a remote provider. BatchWizard writes a
|
|
114
|
+
`submitting` intent first, then updates that same row after the provider returns
|
|
115
|
+
its batch ID. OpenAI receives the intent ID as batch metadata, allowing
|
|
116
|
+
`batchwizard reconcile INTENT` to find and attach a batch after a crash or
|
|
117
|
+
uncertain connection failure. Anthropic does not expose equivalent batch
|
|
118
|
+
metadata; identify the matching job by creation time with `list-jobs`, then run
|
|
119
|
+
`batchwizard reconcile INTENT --batch-id MSGBATCH_ID`. After confirming that no
|
|
120
|
+
provider batch exists, remove the local intent with
|
|
121
|
+
`batchwizard reconcile INTENT --discard`.
|