Upgrading from v0.25.0 to v0.26.0
v0.26.0 improves shared-client ownership, persistence failures, provider compatibility, admission, lifecycle handling, and diagnostics. Review the changes below before upgrading: several defaults and accepted inputs change. Existing JSONL and SQLite schemas remain compatible; no schema migration is required. Recovery of incomplete JSONL tails changes as described below.
Inputs, configuration, and public names
- Pass an
LLMCallStrategyinstance to work items and convenience entry points. Duck-typed strategies must inherit that interface or useCallableStrategy. Wrap model clients withOpenAIStrategyand agents withPydanticAIStrategy. - A
CallableStrategycallback must accept the prompt plusattempt,timeout, andstatekeywords. An inspectable incompatible signature now fails at construction. - Pass an iterable of strings or
(item_id, prompt[, context])tuples toprocess_prompts/process_stream. A top-level string, bytes object or mapping is rejected. Bare integer entries still become strings with a deprecation warning; convert them explicitly. Other invalid entries report their index. - Duplicate IDs in
process_promptswarn once. Use unique IDs orsubmission_indexwhen distinguishing results; IDs are not ordering keys. - Numeric configuration fields reject booleans and non-finite values. Integer fields
also reject integral floats: use
max_workers=5, notmax_workers=5.0. - A bare
ExponentialBackoffStrategy()now starts at 300 seconds, with multiplier 1.5, matchingRateLimitConfig(previously 60 seconds and 2.0). To preserve the former settings, passinitial_cooldown=60, backoff_multiplier=2.0explicitly. LLMCallPoolis now the class name and capacity-warning label.LLMGatewayremains an alias. Code that inspects the class name or matches warning text should update.
CallOutcome.token_usage accepts either input_tokens / output_tokens or OpenAI's
prompt_tokens / completion_tokens. Do not mix naming schemes. Stored counters use
canonical names. testing.FakeStrategy and FakeRateLimitError provide provider-free
success, latency and failure simulation; see the testing guide.
Processor and shared-model lifecycle
Use each processor for one batch or streaming run. Mixing start() and process_all(),
restarting after close, or calling finish() after closure without an earlier finish
now raises. Repeated start() while streaming and repeated finish() following a normal
finish remain no-ops. Use a new processor for the next run.
add_work() raises BatchAdmissionClosedError after finish, shutdown or abort.
Blocked producers are woken when admission closes. A concurrently committed enqueue is
retained; an uncommitted submission is rejected without a submission index. For multiple
producers, join them before calling finish().
Overlapping hosts on the same event loop share strategy preparation and release leases on cleanup; actual cleanup waits for the last owner. Ordinary preparation failures are shared with waiting hosts. Built-in owned OpenAI-compatible and Gemini models also protect clients shared by distinct strategies. Sequential reuse reopens owned clients. This changes prepare/cleanup call counts for shared instances; hooks should remain idempotent.
Caller-provided OpenAI-compatible HTTP transports are no longer closed by model cleanup; the caller must close them. Automatic connection-pool resizing is refused once provider execution has begun, including after cleanup until reopening. Choose capacity up front. Always close hosts explicitly: garbage collection cannot perform asynchronous cleanup. See the cleanup contract for abandoned-lease behavior.
Provider retries, categories, and token usage
OpenAI-compatible from_api_key() clients now default to max_retries=0, so ABL owns
retry counts and token accounting. Explicit SDK retry settings still win and survive
reopening. Applications that relied on layered SDK retries should choose that intentionally.
Audit category-based metrics and abort_on_error_categories:
- Provider timeouts preserve the provider exception and category rather than becoming
framework attempt timeouts. Default, OpenAI and OpenRouter classifiers use
api_timeout; Gemini recognizes a bareTimeoutErrorastimeoutwithis_timeout=True. - Terminal rate-limit budget exhaustion uses
rate_limit_retries_exceededand is no longer marked as a rate-limit event. Empty responses useempty_response. - Gemini structured-output failures use the shared structured-output categories.
- OpenAI
insufficient_quotaand unambiguous Gemini daily quota exhaustion are not retried. PydanticAI HTTP failures use status classification, including explicit insufficient quota. - OpenRouter “No allowed providers” is retryable only with status 502/503;
other statuses use
client_error. - A broken custom classifier produces a non-retryable
classifier_erroritem; the result retains the originating provider exception.
Gemini output totals now include thinking tokens, also exposed as
metadata["reasoning_tokens"]; input totals include tool-use prompt tokens. Token totals
can therefore increase without an increase in visible response text.
Gemini caches are matched using normalized request-content fingerprints and provider expiry.
Existing caches without the reserved abl-content tag are not adopted: first use can create
an additional paid cache. Escaped display names longer than 128 characters are rejected.
A narrowly recognized missing-cache response triggers one replacement/retry.
Admission and deadlines
The optional ProcessorConfig.quota_burst_seconds limits short bursts; leaving it unset
preserves the full-minute default. Oversized TPM reservations may borrow future refill.
See token-aware admission for bounds and reconciliation.
A cooldown detected just before provider start releases unstarted quota/capacity and re-enters FIFO admission without consuming an attempt. Admission events can repeat during this process. Stale-generation 429s no longer escalate an already newer cooldown. An item whose rate-limit budget is exhausted returns without waiting for the shared cooldown.
When the actual retry delay cannot fit the remaining item deadline, ABL returns
ItemDeadlineExceeded immediately. Its message/cause preserve the last provider error;
the attempt timing retains the original category, while abort matching uses the final
framework_total_item_timeout category. Item deadlines start separately at logical-item
pickup, so an item can legitimately start after a peer times out if its own budget remains.
Artifacts, recovery, and events
JSONL inspection remains read-only and excludes all unterminated tails. Writable resume truncates them, even when the final bytes form complete JSON without the terminating newline. A recognizable partial initial manifest can be recovered; unrelated files are not truncated. After a write failure, further appends fail closed. JSONL remains single-writer only.
Built-in stores convert record serialization failures into item-level
artifact_serialization_error results, keeping the output in memory and writing a minimal,
non-replayable fallback record. Invalid data includes lone Unicode surrogates in outputs,
metadata or context; diagnostics identify the offending path. Stored error text escapes such
surrogates. I/O and format failures remain fatal; guardrail audit rules are unchanged.
Custom stores retain their existing fatal serialization behavior.
Serialization fallback error_counts now use ArtifactSerializationError, rather than a
separate key containing each item's message. Decoders run only for successful, included,
non-null output and for non-null context. Replayed output without a decoder is JSON data;
it can differ in type from a fresh typed model. Use an encoder/decoder pair when typed
reconstruction is required; see results and artifacts.
Each finalized item emits exactly one ITEM_COMPLETED, ITEM_FAILED, or ITEM_REPLAYED
after checkpointing and before fail-fast BATCH_ABORTED. Filtering and failed middleware
recovery count as failures. Fatal checkpoint errors or cancellation before finalization emit
no terminal item event. Replay emits only ITEM_REPLAYED; replayed failures contribute their
error type to metrics. Live provider-success event duration/tokens keep final-attempt semantics.
See the observer contracts.
Middleware recovery includes failed-attempt tokens in addition to usage supplied by the hook;
do not return those same failed tokens again. Invalid after_process return types now preserve
the successful result with a warning rather than repeating the provider call.
Diagnostic tracebacks and bounded logging
Batch failures log diagnostic user-code stacks before detaching traceback frames.
call, call_result and LLMCallPool retain original frames, so holding failed results can
also retain callback locals. Routine retryable network failures do not gain diagnostic stacks.
Repeated terminal errors share a diagnostic and summary. Tracking is bounded to 1,000 keys with 200-character message prefixes; overflow errors remain visible at ERROR without being retained. Messages differing only after character 200 deduplicate. See callable integration.
Explicitly deferred follow-ups
These existing behaviors are not changed in v0.26.0:
Since v0.27: #177, #178 and #181 are fixed. See the v0.27 migration guide.
- #177: default message-based
classification can mistake a numeric
429in user error text for a rate limit. Supply a custom classifier when application exceptions need stricter classification. - #178:
REUSE_ALLcan replaytoken_estimator_requiredfailures after configuration is corrected. UseREUSE_SUCCESSESto re-execute failures when resuming that run. - #181: an item expiring
while waiting for capacity can report zero admission wait and no timing timeout
category. Its final
framework_total_item_timeouterror remains correct. - Newer SDK coverage and refactoring follow-ups are tracked in #176, #179, and #180. Dependency PR #166 is deferred; this release keeps the reviewed dependency set and existing OpenAI/OpenRouter SDK floors.
For upgrades from before v0.25, also read the v0.25 and v0.24 migration guides.