Upgrading from v0.26.0 to v0.27.0
v0.27.0 deprecates the names 1.0 removes (v0.28 adds two more; see the v0.28 migration guide). It changes several defaults, so review the sections below before upgrading. JSONL and SQLite artifact schemas are unchanged, and v0.26 artifacts remain readable.
OpenAIModel uses the Responses API
OpenAIModel now calls OpenAI's Responses API by default and sends store=False.
Most Chat-style request fields are translated for you (see
Responses API).
To keep the previous request behavior, opt out:
model = OpenAIModel.from_api_key("gpt-4o-mini", api_surface="chat_completions")
strategy = OpenAIStrategy(model, temperature=0.0) # also restores the old temperature
- SDK floor. The
[openai]extra now requiresopenai>=1.66.2, the first release that parses Responses reasoning summaries. - Rejected request fields.
n,stop,seed,presence_penalty,frequency_penalty,logit_bias, legacyfunctions/function_call,stream=Trueandbackground=TrueraiseValueErroron the Responses surface. So do Chat content parts without a Responses equivalent. Use the opt-out if you rely on them. - Function-call-only replies now succeed with
text="",finish_reason="tool_calls", and the calls inresult.tool_calls. A response parser that expects text should handle the empty string. The Chat surface still raisesEmptyResponseError. - Result metadata and
raw. On the Responses surfacemetadata["logprobs"]is a list of per-token entries rather than the Chat logprobs object,tool_calls[].idis the Responsescall_id, andLLMResponse.rawis a Responses object, not aChatCompletion. New keys areapi_surface("responses"),provider_request_id,response_statusand, when the model refuses,refusal. Code that parses logprobs orrawcan branch onmetadata.get("api_surface") == "responses". Both surfaces now addmetadata["reasoning_tokens"]when the provider reports it; v0.26 didn't. - Other servers. If you pointed
OpenAIModelat a non-OpenAI server withbase_urlorOPENAI_BASE_URL(vLLM, a proxy, an Azure-style endpoint), switch toOpenAICompatibleModel, or tollm("openai-compatible:<model>", base_url=...), which stay on Chat Completions. The alternative isapi_surface="chat_completions". There is no automatic fallback. - Checkpoints. Automatic artifact identity for
OpenAIModelnow includes the API surface when it's the Responses API, so its results are stored and replayed separately: - A v0.26 checkpoint resumed with the new default re-runs its items; there is no error.
- With
api_surface="chat_completions"the identity is unchanged and v0.26 results replay. - If you pass an explicit
ArtifactIdentity, it's used as-is: version it yourself if the surface change should invalidate replay. OpenRouterModel,DeepSeekModelandOpenAICompatibleModelare unaffected.
Temperature is omitted by default
Built-in models, strategies, llm() and the LLMModel protocol now default to
temperature=None, which sends no temperature so each provider's default applies.
The old default was 0.0, which some models or reasoning modes reject. Pass
temperature=0.0 explicitly to keep the previous behavior.
- Custom
LLMModelimplementations.ModelStrategy(and sollm()and every built-in strategy) always passestemperature=tomodel.generate(). A custom model wrapped in one of them now receivestemperature=Nonewhere it used to get0.0. TreatNoneas "omit the parameter" rather than forwarding it to your SDK, or construct the strategy withtemperature=0.0. - Resumed checkpoints. Automatic artifact identity covers the provider and model
(plus a few provider fields, such as
OpenAIModel's API surface), not request settings such as temperature. A v0.26 checkpoint resumed withresume=ResumePolicy.REUSE_SUCCESSESorREUSE_ALLstill replays results that were produced attemperature=0.0, while items that re-run use the new default. (OpenAIModelon the new Responses default re-runs everything anyway; see above.) To keep a resumed run consistent, passtemperature=0.0. To re-run everything at the new default, use a new store or pass an explicitArtifactIdentitythat records the setting (for exampleextra={"temperature": None}) or a changedprompt_version. See Results and artifacts.
Error classification trusts types and status first
Application errors whose message mentions 429, quota, a timeout or a connection no
longer start coordinated cooldowns or retries:
- Built-in programming errors (
ValueError,TypeError,KeyError, …) arelogic_error. - An exception carrying HTTP 429 is a rate limit, with
Retry-Afterhonored. - An exception carrying any other status is never a rate limit by message.
insufficient_quotais a non-retryableinsufficient_balanceeverywhere.- Bare
"quota"no longer signals a rate limit;"quota exceeded"does. - Message matching still applies to untyped exceptions without a status.
If your code raised plain exceptions with rate-limit text to trigger a cooldown, raise
an exception with a status_code = 429 attribute instead. MockAgent's simulated rate
limit now carries code = 429.
Configuration failures re-execute on resume
token_estimator_required, token_estimation_error, token_estimate_exceeds_limit
and quota_scope_error describe the run's configuration, not the item. They no
longer replay from artifacts, even under ResumePolicy.REUSE_ALL: after you fix the
estimator or quota scope, a resumed run re-executes those items. This applies to
records written by v0.26 too, on both JSONL and SQLite. No action is needed unless
you relied on REUSE_ALL to skip these items; filter them before submission instead.
Raised SDK minimums
| Extra | Before | Now | Why |
|---|---|---|---|
[openrouter] |
openai>=1.50.0 |
openai>=1.66.2 |
openai 1.50–1.55.2 fails with current httpx |
[deepseek] |
openai>=1.66.0 |
openai>=1.66.2 |
One floor for every OpenAI-family extra |
[pydantic-ai] |
pydantic-ai>=1.0.0 |
pydantic-ai>=1.32.0 |
Earlier releases fail to import with current opentelemetry |
If you pin an older SDK, upgrade it; the new floors are the lowest releases CI tests.
Deprecations
These still work in v0.27, emit a warning, and will be removed in 1.0:
| Deprecated | Replacement |
|---|---|
execute() returning (output, tokens) |
Return (output, tokens, None), or real metadata instead of None |
effective_input_tokens() / estimated_cost() without cached_token_rate |
Pass a CachedTokenRates constant |
LLMGateway |
LLMCallPool (the same class) |
BatchProcessor |
ParallelBatchProcessor, its only implementation |
ProcessingStats |
The dict returned by ParallelBatchProcessor.get_stats() |
grounding_metadata_extractor |
Nothing: built-in Gemini models already set metadata["grounding"] |
Earlier deprecations are removed in 1.0 too, and their warnings now say so:
| Warns since | Deprecated | Replacement |
|---|---|---|
| v0.5 | ParallelBatchProcessor(max_workers=..., timeout_per_item=..., rate_limit_cooldown=...) |
ParallelBatchProcessor(config=ProcessorConfig(...)) |
| v0.20 | ProcessorConfig(timeout_per_item=...) and ProcessorConfig.timeout_per_item |
attempt_timeout |
| v0.16 | WorkItemResult.gemini_safety_ratings |
result.metadata["safety_ratings"] |
| v0.20 | BatchResult.cache_hit_rate() (called) |
BatchResult.cache_hit_rate (property) |
| v0.26 | Integer prompts in process_prompts / process_stream |
String prompts |
Each emits a DeprecationWarning, which Python hides outside __main__ by default.
Run your tests with -W error::DeprecationWarning, or check pytest's warnings
summary, to find remaining uses. The one exception is an omitted rate while cached
tokens are present, which stays a visible UserWarning, as before.
The four deprecated names stay in async_batch_llm.__all__ until 1.0, so
from async_batch_llm import * warns once for each of them, even if your code uses
none, and raises under -W error::DeprecationWarning. Replace star imports with
explicit ones (from async_batch_llm import ParallelBatchProcessor, llm) before
turning DeprecationWarning into an error. A plain import async_batch_llm raises
none of these warnings, but on Python 3.14 an installed google-genai emits its own
DeprecationWarning at import time; ignore it with
-W "ignore::DeprecationWarning:google.genai.types" after the error filter.
New in v0.27
- Token and cost budgets:
GuardrailConfig(max_total_tokens=..., max_total_cost=..., cost_function=...). See the guardrails guide. ABatchResultserialized withtermination.kind == "budget_exceeded"can't be read by v0.26. - Category constants:
ErrorCategoryandTimeoutCategoryname every built-in category, for exampleabort_on_error_categories={ErrorCategory.AUTHENTICATION}. - API stability draft: API stability shows what 1.0 is planned to keep stable, what stays provisional, and what is deprecated.
- Custom endpoints in the factory:
llm("openai-compatible:<model>", base_url=...). - Capacity-wait timing on deadlines and aborts: an item whose capacity wait ends
at a deadline or abort now reports that wait in
admission_wait_seconds, and a deadline setstiming.timeout_category == "admission_timeout". Dashboards keyed ontimeout_categorysee the new value. - Stricter guardrail timeouts: a boolean or non-numeric
GuardrailConfig(total_timeout_per_item=..., batch_timeout=...)now raisesValueError; pass a positive number orNone. - Plain Gemini metadata values:
metadata["finish_reason"]is"STOP", not"FinishReason.STOP", andmetadata["safety_ratings"]maps"HARM_CATEGORY_HATE_SPEECH"to"LOW"instead of using the enum class prefixes. If you matched the prefixed strings, match the plain names; results replayed from older artifacts still carry the prefixed form. - macOS is now tested in CI and supported.