batchor 0.0.1__tar.gz → 0.0.4__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.
- {batchor-0.0.1 → batchor-0.0.4}/.agents/plugins/marketplace.json +12 -0
- {batchor-0.0.1 → batchor-0.0.4}/.github/workflows/ci.yml +1 -1
- {batchor-0.0.1 → batchor-0.0.4}/PKG-INFO +116 -61
- {batchor-0.0.1 → batchor-0.0.4}/README.md +109 -58
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/ARCHITECTURE.md +55 -15
- batchor-0.0.4/docs/design_docs/GEMINI_BATCHING.md +96 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/OPENAI_BATCHING.md +16 -2
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/ROADMAP.md +2 -1
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/STORAGE_AND_RUNS.md +30 -4
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/STORAGE_MIGRATIONS.md +2 -1
- {batchor-0.0.1 → batchor-0.0.4}/docs/doc-map.md +2 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/getting-started/cli.md +43 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/getting-started/installation.md +37 -1
- {batchor-0.0.1 → batchor-0.0.4}/docs/getting-started/python-api.md +43 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/index.md +2 -2
- {batchor-0.0.1 → batchor-0.0.4}/docs/reference/api.md +10 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/smoke-test.md +43 -2
- {batchor-0.0.1 → batchor-0.0.4}/mkdocs.yml +2 -1
- batchor-0.0.4/plugins/batchor/.codex-plugin/plugin.json +44 -0
- batchor-0.0.4/plugins/batchor/.mcp.json +10 -0
- batchor-0.0.4/plugins/batchor/scripts/batchor_user_mcp.py +205 -0
- batchor-0.0.4/plugins/batchor/skills/use-batchor/SKILL.md +57 -0
- batchor-0.0.4/plugins/batchor/skills/use-batchor/agents/openai.yaml +4 -0
- batchor-0.0.4/plugins/batchor/skills/use-batchor/references/cli-workflows.md +45 -0
- batchor-0.0.4/plugins/batchor/skills/use-batchor/references/python-pipelines.md +64 -0
- {batchor-0.0.1 → batchor-0.0.4}/plugins/batchor-agent-tools/scripts/batchor_repo_mcp.py +10 -3
- {batchor-0.0.1 → batchor-0.0.4}/pyproject.toml +12 -4
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/__init__.py +8 -2
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/cli.py +93 -3
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/core/enums.py +16 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/core/exceptions.py +8 -2
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/core/models.py +155 -1
- batchor-0.0.4/src/batchor/core/responses.py +60 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/core/types.py +10 -7
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/providers/base.py +33 -3
- batchor-0.0.4/src/batchor/providers/gemini.py +554 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/providers/registry.py +20 -7
- batchor-0.0.4/src/batchor/runtime/__init__.py +1 -0
- batchor-0.0.4/src/batchor/runtime/artifacts.py +177 -0
- batchor-0.0.4/src/batchor/runtime/context.py +164 -0
- batchor-0.0.4/src/batchor/runtime/ingestion.py +380 -0
- batchor-0.0.4/src/batchor/runtime/polling.py +552 -0
- batchor-0.0.4/src/batchor/runtime/results.py +119 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/runtime/retry.py +63 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/runtime/run_handle.py +113 -46
- batchor-0.0.4/src/batchor/runtime/runner.py +597 -0
- batchor-0.0.4/src/batchor/runtime/submission.py +561 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/runtime/validation.py +15 -68
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/sources/base.py +10 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/sources/composite.py +4 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/sources/files.py +16 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/memory.py +58 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/postgres_store.py +18 -4
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_lifecycle.py +98 -1
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_protocol.py +1 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_queries.py +3 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_results.py +1 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_schema.py +2 -1
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/state_models.py +26 -0
- batchor-0.0.4/tests/integration/test_batchor_gemini_runner.py +219 -0
- batchor-0.0.4/tests/integration/test_batchor_live_gemini.py +157 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/integration/test_batchor_runner.py +324 -105
- batchor-0.0.4/tests/unit/test_agent_tooling.py +163 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_architecture.py +31 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_cli.py +123 -1
- batchor-0.0.4/tests/unit/test_batchor_gemini_provider.py +355 -0
- batchor-0.0.4/tests/unit/test_batchor_results.py +130 -0
- batchor-0.0.4/tests/unit/test_batchor_retry.py +143 -0
- batchor-0.0.4/tests/unit/test_batchor_runtime_ingestion.py +935 -0
- batchor-0.0.4/tests/unit/test_batchor_runtime_polling.py +419 -0
- batchor-0.0.4/tests/unit/test_batchor_runtime_submission.py +425 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_sources.py +282 -0
- batchor-0.0.4/tests/unit/test_batchor_storage.py +55 -0
- batchor-0.0.4/tests/unit/test_batchor_storage_contracts.py +438 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_tokens.py +86 -0
- batchor-0.0.4/tests/unit/test_batchor_validation.py +379 -0
- {batchor-0.0.1 → batchor-0.0.4}/uv.lock +367 -1
- batchor-0.0.1/src/batchor/runtime/__init__.py +0 -1
- batchor-0.0.1/src/batchor/runtime/runner.py +0 -1015
- batchor-0.0.1/src/batchor/runtime/runner_execution.py +0 -753
- batchor-0.0.1/tests/unit/test_agent_tooling.py +0 -49
- batchor-0.0.1/tests/unit/test_batchor_retry.py +0 -29
- batchor-0.0.1/tests/unit/test_batchor_storage.py +0 -22
- batchor-0.0.1/tests/unit/test_batchor_storage_contracts.py +0 -201
- batchor-0.0.1/tests/unit/test_batchor_validation.py +0 -174
- {batchor-0.0.1 → batchor-0.0.4}/.agents/skills/batchor-dev/SKILL.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/.agents/skills/batchor-dev/agents/openai.yaml +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/.github/workflows/docs.yml +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/.github/workflows/publish.yml +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/.gitignore +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/.pre-commit-config.yaml +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/AGENTS.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/CONTRIBUTING.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/LICENSE +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/SUPPORT.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/VERSIONING.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/assets/stylesheets/extra.css +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/design_docs/BOUNDARY_AND_PHILOSOPHY.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/getting-started/use-cases.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/policies/contributing.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/policies/support.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/docs/policies/versioning.md +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/plugins/batchor-agent-tools/.codex-plugin/plugin.json +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/plugins/batchor-agent-tools/.mcp.json +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/artifacts/__init__.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/artifacts/base.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/artifacts/local.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/core/__init__.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/providers/__init__.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/providers/openai.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/py.typed +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/runtime/tokens.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/sources/__init__.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/__init__.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/postgres.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/registry.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_codec.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/sqlite_store.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/src/batchor/storage/state.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/integration/test_batchor_live_openai.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_artifacts.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_openai_provider.py +0 -0
- {batchor-0.0.1 → batchor-0.0.4}/tests/unit/test_batchor_sqlite_storage_flow.py +0 -0
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
"authentication": "ON_INSTALL"
|
|
16
16
|
},
|
|
17
17
|
"category": "Coding"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "batchor",
|
|
21
|
+
"source": {
|
|
22
|
+
"source": "local",
|
|
23
|
+
"path": "./plugins/batchor"
|
|
24
|
+
},
|
|
25
|
+
"policy": {
|
|
26
|
+
"installation": "AVAILABLE",
|
|
27
|
+
"authentication": "ON_INSTALL"
|
|
28
|
+
},
|
|
29
|
+
"category": "Coding"
|
|
18
30
|
}
|
|
19
31
|
]
|
|
20
32
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batchor
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: Structured-first
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Structured-first provider Batch runner with typed Pydantic results.
|
|
5
5
|
Project-URL: Documentation, https://github.com/AnsonDev42/batchor#readme
|
|
6
6
|
Project-URL: Repository, https://github.com/AnsonDev42/batchor
|
|
7
7
|
Project-URL: Issues, https://github.com/AnsonDev42/batchor/issues
|
|
@@ -79,7 +79,7 @@ License: # PolyForm Noncommercial License 1.0.0
|
|
|
79
79
|
|
|
80
80
|
**Use** means anything you do with the software requiring one of your licenses.
|
|
81
81
|
License-File: LICENSE
|
|
82
|
-
Keywords: batch,cli,openai,pydantic,sqlite
|
|
82
|
+
Keywords: batch,cli,gemini,openai,pydantic,sqlite
|
|
83
83
|
Classifier: Development Status :: 3 - Alpha
|
|
84
84
|
Classifier: Environment :: Console
|
|
85
85
|
Classifier: Intended Audience :: Developers
|
|
@@ -88,6 +88,7 @@ Classifier: Operating System :: OS Independent
|
|
|
88
88
|
Classifier: Programming Language :: Python :: 3
|
|
89
89
|
Classifier: Programming Language :: Python :: 3.12
|
|
90
90
|
Classifier: Programming Language :: Python :: 3.13
|
|
91
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
91
92
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
92
93
|
Requires-Python: <4,>=3.12
|
|
93
94
|
Requires-Dist: openai>=2.7.1
|
|
@@ -98,20 +99,26 @@ Requires-Dist: python-dotenv<2,>=1.0.1
|
|
|
98
99
|
Requires-Dist: sqlalchemy<3,>=2.0.43
|
|
99
100
|
Requires-Dist: tiktoken>=0.12.0
|
|
100
101
|
Requires-Dist: typer<1,>=0.16.0
|
|
102
|
+
Provides-Extra: gemini
|
|
103
|
+
Requires-Dist: google-cloud-storage<4,>=3.4.0; extra == 'gemini'
|
|
104
|
+
Requires-Dist: google-genai<2,>=1.55.0; extra == 'gemini'
|
|
101
105
|
Description-Content-Type: text/markdown
|
|
102
106
|
|
|
103
107
|
# batchor
|
|
104
108
|
|
|
105
|
-
`batchor`
|
|
109
|
+
`batchor` grew out of a recurring problem in academic research: running large datasets through LLMs in batch — reliably, reproducibly, and without reinventing the same glue code across every project. The patterns that kept emerging (durable state, typed results, safe resume after failure) were extracted into this library so they do not have to be rebuilt each time.
|
|
110
|
+
|
|
111
|
+
`batchor` is a durable provider Batch runner for Python teams that want:
|
|
106
112
|
|
|
107
113
|
- typed Pydantic results
|
|
108
114
|
- resumable durable runs
|
|
109
115
|
- replayable request artifacts
|
|
110
116
|
- deterministic source checkpoints
|
|
117
|
+
- provider-side enqueue limit controls to stay within token budgets
|
|
111
118
|
- library-first run controls
|
|
112
119
|
- a small operator CLI for CSV and JSONL jobs
|
|
113
120
|
|
|
114
|
-
It is intentionally narrow today: OpenAI
|
|
121
|
+
It is intentionally narrow today: OpenAI is the CLI default, Gemini is opt-in through `batchor[gemini]`, SQLite is the CLI durability backend, and the Python API exposes the broadest configuration surface.
|
|
115
122
|
|
|
116
123
|
## What problem it solves
|
|
117
124
|
|
|
@@ -136,6 +143,7 @@ Most OpenAI Batch examples stop at "upload a JSONL file and poll until it finish
|
|
|
136
143
|
Built-in implementations:
|
|
137
144
|
|
|
138
145
|
- `OpenAIProviderConfig` + `OpenAIBatchProvider`
|
|
146
|
+
- `GeminiProviderConfig` + `GeminiBatchProvider` for text-only Gemini Batch jobs
|
|
139
147
|
- `SQLiteStorage`
|
|
140
148
|
- `PostgresStorage` as an opt-in durable control-plane backend
|
|
141
149
|
- `MemoryStateStore`
|
|
@@ -151,9 +159,12 @@ Important constraints:
|
|
|
151
159
|
- the CLI supports file-backed inputs only
|
|
152
160
|
- users still own selecting and ordering input files or partitions
|
|
153
161
|
- the built-in CLI uses SQLite durability only
|
|
162
|
+
- the CLI supports OpenAI plus Gemini Developer API and Vertex AI text jobs
|
|
163
|
+
- Gemini support is text-only for now and does not build multimodal requests
|
|
154
164
|
- structured-output rehydration requires an importable module-level Pydantic model
|
|
155
165
|
- raw output artifacts are retained by default and must be exported before raw pruning
|
|
156
166
|
- pause/resume/cancel and incremental terminal-result APIs are library-first today
|
|
167
|
+
- OpenAI control-plane or batch-level 429 quota/billing exhaustion auto-pauses the run instead of burning attempts across the remaining backlog
|
|
157
168
|
|
|
158
169
|
## Mental model
|
|
159
170
|
|
|
@@ -173,6 +184,7 @@ Durability is split on purpose:
|
|
|
173
184
|
- the artifact store keeps replayable request JSONL and downloaded raw batch payloads
|
|
174
185
|
|
|
175
186
|
That split is what allows retries and fresh-process resume without keeping every request inline in the control-plane store.
|
|
187
|
+
On resume, existing active provider batches are polled before `batchor` materializes or submits new local work, so completed batches and retry backoff are reconciled first. Deterministic sources also use stored checkpoint completion metadata to avoid opening a fully materialized Parquet or composite source just to discover there are no rows left.
|
|
176
188
|
|
|
177
189
|
## Architecture
|
|
178
190
|
|
|
@@ -187,6 +199,7 @@ graph LR
|
|
|
187
199
|
|
|
188
200
|
subgraph providers["providers/"]
|
|
189
201
|
OpenAI["OpenAIBatchProvider"]
|
|
202
|
+
Gemini["GeminiBatchProvider"]
|
|
190
203
|
end
|
|
191
204
|
|
|
192
205
|
subgraph sources["sources/"]
|
|
@@ -206,6 +219,7 @@ graph LR
|
|
|
206
219
|
User -->|"start() / run_and_wait()"| BatchRunner
|
|
207
220
|
BatchRunner --> Run
|
|
208
221
|
BatchRunner --> OpenAI
|
|
222
|
+
BatchRunner --> Gemini
|
|
209
223
|
BatchRunner --> SQLite
|
|
210
224
|
BatchRunner --> LocalFS
|
|
211
225
|
Files -->|"BatchItem stream"| BatchRunner
|
|
@@ -241,28 +255,45 @@ Operational semantics for resume, run control, and artifact retention live in
|
|
|
241
255
|
pip install batchor
|
|
242
256
|
```
|
|
243
257
|
|
|
244
|
-
|
|
258
|
+
For Gemini Batch support, install the optional extra:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
pip install "batchor[gemini]"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Agent setup
|
|
245
265
|
|
|
246
|
-
|
|
266
|
+
Batchor keeps contributor tooling separate from the tools intended for researchers and downstream projects:
|
|
247
267
|
|
|
248
|
-
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
268
|
+
- `plugins/batchor/` is the user-facing Codex plugin. Its `$use-batchor` skill and MCP helpers turn datasets and prompts into safe CLI or Python workflows without assuming a Batchor source checkout.
|
|
269
|
+
- `.agents/skills/batchor-dev/` and `plugins/batchor-agent-tools/` are contributor-only. They teach agents how to change this repository, find design docs, and run its validation suite.
|
|
270
|
+
- `.agents/plugins/marketplace.json` registers both plugins for local development.
|
|
271
|
+
- `.vscode/mcp.json` configures the contributor MCP for this workspace.
|
|
252
272
|
|
|
253
|
-
The
|
|
273
|
+
The PyPI package remains the runtime dependency: `pip install batchor` installs the library and CLI. Agent skills and MCP configuration are distributed as the separate `batchor` plugin so Python environments do not receive Codex-specific files implicitly.
|
|
274
|
+
|
|
275
|
+
To try the user plugin from a local checkout:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
codex plugin marketplace add /path/to/batchor
|
|
279
|
+
codex plugin add batchor@batchor-local
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Start a new Codex task after installation, then ask: `Use $use-batchor to turn my CSV and research prompt into a resumable batch job.`
|
|
254
283
|
|
|
255
284
|
Supported Python versions:
|
|
256
285
|
|
|
257
286
|
- `3.12`
|
|
258
287
|
- `3.13`
|
|
288
|
+
- `3.14`
|
|
259
289
|
|
|
260
290
|
## Authentication
|
|
261
291
|
|
|
262
292
|
For Python API usage, auth resolution is:
|
|
263
293
|
|
|
264
|
-
1. explicit `OpenAIProviderConfig(api_key=...)`
|
|
265
|
-
2. ambient `OPENAI_API_KEY`
|
|
294
|
+
1. explicit provider config credentials such as `OpenAIProviderConfig(api_key=...)` or `GeminiProviderConfig(api_key=...)`
|
|
295
|
+
2. ambient provider environment variables, currently `OPENAI_API_KEY` or `GEMINI_API_KEY`
|
|
296
|
+
3. Vertex AI Application Default Credentials when `GeminiProviderConfig(vertexai=True, ...)` or `GOOGLE_GENAI_USE_VERTEXAI=true` is used
|
|
266
297
|
|
|
267
298
|
The Python library does not auto-load `.env`.
|
|
268
299
|
|
|
@@ -291,6 +322,43 @@ run = runner.run_and_wait(
|
|
|
291
322
|
print(run.results()[0].output_text)
|
|
292
323
|
```
|
|
293
324
|
|
|
325
|
+
### Gemini text job
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from batchor import BatchItem, BatchJob, BatchRunner, GeminiProviderConfig, PromptParts
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
runner = BatchRunner(storage="memory")
|
|
332
|
+
run = runner.run_and_wait(
|
|
333
|
+
BatchJob(
|
|
334
|
+
items=[BatchItem(item_id="row1", payload="Summarize this text")],
|
|
335
|
+
build_prompt=lambda item: PromptParts(prompt=item.payload),
|
|
336
|
+
provider_config=GeminiProviderConfig(
|
|
337
|
+
model="gemini-2.5-flash",
|
|
338
|
+
api_key="YOUR_GEMINI_API_KEY",
|
|
339
|
+
),
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
print(run.results()[0].output_text)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Gemini support currently builds text-only `GenerateContent` batch requests. It uses Gemini JSONL `key` values internally while keeping `batchor`'s durable item and attempt tracking unchanged.
|
|
347
|
+
|
|
348
|
+
For Vertex AI, provide a Cloud Storage staging prefix and use Application Default Credentials:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
provider_config = GeminiProviderConfig(
|
|
352
|
+
model="gemini-2.5-flash",
|
|
353
|
+
vertexai=True,
|
|
354
|
+
project="my-project",
|
|
355
|
+
location="europe-west8",
|
|
356
|
+
gcs_uri="gs://my-bucket/batchor",
|
|
357
|
+
)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Vertex AI stages JSONL input and output in that prefix. The Gemini Developer API uses inline requests for batches below 20 MB and the Files API for larger batches; `input_mode=` can override that automatic choice. Vertex request/output correlation uses a generated request label because Vertex output does not include the Developer API JSONL `key`.
|
|
361
|
+
|
|
294
362
|
### Structured output
|
|
295
363
|
|
|
296
364
|
```python
|
|
@@ -334,6 +402,8 @@ run.wait()
|
|
|
334
402
|
print(run.results()[0].output)
|
|
335
403
|
```
|
|
336
404
|
|
|
405
|
+
While waiting, `batchor` polls active batches before submitting more work. If a refresh consumes a completed batch, records terminal item state, or submits another provider batch, `Run.wait()` immediately continues draining instead of sleeping for the next poll interval.
|
|
406
|
+
|
|
337
407
|
Structured-output models are validated up front against the OpenAI strict-schema subset used by `batchor`.
|
|
338
408
|
|
|
339
409
|
- root schema must be an object
|
|
@@ -342,6 +412,8 @@ Structured-output models are validated up front against the OpenAI strict-schema
|
|
|
342
412
|
|
|
343
413
|
If you need a field to be optional in Python, model it as nullable in the schema shape OpenAI accepts rather than relying on omitted required fields.
|
|
344
414
|
|
|
415
|
+
The same `structured_output=` API is available with `GeminiProviderConfig`; batchor sends the schema through Gemini `generation_config.response_json_schema` and validates the returned JSON text with the same Pydantic model.
|
|
416
|
+
|
|
345
417
|
### Rehydrate a durable run
|
|
346
418
|
|
|
347
419
|
```python
|
|
@@ -479,6 +551,8 @@ run.cancel()
|
|
|
479
551
|
|
|
480
552
|
Run control and incremental terminal-result APIs are Python-first in this release. The CLI does not yet expose `pause`, `resume`, `cancel`, or incremental terminal-result export commands.
|
|
481
553
|
|
|
554
|
+
OpenAI insufficient-quota failures during upload/create/polling, or as a batch-level terminal error, are treated as an automatic pause with `summary().control_reason == "openai_insufficient_quota"`. Row-level insufficient-quota records inside a completed batch output stay item-scoped: those rows become retryable without consuming attempts, emit `openai_insufficient_quota` failure telemetry, and use retry backoff before resubmission. Auto-pause does not replace an in-progress cancellation.
|
|
555
|
+
|
|
482
556
|
## CLI quickstart
|
|
483
557
|
|
|
484
558
|
The CLI is intentionally narrower than the Python API:
|
|
@@ -487,6 +561,31 @@ The CLI is intentionally narrower than the Python API:
|
|
|
487
561
|
- CSV and JSONL only
|
|
488
562
|
- SQLite-backed durable runs only
|
|
489
563
|
|
|
564
|
+
OpenAI remains the default provider. For Gemini, install `batchor[gemini]` and select the backend explicitly or let `auto` follow `GOOGLE_GENAI_USE_VERTEXAI`:
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
batchor start \
|
|
568
|
+
--input input/items.jsonl \
|
|
569
|
+
--id-field id \
|
|
570
|
+
--prompt-field text \
|
|
571
|
+
--provider gemini \
|
|
572
|
+
--model gemini-2.5-flash \
|
|
573
|
+
--gemini-backend developer
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Vertex AI additionally needs a writable staging prefix:
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
batchor start \
|
|
580
|
+
--input input/items.jsonl \
|
|
581
|
+
--id-field id \
|
|
582
|
+
--prompt-field text \
|
|
583
|
+
--provider gemini \
|
|
584
|
+
--model gemini-2.5-flash \
|
|
585
|
+
--gemini-backend vertex \
|
|
586
|
+
--gcs-uri gs://my-bucket/batchor
|
|
587
|
+
```
|
|
588
|
+
|
|
490
589
|
Start a run from JSONL:
|
|
491
590
|
|
|
492
591
|
```bash
|
|
@@ -539,12 +638,13 @@ def observer(event: RunEvent) -> None:
|
|
|
539
638
|
runner = BatchRunner(observer=observer)
|
|
540
639
|
```
|
|
541
640
|
|
|
542
|
-
Current events include run creation/resume, item ingestion, batch submission/polling/completion, item completion/failure, and artifact export/prune.
|
|
641
|
+
Current events include run creation/resume, automatic quota pause, item ingestion, batch submission/polling/completion, item completion/failure, and artifact export/prune.
|
|
543
642
|
|
|
544
643
|
## Storage notes
|
|
545
644
|
|
|
546
645
|
- SQLite remains the default durable backend.
|
|
547
|
-
- `PostgresStorage` is available for shared control-plane state, but the CLI remains SQLite-only today.
|
|
646
|
+
- `PostgresStorage` is available for shared control-plane state, but the CLI remains SQLite-only today. Plain `postgresql://` and `postgres://` DSNs are normalized to the package's psycopg v3 driver URL.
|
|
647
|
+
- Item `attempt_count` means consumed provider attempts: successful submitted completions increment it, counted item-level failures increment it, and local or batch-level retry/reset paths that did not consume an item attempt leave it unchanged.
|
|
548
648
|
- Durable artifacts now go through an `ArtifactStore` seam. The built-in implementation is `LocalArtifactStore`, intended for local disk or a shared mounted volume.
|
|
549
649
|
- Fresh-process resume requeues any locally claimed but not yet submitted items before continuing, so a process crash after request-artifact persistence does not strand work in `queued_local`.
|
|
550
650
|
|
|
@@ -591,51 +691,6 @@ The default pytest configuration enforces an `85%` coverage floor.
|
|
|
591
691
|
|
|
592
692
|
GitHub Actions pull requests run:
|
|
593
693
|
|
|
594
|
-
- `test (3.12)` and `test (3.13)`: `uv run ty check src` plus `uv run pytest -q`
|
|
595
|
-
- `docs`: `uv run mkdocs build --strict`
|
|
596
|
-
- `build (3.13)`: `uv build`
|
|
597
|
-
- `postgres-contract`: `uv run pytest tests/unit/test_batchor_storage_contracts.py --no-cov -q` with an ephemeral PostgreSQL service
|
|
598
|
-
|
|
599
|
-
The live OpenAI smoke remains manual-only and is not part of required CI.
|
|
600
|
-
|
|
601
|
-
Manual live OpenAI smoke:
|
|
602
|
-
|
|
603
|
-
```bash
|
|
604
|
-
export OPENAI_API_KEY=sk-...
|
|
605
|
-
export BATCHOR_RUN_LIVE_TESTS=1
|
|
606
|
-
uv run pytest tests/integration/test_batchor_live_openai.py --no-cov -q
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
If `BATCHOR_LIVE_OPENAI_MODEL` is unset, the harness defaults to `gpt-5-nano`.
|
|
610
|
-
If `BATCHOR_LIVE_OPENAI_REASONING_EFFORT` is unset, no reasoning field is sent.
|
|
611
|
-
The live smoke also requires an OpenAI account with Batch API access and available billing quota.
|
|
612
|
-
|
|
613
|
-
## Documentation guide
|
|
614
|
-
|
|
615
|
-
- `docs/design_docs/ARCHITECTURE.md`: canonical runtime diagrams and module boundaries
|
|
616
|
-
- `docs/design_docs/STORAGE_AND_RUNS.md`: durable run, resume, and artifact lifecycle semantics
|
|
617
|
-
- `docs/getting-started/use-cases.md`: practical single-file, multi-file, and pipeline examples
|
|
618
|
-
- `docs/getting-started/python-api.md`: Python-first workflows
|
|
619
|
-
- `docs/getting-started/cli.md`: operator CLI workflows
|
|
620
|
-
- `docs/reference/api.md`: generated API surface
|
|
621
|
-
- `docs/design_docs/ARCHITECTURE.md`: package boundaries
|
|
622
|
-
- `docs/design_docs/OPENAI_BATCHING.md`: OpenAI-specific behavior
|
|
623
|
-
- `docs/design_docs/STORAGE_AND_RUNS.md`: durability, rehydration, and artifacts
|
|
624
|
-
- `docs/smoke-test.md`: minimum validation bar
|
|
625
|
-
|
|
626
|
-
## Scope
|
|
627
|
-
|
|
628
|
-
Today `batchor` is intentionally narrow:
|
|
629
|
-
|
|
630
|
-
- one built-in provider: OpenAI Batch
|
|
631
|
-
- one default durable backend: SQLite
|
|
632
|
-
- one opt-in durable backend: Postgres
|
|
633
|
-
- one ephemeral backend: in-memory storage
|
|
634
|
-
- structured output is Python API-first
|
|
635
|
-
- CLI job creation is file-backed and supports both text and structured-output jobs
|
|
636
|
-
|
|
637
|
-
Anything outside that scope should be treated as out of scope or `TBD`, not implied support.
|
|
638
|
-
|
|
639
694
|
## License
|
|
640
695
|
|
|
641
696
|
This project is licensed under PolyForm Noncommercial 1.0.0. See [LICENSE](LICENSE).
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# batchor
|
|
2
2
|
|
|
3
|
-
`batchor`
|
|
3
|
+
`batchor` grew out of a recurring problem in academic research: running large datasets through LLMs in batch — reliably, reproducibly, and without reinventing the same glue code across every project. The patterns that kept emerging (durable state, typed results, safe resume after failure) were extracted into this library so they do not have to be rebuilt each time.
|
|
4
|
+
|
|
5
|
+
`batchor` is a durable provider Batch runner for Python teams that want:
|
|
4
6
|
|
|
5
7
|
- typed Pydantic results
|
|
6
8
|
- resumable durable runs
|
|
7
9
|
- replayable request artifacts
|
|
8
10
|
- deterministic source checkpoints
|
|
11
|
+
- provider-side enqueue limit controls to stay within token budgets
|
|
9
12
|
- library-first run controls
|
|
10
13
|
- a small operator CLI for CSV and JSONL jobs
|
|
11
14
|
|
|
12
|
-
It is intentionally narrow today: OpenAI
|
|
15
|
+
It is intentionally narrow today: OpenAI is the CLI default, Gemini is opt-in through `batchor[gemini]`, SQLite is the CLI durability backend, and the Python API exposes the broadest configuration surface.
|
|
13
16
|
|
|
14
17
|
## What problem it solves
|
|
15
18
|
|
|
@@ -34,6 +37,7 @@ Most OpenAI Batch examples stop at "upload a JSONL file and poll until it finish
|
|
|
34
37
|
Built-in implementations:
|
|
35
38
|
|
|
36
39
|
- `OpenAIProviderConfig` + `OpenAIBatchProvider`
|
|
40
|
+
- `GeminiProviderConfig` + `GeminiBatchProvider` for text-only Gemini Batch jobs
|
|
37
41
|
- `SQLiteStorage`
|
|
38
42
|
- `PostgresStorage` as an opt-in durable control-plane backend
|
|
39
43
|
- `MemoryStateStore`
|
|
@@ -49,9 +53,12 @@ Important constraints:
|
|
|
49
53
|
- the CLI supports file-backed inputs only
|
|
50
54
|
- users still own selecting and ordering input files or partitions
|
|
51
55
|
- the built-in CLI uses SQLite durability only
|
|
56
|
+
- the CLI supports OpenAI plus Gemini Developer API and Vertex AI text jobs
|
|
57
|
+
- Gemini support is text-only for now and does not build multimodal requests
|
|
52
58
|
- structured-output rehydration requires an importable module-level Pydantic model
|
|
53
59
|
- raw output artifacts are retained by default and must be exported before raw pruning
|
|
54
60
|
- pause/resume/cancel and incremental terminal-result APIs are library-first today
|
|
61
|
+
- OpenAI control-plane or batch-level 429 quota/billing exhaustion auto-pauses the run instead of burning attempts across the remaining backlog
|
|
55
62
|
|
|
56
63
|
## Mental model
|
|
57
64
|
|
|
@@ -71,6 +78,7 @@ Durability is split on purpose:
|
|
|
71
78
|
- the artifact store keeps replayable request JSONL and downloaded raw batch payloads
|
|
72
79
|
|
|
73
80
|
That split is what allows retries and fresh-process resume without keeping every request inline in the control-plane store.
|
|
81
|
+
On resume, existing active provider batches are polled before `batchor` materializes or submits new local work, so completed batches and retry backoff are reconciled first. Deterministic sources also use stored checkpoint completion metadata to avoid opening a fully materialized Parquet or composite source just to discover there are no rows left.
|
|
74
82
|
|
|
75
83
|
## Architecture
|
|
76
84
|
|
|
@@ -85,6 +93,7 @@ graph LR
|
|
|
85
93
|
|
|
86
94
|
subgraph providers["providers/"]
|
|
87
95
|
OpenAI["OpenAIBatchProvider"]
|
|
96
|
+
Gemini["GeminiBatchProvider"]
|
|
88
97
|
end
|
|
89
98
|
|
|
90
99
|
subgraph sources["sources/"]
|
|
@@ -104,6 +113,7 @@ graph LR
|
|
|
104
113
|
User -->|"start() / run_and_wait()"| BatchRunner
|
|
105
114
|
BatchRunner --> Run
|
|
106
115
|
BatchRunner --> OpenAI
|
|
116
|
+
BatchRunner --> Gemini
|
|
107
117
|
BatchRunner --> SQLite
|
|
108
118
|
BatchRunner --> LocalFS
|
|
109
119
|
Files -->|"BatchItem stream"| BatchRunner
|
|
@@ -139,28 +149,45 @@ Operational semantics for resume, run control, and artifact retention live in
|
|
|
139
149
|
pip install batchor
|
|
140
150
|
```
|
|
141
151
|
|
|
142
|
-
|
|
152
|
+
For Gemini Batch support, install the optional extra:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pip install "batchor[gemini]"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Agent setup
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
Batchor keeps contributor tooling separate from the tools intended for researchers and downstream projects:
|
|
145
161
|
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
162
|
+
- `plugins/batchor/` is the user-facing Codex plugin. Its `$use-batchor` skill and MCP helpers turn datasets and prompts into safe CLI or Python workflows without assuming a Batchor source checkout.
|
|
163
|
+
- `.agents/skills/batchor-dev/` and `plugins/batchor-agent-tools/` are contributor-only. They teach agents how to change this repository, find design docs, and run its validation suite.
|
|
164
|
+
- `.agents/plugins/marketplace.json` registers both plugins for local development.
|
|
165
|
+
- `.vscode/mcp.json` configures the contributor MCP for this workspace.
|
|
150
166
|
|
|
151
|
-
The
|
|
167
|
+
The PyPI package remains the runtime dependency: `pip install batchor` installs the library and CLI. Agent skills and MCP configuration are distributed as the separate `batchor` plugin so Python environments do not receive Codex-specific files implicitly.
|
|
168
|
+
|
|
169
|
+
To try the user plugin from a local checkout:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
codex plugin marketplace add /path/to/batchor
|
|
173
|
+
codex plugin add batchor@batchor-local
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Start a new Codex task after installation, then ask: `Use $use-batchor to turn my CSV and research prompt into a resumable batch job.`
|
|
152
177
|
|
|
153
178
|
Supported Python versions:
|
|
154
179
|
|
|
155
180
|
- `3.12`
|
|
156
181
|
- `3.13`
|
|
182
|
+
- `3.14`
|
|
157
183
|
|
|
158
184
|
## Authentication
|
|
159
185
|
|
|
160
186
|
For Python API usage, auth resolution is:
|
|
161
187
|
|
|
162
|
-
1. explicit `OpenAIProviderConfig(api_key=...)`
|
|
163
|
-
2. ambient `OPENAI_API_KEY`
|
|
188
|
+
1. explicit provider config credentials such as `OpenAIProviderConfig(api_key=...)` or `GeminiProviderConfig(api_key=...)`
|
|
189
|
+
2. ambient provider environment variables, currently `OPENAI_API_KEY` or `GEMINI_API_KEY`
|
|
190
|
+
3. Vertex AI Application Default Credentials when `GeminiProviderConfig(vertexai=True, ...)` or `GOOGLE_GENAI_USE_VERTEXAI=true` is used
|
|
164
191
|
|
|
165
192
|
The Python library does not auto-load `.env`.
|
|
166
193
|
|
|
@@ -189,6 +216,43 @@ run = runner.run_and_wait(
|
|
|
189
216
|
print(run.results()[0].output_text)
|
|
190
217
|
```
|
|
191
218
|
|
|
219
|
+
### Gemini text job
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from batchor import BatchItem, BatchJob, BatchRunner, GeminiProviderConfig, PromptParts
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
runner = BatchRunner(storage="memory")
|
|
226
|
+
run = runner.run_and_wait(
|
|
227
|
+
BatchJob(
|
|
228
|
+
items=[BatchItem(item_id="row1", payload="Summarize this text")],
|
|
229
|
+
build_prompt=lambda item: PromptParts(prompt=item.payload),
|
|
230
|
+
provider_config=GeminiProviderConfig(
|
|
231
|
+
model="gemini-2.5-flash",
|
|
232
|
+
api_key="YOUR_GEMINI_API_KEY",
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
print(run.results()[0].output_text)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Gemini support currently builds text-only `GenerateContent` batch requests. It uses Gemini JSONL `key` values internally while keeping `batchor`'s durable item and attempt tracking unchanged.
|
|
241
|
+
|
|
242
|
+
For Vertex AI, provide a Cloud Storage staging prefix and use Application Default Credentials:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
provider_config = GeminiProviderConfig(
|
|
246
|
+
model="gemini-2.5-flash",
|
|
247
|
+
vertexai=True,
|
|
248
|
+
project="my-project",
|
|
249
|
+
location="europe-west8",
|
|
250
|
+
gcs_uri="gs://my-bucket/batchor",
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Vertex AI stages JSONL input and output in that prefix. The Gemini Developer API uses inline requests for batches below 20 MB and the Files API for larger batches; `input_mode=` can override that automatic choice. Vertex request/output correlation uses a generated request label because Vertex output does not include the Developer API JSONL `key`.
|
|
255
|
+
|
|
192
256
|
### Structured output
|
|
193
257
|
|
|
194
258
|
```python
|
|
@@ -232,6 +296,8 @@ run.wait()
|
|
|
232
296
|
print(run.results()[0].output)
|
|
233
297
|
```
|
|
234
298
|
|
|
299
|
+
While waiting, `batchor` polls active batches before submitting more work. If a refresh consumes a completed batch, records terminal item state, or submits another provider batch, `Run.wait()` immediately continues draining instead of sleeping for the next poll interval.
|
|
300
|
+
|
|
235
301
|
Structured-output models are validated up front against the OpenAI strict-schema subset used by `batchor`.
|
|
236
302
|
|
|
237
303
|
- root schema must be an object
|
|
@@ -240,6 +306,8 @@ Structured-output models are validated up front against the OpenAI strict-schema
|
|
|
240
306
|
|
|
241
307
|
If you need a field to be optional in Python, model it as nullable in the schema shape OpenAI accepts rather than relying on omitted required fields.
|
|
242
308
|
|
|
309
|
+
The same `structured_output=` API is available with `GeminiProviderConfig`; batchor sends the schema through Gemini `generation_config.response_json_schema` and validates the returned JSON text with the same Pydantic model.
|
|
310
|
+
|
|
243
311
|
### Rehydrate a durable run
|
|
244
312
|
|
|
245
313
|
```python
|
|
@@ -377,6 +445,8 @@ run.cancel()
|
|
|
377
445
|
|
|
378
446
|
Run control and incremental terminal-result APIs are Python-first in this release. The CLI does not yet expose `pause`, `resume`, `cancel`, or incremental terminal-result export commands.
|
|
379
447
|
|
|
448
|
+
OpenAI insufficient-quota failures during upload/create/polling, or as a batch-level terminal error, are treated as an automatic pause with `summary().control_reason == "openai_insufficient_quota"`. Row-level insufficient-quota records inside a completed batch output stay item-scoped: those rows become retryable without consuming attempts, emit `openai_insufficient_quota` failure telemetry, and use retry backoff before resubmission. Auto-pause does not replace an in-progress cancellation.
|
|
449
|
+
|
|
380
450
|
## CLI quickstart
|
|
381
451
|
|
|
382
452
|
The CLI is intentionally narrower than the Python API:
|
|
@@ -385,6 +455,31 @@ The CLI is intentionally narrower than the Python API:
|
|
|
385
455
|
- CSV and JSONL only
|
|
386
456
|
- SQLite-backed durable runs only
|
|
387
457
|
|
|
458
|
+
OpenAI remains the default provider. For Gemini, install `batchor[gemini]` and select the backend explicitly or let `auto` follow `GOOGLE_GENAI_USE_VERTEXAI`:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
batchor start \
|
|
462
|
+
--input input/items.jsonl \
|
|
463
|
+
--id-field id \
|
|
464
|
+
--prompt-field text \
|
|
465
|
+
--provider gemini \
|
|
466
|
+
--model gemini-2.5-flash \
|
|
467
|
+
--gemini-backend developer
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Vertex AI additionally needs a writable staging prefix:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
batchor start \
|
|
474
|
+
--input input/items.jsonl \
|
|
475
|
+
--id-field id \
|
|
476
|
+
--prompt-field text \
|
|
477
|
+
--provider gemini \
|
|
478
|
+
--model gemini-2.5-flash \
|
|
479
|
+
--gemini-backend vertex \
|
|
480
|
+
--gcs-uri gs://my-bucket/batchor
|
|
481
|
+
```
|
|
482
|
+
|
|
388
483
|
Start a run from JSONL:
|
|
389
484
|
|
|
390
485
|
```bash
|
|
@@ -437,12 +532,13 @@ def observer(event: RunEvent) -> None:
|
|
|
437
532
|
runner = BatchRunner(observer=observer)
|
|
438
533
|
```
|
|
439
534
|
|
|
440
|
-
Current events include run creation/resume, item ingestion, batch submission/polling/completion, item completion/failure, and artifact export/prune.
|
|
535
|
+
Current events include run creation/resume, automatic quota pause, item ingestion, batch submission/polling/completion, item completion/failure, and artifact export/prune.
|
|
441
536
|
|
|
442
537
|
## Storage notes
|
|
443
538
|
|
|
444
539
|
- SQLite remains the default durable backend.
|
|
445
|
-
- `PostgresStorage` is available for shared control-plane state, but the CLI remains SQLite-only today.
|
|
540
|
+
- `PostgresStorage` is available for shared control-plane state, but the CLI remains SQLite-only today. Plain `postgresql://` and `postgres://` DSNs are normalized to the package's psycopg v3 driver URL.
|
|
541
|
+
- Item `attempt_count` means consumed provider attempts: successful submitted completions increment it, counted item-level failures increment it, and local or batch-level retry/reset paths that did not consume an item attempt leave it unchanged.
|
|
446
542
|
- Durable artifacts now go through an `ArtifactStore` seam. The built-in implementation is `LocalArtifactStore`, intended for local disk or a shared mounted volume.
|
|
447
543
|
- Fresh-process resume requeues any locally claimed but not yet submitted items before continuing, so a process crash after request-artifact persistence does not strand work in `queued_local`.
|
|
448
544
|
|
|
@@ -489,51 +585,6 @@ The default pytest configuration enforces an `85%` coverage floor.
|
|
|
489
585
|
|
|
490
586
|
GitHub Actions pull requests run:
|
|
491
587
|
|
|
492
|
-
- `test (3.12)` and `test (3.13)`: `uv run ty check src` plus `uv run pytest -q`
|
|
493
|
-
- `docs`: `uv run mkdocs build --strict`
|
|
494
|
-
- `build (3.13)`: `uv build`
|
|
495
|
-
- `postgres-contract`: `uv run pytest tests/unit/test_batchor_storage_contracts.py --no-cov -q` with an ephemeral PostgreSQL service
|
|
496
|
-
|
|
497
|
-
The live OpenAI smoke remains manual-only and is not part of required CI.
|
|
498
|
-
|
|
499
|
-
Manual live OpenAI smoke:
|
|
500
|
-
|
|
501
|
-
```bash
|
|
502
|
-
export OPENAI_API_KEY=sk-...
|
|
503
|
-
export BATCHOR_RUN_LIVE_TESTS=1
|
|
504
|
-
uv run pytest tests/integration/test_batchor_live_openai.py --no-cov -q
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
If `BATCHOR_LIVE_OPENAI_MODEL` is unset, the harness defaults to `gpt-5-nano`.
|
|
508
|
-
If `BATCHOR_LIVE_OPENAI_REASONING_EFFORT` is unset, no reasoning field is sent.
|
|
509
|
-
The live smoke also requires an OpenAI account with Batch API access and available billing quota.
|
|
510
|
-
|
|
511
|
-
## Documentation guide
|
|
512
|
-
|
|
513
|
-
- `docs/design_docs/ARCHITECTURE.md`: canonical runtime diagrams and module boundaries
|
|
514
|
-
- `docs/design_docs/STORAGE_AND_RUNS.md`: durable run, resume, and artifact lifecycle semantics
|
|
515
|
-
- `docs/getting-started/use-cases.md`: practical single-file, multi-file, and pipeline examples
|
|
516
|
-
- `docs/getting-started/python-api.md`: Python-first workflows
|
|
517
|
-
- `docs/getting-started/cli.md`: operator CLI workflows
|
|
518
|
-
- `docs/reference/api.md`: generated API surface
|
|
519
|
-
- `docs/design_docs/ARCHITECTURE.md`: package boundaries
|
|
520
|
-
- `docs/design_docs/OPENAI_BATCHING.md`: OpenAI-specific behavior
|
|
521
|
-
- `docs/design_docs/STORAGE_AND_RUNS.md`: durability, rehydration, and artifacts
|
|
522
|
-
- `docs/smoke-test.md`: minimum validation bar
|
|
523
|
-
|
|
524
|
-
## Scope
|
|
525
|
-
|
|
526
|
-
Today `batchor` is intentionally narrow:
|
|
527
|
-
|
|
528
|
-
- one built-in provider: OpenAI Batch
|
|
529
|
-
- one default durable backend: SQLite
|
|
530
|
-
- one opt-in durable backend: Postgres
|
|
531
|
-
- one ephemeral backend: in-memory storage
|
|
532
|
-
- structured output is Python API-first
|
|
533
|
-
- CLI job creation is file-backed and supports both text and structured-output jobs
|
|
534
|
-
|
|
535
|
-
Anything outside that scope should be treated as out of scope or `TBD`, not implied support.
|
|
536
|
-
|
|
537
588
|
## License
|
|
538
589
|
|
|
539
590
|
This project is licensed under PolyForm Noncommercial 1.0.0. See [LICENSE](LICENSE).
|