Single Call & Gateway
Convenience surfaces for running individual calls through the full resilience
pipeline — error-type-aware retries, the coordinated rate-limit cooldown, and
token accounting — without constructing a
ParallelBatchProcessor.
call/call_result— one prompt, one result. No queue, workers, or result stream are created.LLMGateway— a long-lived, shared entry point for the request path: many concurrent callers, one coordinated cooldown, concurrency bounded by a semaphore.
For large bulk jobs, keep using ParallelBatchProcessor /
process_prompts; these surfaces are for single calls, not batches.
Example
from async_batch_llm import OpenAIModel, OpenAIStrategy, call, call_result, LLMGateway, ProcessorConfig
strategy = OpenAIStrategy(OpenAIModel.from_api_key("gpt-4o-mini"))
# One prompt through the full pipeline — no queue, workers, or result stream.
summary = await call(strategy, "Summarize: ...") # output, or raises
# A long-lived, shared entry point for a web service's request path.
async with LLMGateway(
strategy,
config=ProcessorConfig(max_workers=5),
max_pending=100, # admission cap: reject instantly when saturated
submit_timeout=30, # per-caller latency budget (seconds)
) as gw:
reply = await gw.submit("Answer this one request")
Failure semantics
call()/LLMGateway.submit()re-raise the provider's own exception on failure, preserving its type.LLMCallErroris raised only when no provider exception was preserved — the gateway'smax_pending/submit_timeoutrejections.call_result()/LLMGateway.submit_result()never raise for a request failure; they return the fullWorkItemResult— inspectsuccess,error,token_usage,metadata, and the originatingexception.
result = await call_result(strategy, "Summarize: ...")
if not result.success:
print(result.error, result.exception) # exception preserves the provider type
The gateway drains already-admitted requests on aclose() (the async with
exit) before cleaning up the shared strategy, so in-flight calls aren't cut off
by shutdown. Set submit_timeout to bound how long shutdown waits for that
drain — otherwise it waits as long as the admitted work takes.
call
async_batch_llm.call
async
call(strategy: LLMCallStrategy[TOutput], prompt: str, *, config: ProcessorConfig | None = None, error_classifier: Any = None) -> TOutput
Run one prompt and return its output, raising on failure.
Set a timeout or retry policy via config (e.g.
ProcessorConfig(timeout_per_item=20)).
Source code in src/async_batch_llm/single.py
call_result
async_batch_llm.call_result
async
call_result(strategy: LLMCallStrategy[TOutput], prompt: str, *, config: ProcessorConfig | None = None, error_classifier: Any = None) -> WorkItemResult[TOutput, Any]
Run one prompt through the full pipeline and return the WorkItemResult.
Inspect .success / .output / .error / .token_usage. Use this
when you want token accounting or to branch on failure without exceptions;
for the happy-path one-liner use :func:call.
Source code in src/async_batch_llm/single.py
LLMGateway
async_batch_llm.LLMGateway
LLMGateway(strategy: LLMCallStrategy[TOutput], *, config: ProcessorConfig | None = None, error_classifier: Any = None, max_pending: int | None = None, submit_timeout: float | None = None)
Bases: Generic[TOutput]
A shared, rate-limited entry point for single LLM calls from many callers.
Create one at startup and call :meth:submit from any number of concurrent
handlers. config.max_workers is the global concurrency budget. The
strategy is shared, so use a stateless strategy or one whose prepare()
yields a reusable resource.
A long rate_limit.cooldown_seconds stalls all callers during a 429 —
tune it via config to trade upstream protection against latency.
Two opt-in knobs bound the request path under load (both off by default, so
the default is pure backpressure — callers beyond max_workers wait):
max_pending— an admission cap. With it set, at mostmax_workers + max_pendingrequests may be in flight (running or waiting on the semaphore); further submits are rejected instantly with a failed result instead of growing an unbounded waiter list. Bounds memory.submit_timeout— a per-caller latency bound (seconds). An admitted request that hasn't completed within the budget (e.g. stuck behind a cooldown) is cancelled and returns a failed result. A firing timeout cancels an in-flight request that might have been about to succeed — the right trade for a web latency budget where the client is already gone.
Source code in src/async_batch_llm/gateway.py
aclose
async
Stop accepting work and run the strategy's cleanup(). Idempotent.
Marks the gateway closed (new submits raise immediately), then waits for
already-admitted requests — running or still waiting on the semaphore —
to drain before cleaning up the shared strategy, whose clients/caches may
still be in use. Set submit_timeout to bound how long an admitted
request can hold up shutdown (otherwise the drain waits indefinitely).
Concurrent callers share one drain+cleanup task, so every
await gw.aclose() returns only once cleanup has actually completed —
not just because another call set the closed flag first. The shared task
is shield-ed, so cancelling one caller's aclose() doesn't abort
the drain/cleanup for the others.
Source code in src/async_batch_llm/gateway.py
submit
async
Submit one prompt and await its output, raising on failure.
Blocks on the semaphore when the pool is saturated (backpressure), unless
max_pending is set (then an over-cap submit raises immediately). A
rejected/timed-out submit raises :class:LLMCallError. timeout
overrides the gateway's submit_timeout for this call.
Source code in src/async_batch_llm/gateway.py
submit_result
async
Like :meth:submit but returns the WorkItemResult instead of raising.
Gives access to token_usage / metadata and lets callers branch
on .success without exception handling. A request rejected by the
admission cap or cut off by the timeout comes back as a failed result
(success=False) rather than raising.
timeout overrides the gateway's submit_timeout for this call.
Source code in src/async_batch_llm/gateway.py
LLMCallError
async_batch_llm.LLMCallError
Bases: RuntimeError
Raised by :func:call / LLMGateway.submit when a request fails and no
originating provider exception was preserved.
When the failure carried a provider exception — the usual case for an
exhausted-retry or permanent error — that exact exception is re-raised
instead, preserving its type. LLMCallError is the fallback for failures
that produce no exception: the gateway's admission-cap (max_pending) and
submit_timeout rejections.
Carries the originating :class:WorkItemResult on .result so callers
can still reach token usage, metadata, and the error string.