Strategies API Reference
LLMCallStrategy
async_batch_llm.LLMCallStrategy
Bases: ABC, Generic[TOutput]
Abstract base class for LLM call strategies.
A strategy encapsulates how LLM calls are made, including: - Resource initialization (caches, clients) - Call execution with retries - Resource cleanup
The framework calls: 1. prepare() once per unique strategy instance before its first execution 2. execute() for each attempt (including retries) 3. cleanup() once per prepared strategy when the processor exits or shuts down
cleanup
async
Clean up resources when the processor exits or shuts down.
Called once per prepared strategy instance, not once per work item.
Use this for: - Closing connections/sessions - Releasing locks - Logging final metrics - Deleting temporary files
Do NOT use this for: - Deleting caches intended for reuse across runs - Destructive cleanup that prevents resource reuse
Note on Caches (v0.2.0):
For reusable resources like Gemini caches with TTLs, consider letting
them expire naturally to enable cost savings across multiple pipeline
runs. See GeminiCachedModel for an example.
Default: no-op
Source code in src/async_batch_llm/llm_strategies.py
dry_run
async
Return mock output for dry-run mode (testing without API calls).
Override this method to provide realistic mock data for testing. Default implementation returns placeholder values that may not match your output type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
The prompt that would have been sent to the LLM |
required |
Returns:
| Type | Description |
|---|---|
tuple[TOutput, TokenUsage]
|
Tuple of (mock_output, mock_token_usage) |
Default behavior: - Returns string "[DRY-RUN] Mock output" as output - Returns mock token usage: 100 input, 50 output, 150 total
Source code in src/async_batch_llm/llm_strategies.py
execute
abstractmethod
async
execute(prompt: str, attempt: int, timeout: float, state: RetryState | None = None) -> tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
Execute an LLM call for the given attempt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
The prompt to send to the LLM |
required |
attempt
|
int
|
Which logical retry attempt this is (1, 2, 3, ...).
Guarantee: rate-limit errors do NOT advance this number. If a
call is throttled (429 / coordinated cooldown), the framework
retries the same |
required |
timeout
|
float
|
Maximum time to wait for response (seconds) |
required |
state
|
RetryState | None
|
Optional retry state that persists across attempts (v0.3.0) |
None
|
Returns:
| Type | Description |
|---|---|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
Either a 2-tuple |
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
TokenUsage dict with optional keys |
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
|
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
pass |
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
supported for backward compatibility but will be removed in a |
tuple[TOutput, TokenUsage] | tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
future release; built-in strategies all return the 3-tuple shape. |
Raises:
| Type | Description |
|---|---|
Exception
|
Any exception propagated to trigger a retry (if retryable) or a permanent failure. |
Note (v0.3.0): The state parameter allows strategies to maintain state across retry attempts for multi-stage retry patterns. See RetryState documentation for examples.
Source code in src/async_batch_llm/llm_strategies.py
on_error
async
Handle errors that occur during execute().
Called by the framework when execute() raises an exception, before deciding whether to retry. This allows strategies to: - Inspect the error type to adjust retry behavior - Store error information for use in next attempt - Modify prompts based on validation errors - Track error patterns across attempts
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
Exception
|
The exception that was raised during execute() |
required |
attempt
|
int
|
Which attempt number failed (1, 2, 3, ...) |
required |
state
|
RetryState | None
|
Optional retry state that persists across attempts (v0.3.0) |
None
|
Default: no-op
Example (v0.2.0): async def on_error(self, exception: Exception, attempt: int) -> None: # Store last error for smart retry logic self.last_error = exception
# Track validation errors vs network errors
if isinstance(exception, ValidationError):
self.should_escalate_model = True
Example (v0.3.0 with retry state): async def on_error( self, exception: Exception, attempt: int, state: RetryState | None = None ) -> None: if state: # Track validation errors separately from other errors if isinstance(exception, ValidationError): count = state.get('validation_failures', 0) + 1 state.set('validation_failures', count) # Save partial results for recovery if hasattr(exception, 'partial_data'): state.set('partial_data', exception.partial_data)
Source code in src/async_batch_llm/llm_strategies.py
prepare
async
Initialize resources before making any LLM calls.
Called once per unique strategy instance before the first work item using that instance executes. Use this to set up shared caches, clients, etc. Per-item retry state belongs in execute()/on_error() via RetryState.
Default: no-op
Source code in src/async_batch_llm/llm_strategies.py
recommended_error_classifier
Return the error classifier best suited to this strategy's provider.
:class:~async_batch_llm.ParallelBatchProcessor calls this to
auto-select a classifier when the caller didn't pass error_classifier
explicitly — it reads the recommendation off the work items' strategies.
Returns None by default ("no preference"), which lets the framework
fall back to :class:DefaultErrorClassifier. Provider strategies
(GeminiStrategy, OpenAIStrategy, …) override this to return their
matching classifier. An explicit error_classifier= on the processor
always wins over this recommendation.
Source code in src/async_batch_llm/llm_strategies.py
ModelStrategy
Shared base for the provider-named strategies below; delegates to an
LLMModel. Use directly for a custom model you don't want a dedicated
subclass for.
async_batch_llm.ModelStrategy
ModelStrategy(model: LLMModel, response_parser: Callable[[LLMResponse], TOutput] | None = None, *, temperature: float | None = 0.0, generation_config: dict[str, Any] | None = None)
Bases: LLMCallStrategy[TOutput]
Base strategy for any provider exposed as an :class:LLMModel.
Holds the machinery shared by all model-backed strategies: the model
reference, an optional response parser, lifecycle delegation to
:class:ManagedLLMModel, and an execute() that calls
model.generate(), parses the response, and forwards
LLMResponse.metadata as the third tuple element.
The provider-named subclasses (:class:GeminiStrategy,
:class:OpenAIStrategy, :class:OpenRouterStrategy) are thin shells over
this base — they exist so users can pick the strategy named after the
provider they're using. Use this base directly for a custom
:class:LLMModel you don't want to name a dedicated subclass for.
Added in v0.10.0 (extracted from the formerly-duplicated provider strategy classes).
Initialize strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
LLMModel
|
An LLMModel instance (e.g., GeminiModel, OpenAIModel). |
required |
response_parser
|
Callable[[LLMResponse], TOutput] | None
|
Function to parse LLMResponse into TOutput. Defaults to
returning |
None
|
temperature
|
float | None
|
Default sampling temperature. Pass |
0.0
|
generation_config
|
dict[str, Any] | None
|
Provider-specific config forwarded to
|
None
|
Source code in src/async_batch_llm/llm_strategies.py
cleanup
async
Delegate to model.cleanup() if the model has a managed lifecycle.
execute
async
execute(prompt: str, attempt: int, timeout: float, state: RetryState | None = None) -> tuple[TOutput, TokenUsage, dict[str, Any] | None]
Execute the LLM call via the model and parse the response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
The prompt to send to the LLM. |
required |
attempt
|
int
|
Which retry attempt this is (1, 2, 3, ...). |
required |
timeout
|
float
|
Maximum time for response (enforced by the framework). |
required |
state
|
RetryState | None
|
Optional retry state for cross-attempt persistence. |
None
|
Returns:
| Type | Description |
|---|---|
TOutput
|
3-tuple |
TokenUsage
|
is forwarded from |
dict[str, Any] | None
|
model, safety_ratings, etc.). Added the metadata slot in v0.10.0; the |
tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
framework still accepts the legacy 2-tuple shape from custom |
tuple[TOutput, TokenUsage, dict[str, Any] | None]
|
strategies via a compat shim. |
Source code in src/async_batch_llm/llm_strategies.py
prepare
async
Delegate to model.prepare() if the model has a managed lifecycle.
PydanticAIStrategy
async_batch_llm.PydanticAIStrategy
Bases: LLMCallStrategy[TOutput]
Strategy for using PydanticAI agents.
This strategy wraps a PydanticAI agent, providing a clean interface for batch processing. The agent handles all model interaction, validation, and parsing.
Best for: Structured output with Pydantic models, using PydanticAI's features.
Initialize PydanticAI strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
Agent[None, TOutput]
|
Configured PydanticAI agent |
required |
Source code in src/async_batch_llm/llm_strategies.py
dry_run
async
Return mock output based on agent's result_type for dry-run mode.
Source code in src/async_batch_llm/llm_strategies.py
execute
async
execute(prompt: str, attempt: int, timeout: float, state: RetryState | None = None) -> tuple[TOutput, TokenUsage, dict[str, Any] | None]
Execute PydanticAI agent call.
Note: timeout parameter is provided for information but timeout enforcement is handled by the framework wrapping this call in asyncio.wait_for().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
The prompt to send to the LLM |
required |
attempt
|
int
|
Which retry attempt this is (1, 2, 3, ...) |
required |
timeout
|
float
|
Maximum time to wait for response (seconds) |
required |
state
|
RetryState | None
|
Optional retry state (v0.3.0, unused by this strategy) |
None
|
Returns:
| Type | Description |
|---|---|
TOutput
|
3-tuple |
TokenUsage
|
object doesn't expose provider-side metadata uniformly, so |
dict[str, Any] | None
|
|
Source code in src/async_batch_llm/llm_strategies.py
GeminiStrategy
async_batch_llm.GeminiStrategy
GeminiStrategy(model: LLMModel, response_parser: Callable[[LLMResponse], TOutput] | None = None, *, temperature: float | None = 0.0, generation_config: dict[str, Any] | None = None)
Bases: ModelStrategy[TOutput]
Strategy for calling a Gemini model and parsing the response.
Accepts an LLMModel (e.g., GeminiModel or GeminiCachedModel) and a response parser. The model handles the API call and token extraction; the strategy handles response parsing and lifecycle delegation.
For caching, use GeminiStrategy(model=GeminiCachedModel(...)).
v0.6.0: Accepts LLMModel instead of raw client + model string.
Example
model = GeminiModel("gemini-2.5-flash", client) strategy = GeminiStrategy(model, response_parser=lambda r: r.text)
With caching:
cached_model = GeminiCachedModel("gemini-2.5-flash", client, cached_content=[...]) strategy = GeminiStrategy(cached_model, response_parser=lambda r: r.text)
Source code in src/async_batch_llm/llm_strategies.py
OpenAIStrategy
async_batch_llm.OpenAIStrategy
OpenAIStrategy(model: LLMModel, response_parser: Callable[[LLMResponse], TOutput] | None = None, *, temperature: float | None = 0.0, generation_config: dict[str, Any] | None = None)
Bases: ModelStrategy[TOutput]
Strategy for calling an OpenAI-compatible model and parsing the response.
Accepts an LLMModel (typically OpenAIModel) and an optional response parser. The model handles the API call and token extraction; the strategy handles response parsing and lifecycle delegation.
Added in v0.9.0.
Example
model = OpenAIModel.from_api_key("gpt-4o-mini", api_key="sk-...") strategy = OpenAIStrategy(model)
Structured output via response_parser:
strategy = OpenAIStrategy( ... model, ... response_parser=lambda r: MyModel.model_validate_json(r.text), ... )
Source code in src/async_batch_llm/llm_strategies.py
OpenRouterStrategy
async_batch_llm.OpenRouterStrategy
OpenRouterStrategy(model: LLMModel, response_parser: Callable[[LLMResponse], TOutput] | None = None, *, temperature: float | None = 0.0, generation_config: dict[str, Any] | None = None)
Bases: ModelStrategy[TOutput]
Strategy for calling an OpenRouter-backed model and parsing the response.
Functionally identical to :class:OpenAIStrategy (both delegate to an
LLMModel via :class:ModelStrategy); the separate class exists for
provider-named symmetry so users can pick the strategy named after the
provider they're using. For OpenRouter, LLMResponse.metadata typically
includes provider (the upstream that served the request), model
(the actually-routed model), and finish_reason.
Added in v0.9.0.
Example
model = OpenRouterModel.from_api_key( ... "anthropic/claude-haiku-4-5", api_key="sk-or-...", ... ) strategy = OpenRouterStrategy(model)
Source code in src/async_batch_llm/llm_strategies.py
DeepSeekStrategy
async_batch_llm.DeepSeekStrategy
DeepSeekStrategy(model: LLMModel, response_parser: Callable[[LLMResponse], TOutput] | None = None, *, temperature: float | None = 0.0, generation_config: dict[str, Any] | None = None)
Bases: ModelStrategy[TOutput]
Strategy for calling a DeepSeek model and parsing the response.
Functionally identical to :class:OpenAIStrategy (both delegate to an
LLMModel via :class:ModelStrategy); the separate class exists for
provider-named symmetry. Pair it with :class:DeepSeekModel, which
surfaces DeepSeek's native cache-hit token counts.
Added in v0.10.0.
Example
model = DeepSeekModel.from_api_key("deepseek-chat", api_key="sk-...") strategy = DeepSeekStrategy(model)
Source code in src/async_batch_llm/llm_strategies.py
Models
GeminiModel
async_batch_llm.GeminiModel
GeminiModel(model: str, client: Client, *, safety_settings: list[dict[str, Any]] | None = None, system_instruction: str | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
LLM model backed by the Google Gemini API.
Wraps a genai.Client and model name, handling API calls, token extraction, and response normalization. Implements the LLMModel protocol.
Example
client = genai.Client(api_key="...") model = GeminiModel("gemini-2.5-flash", client) response = await model.generate("Hello!") print(response.text, response.input_tokens)
Added in v0.6.0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Model name (e.g., "gemini-3.1-flash-lite-preview"). |
required |
client
|
Client
|
Initialized genai.Client. |
required |
safety_settings
|
list[dict[str, Any]] | None
|
Default safety settings for all calls. |
None
|
system_instruction
|
str | None
|
Default system instruction (overridable per-call). |
None
|
metadata_extractors
|
list[MetadataExtractor] | None
|
Optional hooks that contribute extra keys to
|
None
|
Source code in src/async_batch_llm/models.py
generate
async
generate(prompt: str | list[Any], *, temperature: float | None = 0.0, system_instruction: str | None = None, config: dict[str, Any] | None = None) -> LLMResponse
Generate a response from Gemini.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str | list[Any]
|
Text prompt or list of content parts (multimodal). |
required |
temperature
|
float | None
|
Sampling temperature. Pass |
0.0
|
system_instruction
|
str | None
|
Override default system instruction. |
None
|
config
|
dict[str, Any] | None
|
Additional provider-specific config entries. |
None
|
Returns:
| Type | Description |
|---|---|
LLMResponse
|
Normalized LLMResponse. |
Source code in src/async_batch_llm/models.py
GeminiCachedModel
async_batch_llm.GeminiCachedModel
GeminiCachedModel(model: str, client: Client, cached_content: list[Content], *, cache_ttl_seconds: int = 3600, cache_renewal_buffer_seconds: int = 300, auto_renew: bool = True, cache_tags: dict[str, str] | None = None, safety_settings: list[dict[str, Any]] | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
LLM model backed by Google Gemini with context caching.
Wraps a genai.Client with cache lifecycle management. Implements the ManagedLLMModel protocol: call prepare() before first use, cleanup() when done.
IMPORTANT — share one instance across work items. Create ONE GeminiCachedModel and reuse it across every LLMWorkItem that should share the cached context. Constructing a new instance per item defeats caching entirely and can cost 10× more. The framework calls prepare() exactly once per unique instance, so sharing is the intended lifecycle. See examples/example_llm_strategies.py for the pattern.
This provides 70-90% cost savings when shared correctly.
Example
model = GeminiCachedModel( ... "gemini-2.5-flash", client, ... cached_content=[system_instruction, context_docs], ... ) await model.prepare() # finds or creates cache response = await model.generate("Process this") await model.cleanup() # preserves cache for reuse
Added in v0.6.0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Model name (e.g., "gemini-2.5-flash"). |
required |
client
|
Client
|
Initialized genai.Client. |
required |
cached_content
|
list[Content]
|
Content to cache (system instructions, documents). |
required |
cache_ttl_seconds
|
int
|
Cache TTL in seconds (default: 3600 = 1 hour). |
3600
|
cache_renewal_buffer_seconds
|
int
|
Renew this many seconds before expiry (default: 300 = 5 minutes). |
300
|
auto_renew
|
bool
|
Auto-renew expired caches in generate() (default: True). |
True
|
cache_tags
|
dict[str, str] | None
|
Tags for precise cache matching. Encoded into the cache's
|
None
|
safety_settings
|
list[dict[str, Any]] | None
|
Default safety settings for all calls. |
None
|
metadata_extractors
|
list[MetadataExtractor] | None
|
Optional hooks that contribute extra keys to
|
None
|
Source code in src/async_batch_llm/models.py
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | |
cleanup
async
Preserve cache for reuse (does not delete). Idempotent.
Source code in src/async_batch_llm/models.py
delete_cache
async
Explicitly delete the cache.
Safe to call concurrently: the cache lock serializes delete attempts so the provider API fires at most once, and late callers that arrive after the cache is cleared return silently.
Source code in src/async_batch_llm/models.py
generate
async
generate(prompt: str | list[Any], *, temperature: float | None = 0.0, system_instruction: str | None = None, config: dict[str, Any] | None = None) -> LLMResponse
Generate a response using the cached context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str | list[Any]
|
Text prompt or multimodal content parts. |
required |
temperature
|
float | None
|
Sampling temperature. Pass |
0.0
|
system_instruction
|
str | None
|
Not supported with caching — raises ValueError. |
None
|
config
|
dict[str, Any] | None
|
Additional provider-specific config entries. |
None
|
Returns:
| Type | Description |
|---|---|
LLMResponse
|
Normalized LLMResponse. |
Source code in src/async_batch_llm/models.py
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | |
prepare
async
Find or create the Gemini cache. Idempotent.
Source code in src/async_batch_llm/models.py
OpenAICompatibleModel
async_batch_llm.OpenAICompatibleModel
OpenAICompatibleModel(model: str, client: AsyncOpenAI, *, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
Base class for OpenAI chat-completions-compatible providers.
Wraps an AsyncOpenAI client pointed at any chat-completions endpoint
(OpenAI itself, OpenRouter, DeepSeek, HuggingFace Inference Providers,
Together, Fireworks, local vLLM, etc.). Subclasses customize the default
base URL, the install-extras hint, the env var read by
:meth:from_api_key, and optionally the token/metadata extractors.
Implements the ManagedLLMModel protocol — :meth:cleanup closes the
underlying AsyncOpenAI client when this model owns it (i.e. it was
constructed via :meth:from_api_key). User-provided clients are left
alone.
Added in v0.9.0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Provider model id (e.g. "gpt-4o-mini" or "anthropic/claude-haiku-4-5"). |
required |
client
|
AsyncOpenAI
|
Initialized AsyncOpenAI (point |
required |
system_instruction
|
str | None
|
Default system message prepended to each call.
Per-call |
None
|
extra_headers
|
dict[str, str] | None
|
Default headers forwarded on every call (e.g.
OpenRouter's |
None
|
extra_body
|
dict[str, Any] | None
|
Default extra body fields forwarded on every call
(e.g. OpenRouter |
None
|
metadata_extractors
|
list[MetadataExtractor] | None
|
Optional hooks that contribute extra keys to
|
None
|
Source code in src/async_batch_llm/models.py
cleanup
async
Close the underlying AsyncOpenAI client if this model owns it.
Models constructed directly with OpenAIModel(model, client=...)
do NOT own the client — the caller is expected to close it. Models
constructed via :meth:from_api_key do own the client and close
it here so repeated processor runs don't leak httpx connections.
Source code in src/async_batch_llm/models.py
from_api_key
classmethod
from_api_key(model: str, api_key: str | None = None, *, base_url: str | None = None, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, json_mode: bool = False, max_connections: int | None = None, metadata_extractors: list[MetadataExtractor] | None = None, **client_kwargs: Any) -> TM
Build the model with a freshly-constructed AsyncOpenAI client.
The returned model owns the client — its connections are released
when the framework calls :meth:cleanup (typically when the
ParallelBatchProcessor exits).
Uses base_url (if provided) or the class's _default_base_url.
Pass client_kwargs to forward additional kwargs (timeout,
max_retries, http_client, etc.) to the SDK constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Provider model id. |
required |
api_key
|
str | None
|
API key. If
|
None
|
json_mode
|
bool
|
When |
False
|
max_connections
|
int | None
|
Size of the underlying httpx connection pool
(both |
None
|
Source code in src/async_batch_llm/models.py
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | |
generate
async
generate(prompt: str | list[Any], *, temperature: float | None = 0.0, system_instruction: str | None = None, config: dict[str, Any] | None = None) -> LLMResponse
Call client.chat.completions.create and normalize the response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str | list[Any]
|
A string (becomes a single user message) or a list of
OpenAI-shaped message dicts (passed through unchanged — used
for multimodal content and Anthropic-via-OpenRouter
|
required |
temperature
|
float | None
|
Sampling temperature. Pass |
0.0
|
system_instruction
|
str | None
|
Per-call override for the system message. |
None
|
config
|
dict[str, Any] | None
|
Per-call extra kwargs forwarded to the SDK call (merged
over the instance's |
None
|
Returns:
| Type | Description |
|---|---|
LLMResponse
|
Normalized LLMResponse. |
Source code in src/async_batch_llm/models.py
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 | |
OpenAIModel
async_batch_llm.OpenAIModel
OpenAIModel(model: str, client: AsyncOpenAI, *, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
Bases: OpenAICompatibleModel
LLM model backed by OpenAI's chat completions API.
Uses the OpenAI SDK's default base URL (https://api.openai.com/v1).
OpenAI's automatic prompt cache surfaces in cached_input_tokens for
prompts longer than ~1024 tokens.
Example
model = OpenAIModel.from_api_key("gpt-4o-mini", api_key="sk-...") response = await model.generate("Hello!") print(response.text, response.cached_input_tokens)
Added in v0.9.0.
Source code in src/async_batch_llm/models.py
OpenRouterModel
async_batch_llm.OpenRouterModel
OpenRouterModel(model: str, client: AsyncOpenAI, *, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
Bases: OpenAICompatibleModel
LLM model backed by OpenRouter (https://openrouter.ai).
OpenRouter exposes a unified OpenAI-compatible API for many upstream
providers (Anthropic, OpenAI, Google, Mistral, DeepSeek, etc.). Model
ids are prefixed with the provider, e.g. "anthropic/claude-haiku-4-5".
Caching is provider-dependent:
- OpenAI / Gemini (implicit) / DeepSeek — automatic;
cached_input_tokensis populated when the upstream cache hits. - Anthropic — opt-in. Pass
promptas a list of message dicts withcache_control: {"type": "ephemeral"}markers on the blocks you want cached.
Example
model = OpenRouterModel.from_api_key( ... "anthropic/claude-haiku-4-5", ... api_key="sk-or-...", ... referer="https://my-app.example.com", ... title="My App", ... ) response = await model.generate("Hello!")
Added in v0.9.0.
Source code in src/async_batch_llm/models.py
from_api_key
classmethod
from_api_key(model: str, api_key: str | None = None, *, base_url: str | None = None, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, json_mode: bool = False, max_connections: int | None = None, referer: str | None = None, title: str | None = None, metadata_extractors: list[MetadataExtractor] | None = None, **client_kwargs: Any) -> OpenRouterModel
Build an OpenRouterModel.
If api_key is None, reads OPENROUTER_API_KEY from the
environment and raises ValueError if neither is set. (The
OpenAI SDK doesn't know about OPENROUTER_API_KEY, so we have
to read it ourselves rather than relying on the SDK's default.)
referer and title map to OpenRouter's optional
HTTP-Referer and X-Title headers (used for app attribution
on openrouter.ai's leaderboard).
Source code in src/async_batch_llm/models.py
DeepSeekModel
async_batch_llm.DeepSeekModel
DeepSeekModel(model: str, client: AsyncOpenAI, *, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, thinking: bool | None = None, metadata_extractors: list[MetadataExtractor] | None = None)
Bases: OpenAICompatibleModel
LLM model backed by DeepSeek's OpenAI-compatible API.
Points at https://api.deepseek.com and reads
DEEPSEEK_API_KEY in :meth:from_api_key. Model ids are bare DeepSeek
names, e.g. "deepseek-chat" or "deepseek-reasoner".
DeepSeek's automatic context cache reports hits at the top level of the
usage object (prompt_cache_hit_tokens / prompt_cache_miss_tokens)
rather than under OpenAI's nested prompt_tokens_details.cached_tokens —
so this subclass overrides :meth:_extract_tokens to surface them in
cached_input_tokens. Use :attr:CachedTokenRates.DEEPSEEK (~2%) when
computing billable tokens.
(Calling DeepSeek through OpenRouter uses :class:OpenRouterModel
instead; the native cache fields aren't reliably forwarded there, which is
why direct access via this class gives better cache telemetry.)
Thinking mode. DeepSeek's V4 models (deepseek-v4-flash /
deepseek-v4-pro) default to thinking, which for a batch
classification job is a surprising, expensive default — thinking can emit
several times the output tokens (and cost, and latency) of non-thinking.
Pass thinking=False to force non-thinking mode explicitly rather than
relying on the deepseek-chat (non-thinking) / deepseek-reasoner
(thinking) aliases, which DeepSeek is deprecating. Under the hood this sends
extra_body={"thinking": {"type": "disabled"}}.
Example
model = DeepSeekModel.from_api_key( ... "deepseek-v4-flash", api_key="sk-...", thinking=False ... ) response = await model.generate("Hello!") print(response.text, response.cached_input_tokens)
Added in v0.10.0.
See :class:OpenAICompatibleModel; adds the DeepSeek thinking
toggle (True/False to force thinking on/off, None for the
model default).
Source code in src/async_batch_llm/models.py
from_api_key
classmethod
from_api_key(model: str, api_key: str | None = None, *, base_url: str | None = None, system_instruction: str | None = None, extra_headers: dict[str, str] | None = None, extra_body: dict[str, Any] | None = None, json_mode: bool = False, max_connections: int | None = None, thinking: bool | None = None, metadata_extractors: list[MetadataExtractor] | None = None, **client_kwargs: Any) -> DeepSeekModel
Build a DeepSeekModel; reads DEEPSEEK_API_KEY when api_key is
None. Adds the thinking toggle (see the class docstring) on top of
the shared :meth:OpenAICompatibleModel.from_api_key arguments.
Source code in src/async_batch_llm/models.py
Protocols
LLMModel
async_batch_llm.LLMModel
Bases: Protocol
Protocol for LLM model instances that can generate responses.
Implementations wrap a specific provider's client and model configuration, handling API calls and response normalization. Strategies call generate() without needing to know about provider-specific details.
Added in v0.6.0.
generate
async
generate(prompt: str | list[Any], *, temperature: float | None = 0.0, system_instruction: str | None = None, config: dict[str, Any] | None = None) -> LLMResponse
Generate a response from the LLM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str | list[Any]
|
Text prompt, or list of content parts for multimodal input. |
required |
temperature
|
float | None
|
Sampling temperature (0.0 = deterministic). Pass |
0.0
|
system_instruction
|
str | None
|
System instruction override (None = use default). |
None
|
config
|
dict[str, Any] | None
|
Provider-specific configuration (e.g., response_mime_type). |
None
|
Returns:
| Type | Description |
|---|---|
LLMResponse
|
Normalized LLMResponse with text, token counts, and metadata. |
Source code in src/async_batch_llm/core/protocols.py
ManagedLLMModel
async_batch_llm.ManagedLLMModel
Bases: LLMModel, Protocol
LLMModel with lifecycle management (e.g., caching).
Models that need one-time setup (creating a cache) or cleanup implement this protocol. The strategy delegates prepare/cleanup calls to the model.
Added in v0.6.0.
cleanup
async
LLMResponse
async_batch_llm.LLMResponse
dataclass
LLMResponse(text: str, input_tokens: int, output_tokens: int, total_tokens: int, cached_input_tokens: int = 0, metadata: dict[str, Any] | None = None, raw: Any = None)
Bases: ProviderOutputViews
Normalized response from any LLM provider.
Returned by LLMModel.generate(). Provides a provider-agnostic interface so strategies don't need to know about Gemini, OpenAI, etc. response formats.
Attributes:
| Name | Type | Description |
|---|---|---|
text |
str
|
The response text content. |
input_tokens |
int
|
Number of input/prompt tokens. |
output_tokens |
int
|
Number of output/completion tokens. |
total_tokens |
int
|
Total tokens used. |
cached_input_tokens |
int
|
Input tokens served from cache (0 if no caching). |
metadata |
dict[str, Any] | None
|
Provider-specific metadata (safety ratings, finish reason, etc.).
The keys |
raw |
Any
|
The raw provider response object, for edge cases. |
Added in v0.6.0.