LBI: LLM Batch Inference
Many LLM providers offer discounts (often 50%) for requests submitted through their batch APIs instead of the regular synchronous endpoints. Each provider's batch API looks different, though: different upload formats, different status fields, different ways of matching results back to requests. This project wraps OpenAI, Anthropic, Mistral, and Gemini's batch APIs in one normalized interface, so you can submit a batch, poll for completion, and read back results the same way regardless of provider.
Features
- One interface, four providers -
BaseBatchProvidergives OpenAI, Anthropic, Mistral, and Gemini the samecreate_batch/get_batch/get_results/cancel_batch/list_batchesshape. - Normalized request/response models - build a
BatchRequestonce and run it against any provider; results always come back asBatchResult, matched to requests bycustom_id. - Multi-target fan-out - send the same batch to several provider/model combinations at once and collect results independently, with per-target overrides and no single failure blocking the rest (
run_batches_and_wait). - Async-first, with a one-call convenience -
submit_and_waithandles submission, polling, and result retrieval in a single await; the individual steps are also available when you need to submit and collect in separate processes.
Supported providers
| Provider | Class | Backing API |
|---|---|---|
| OpenAI | OpenAIBatchProvider |
Batch API (file upload), or Chat Completions when inline |
| Anthropic | AnthropicBatchProvider |
Message Batches API |
| Mistral | MistralBatchProvider |
Batch API (file upload) |
| Gemini | GeminiBatchProvider |
Batch API (inline requests) |
Installation
pip install llm-batch-inference
or with uv:
uv add llm-batch-inference
The package is imported as lbi.
Configuration
Every provider accepts an explicit api_key, and falls back to its standard environment variable if one isn't passed:
| Provider | Environment variable |
|---|---|
| OpenAI | OPENAI_API_KEY |
| Anthropic | ANTHROPIC_API_KEY |
| Mistral | MISTRAL_API_KEY |
| Gemini | GEMINI_API_KEY (or GOOGLE_API_KEY) |
See .env.example for a template.
Quickstart
lbi.v1 is the library's current public API.
import asyncio
import lbi.v1 as lbi
async def main() -> None:
provider = lbi.create_provider('openai', api_key='sk-...')
requests = [
lbi.BatchRequest(
custom_id='1',
messages=[
lbi.Message(role=lbi.Role.USER, content='Say hi in French.'),
],
),
lbi.BatchRequest(
custom_id='2',
messages=[
lbi.Message(role=lbi.Role.USER, content='Say hi in German.'),
],
),
]
results = await provider.submit_and_wait(
requests,
model='gpt-4o-mini',
batch_filename='greetings.jsonl',
)
for result in results:
print(result.custom_id, result.status, result.content)
asyncio.run(main())
submit_and_wait is a convenience wrapper around three steps you can also call individually. If you want to submit a batch in one process and collect it in another later on:
info = await provider.create_batch(
requests, model='gpt-4o-mini', batch_filename='greetings.jsonl'
)
info = await provider.wait_for_completion(info.batch_id) # polls until terminal
results = await provider.get_results(info.batch_id)
get_batch, cancel_batch, and list_batches are also available for checking on or managing batches.
Running one batch across multiple providers or models
import lbi.v1 as lbi
targets = [
lbi.BatchTarget(
provider=lbi.create_provider('openai', api_key=OPENAI_KEY),
model='gpt-4o-mini',
),
lbi.BatchTarget(
provider=lbi.create_provider('anthropic', api_key=ANTHROPIC_KEY),
model='claude-haiku-4-5-20251001',
),
]
results = await lbi.run_batches_and_wait(requests, targets)
for r in results:
print(r.label, r.status, r.error)
A failure on one target (submission, polling, or downloading results) is recorded on that target's MultiBatchResult and never stops the others. Call lbi.pre_validate(requests, targets) first to catch per-target constraint violations (e.g. a model that rejects a non-default temperature) before submitting anything.
Per-target overrides let you tune requests for one target without touching the shared request list:
lbi.BatchTarget(
provider=lbi.create_provider('anthropic', api_key=ANTHROPIC_KEY),
model='claude-sonnet-5',
overrides=lbi.BatchOverrides(temperature=None), # this model rejects a set temperature
)
Error handling
All exceptions raised by LBI subclass lbi.BatchLLMError:
| Exception | Raised when |
|---|---|
BatchCreationError |
The provider rejected the batch at submission time |
BatchNotFoundError |
The batch ID is unknown to the provider |
ResultsNotReadyError |
Results were requested before the batch reached a terminal state |
BatchPollTimeoutError |
wait_for_completion / submit_and_wait exceeded its timeout |
BatchCancelledError |
An operation was attempted on a cancelled batch |
ProviderError |
The underlying provider SDK returned an unexpected error |
Running Tests
To run the unit tests:
uv run pytest
To run the integration tests:
uv run pytest -m integration
Use the verbose flag -v to get more detailed output and use the -n flag to run multiple tests in parallel (-n auto runs one test per CPU core in parallel).
Example:
uv run pytest -n auto -v -m integration
You can also run specific tests and fixtures by keyword:
uv run pytest -n auto -v -m integration -k 'full_batch_lifecycle and mistral'
Publishing to PyPI
-
Bump the version in
[pyproject.toml](./pyproject.toml):version = "0.2.x" -
Build the package:
uv build -
Publish package:
uv publish -
Add tag in Git:
git add pyproject.toml git commit -m "Bump version to 0.2.x" git tag v0.2.x git push origin main --tags
Contact
If you have any questions or feedback, feel free to connect with me on LinkedIn at linkedin.com/in/daniel-di-giovanni/ or send me an email at dannyjdigio@gmail.com.
Metadata
Release files for llm-batch-inference 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| llm_batch_inference-0.2.1.tar.gz | 20.8 kB | Without provenance | Details for llm_batch_inference-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| llm_batch_inference-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for llm_batch_inference-0.2.1-py3-none-any.whl |
Total release size: 50.7 kB
Release files / llm_batch_inference-0.2.1.tar.gz
| Download URL | llm_batch_inference-0.2.1.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7785e0bc67b6805ed262fe6aacb35049b028aae5f67d807852aac95790c03827
|
|
BLAKE2b-256 checksum How to use checksums |
6e3b4ddc5d35d01aebab2580e1525eb6497f340b1e491e3d83dc68add4892393
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / llm_batch_inference-0.2.1-py3-none-any.whl
| Download URL | llm_batch_inference-0.2.1-py3-none-any.whl |
|---|---|
| Size | 29.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e416f694a219384093f0edb63c5e24fe6ac3af50e8ff2040a8b483f313fd334c
|
|
BLAKE2b-256 checksum How to use checksums |
94423a330f1a2bd52ce4660b1c5d1744a7fa7155c3647cd65fe3c4e15d5a3e89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for llm-batch-inference 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.