Repository navigation
fix(anthropic): make the SDK compatible with the anthropic 1.x client (temperature crash + strict-endpoint nulls) - #312
Merged
Conversation
AnthropicMessagesApi.create_message always passed `temperature=` to `client.beta.messages.create`, even as the `omit` sentinel. `anthropic` client versions that dropped `temperature` from `beta.messages.create` (and accept no `**kwargs`) then raise `TypeError: ... unexpected keyword argument 'temperature'` at argument binding, making AnthropicVlmProvider unusable out of the box. Forward `temperature` only when a value was actually requested (build it into the create() kwargs conditionally). The other options remain passed as `omit` since they are still part of the client signature. Also fix a related pre-existing bug: `temperature or omit` dropped an explicit `temperature=0.0` (a valid deterministic value) because 0.0 is falsy; use `is None` instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `temperature` crash only reproduces on anthropic versions that removed the parameter (1.2.0), which a fresh `pip install` already resolves (the constraint is `anthropic>=0.86.0` with no upper bound); CI missed it only because the lock pinned 0.116.0, which still has `temperature`. - Bump the lockfile to anthropic 1.2.0 so CI runs against the version users actually get. Full unit suite + typecheck pass on 1.2.0. - Add an integration guard that binds the SDK's actual create() kwargs against the real installed client signature, so forwarding any unsupported parameter fails in CI (on 1.2.0 this fails if the temperature fix is reverted). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous fix stopped forwarding `temperature` when unset, but forwarding it
when a value *was* set still crashed on anthropic clients that removed the typed
parameter (e.g. 1.2.0) - which affects BOTH AnthropicVlmProvider and
AskUIVlmProvider, since both route Claude through this AnthropicMessagesApi.
The Messages API still accepts `temperature` in the request body, so send it via
`extra_body` (only when set) instead of as a typed keyword. Verified against the
real anthropic 1.2.0 client: `extra_body={"temperature": x}` binds cleanly while
`temperature=x` raises TypeError.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ested Live testing against the real Anthropic API showed two things: - The previous `extra_body=... or omit` passed the `omit` sentinel, crashing the default (unset) path with `TypeError: 'Omit' object is not a mapping`. - Anthropic has DEPRECATED `temperature` for its models: newer clients removed it from `beta.messages.create`, and the API rejects a non-default value with `400 "temperature is deprecated for this model."` (only the default is accepted). So there is no way to make a non-default temperature work. Stop forwarding `temperature` to the Anthropic Messages API entirely (this also fixes the AskUI provider, which routes Claude through the same API), and log a warning when a caller explicitly requests one so it is not silently ignored. `act()` now works for any temperature value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
temperature to the client when settemperature (fixes act() crash)
`from_content_block` used `model_dump()`, which serialized unset optional fields as explicit `null` (e.g. `cache_control: null`, `citations: null`) on every text/ image/tool_result block. The real Anthropic API and the AskUI proxy tolerate these nulls, but stricter Anthropic-compatible endpoints (e.g. OpenRouter's) reject them with `cache_control: expected object, received null`, failing every request. Serialize with `exclude_none=True` so optional fields are omitted rather than sent as null. This is more schema-correct and unblocks strict endpoints while remaining valid for Anthropic/AskUI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
temperature (fixes act() crash)Replaces the blanket temperature drop with the SDK's existing per-model capability check (`accepts_sampling_params`). Forward `temperature` only to models that accept sampling params, routed via `extra_body` (the typed param was removed from newer clients); drop it for the adaptive-thinking generation and warn once per model. This preserves backward compatibility for legacy budget-thinking models (Sonnet 4/4.5, Opus 4.1/4.5, Haiku 4.5, ...) - including the Android agent's deterministic `temperature=0.0` via `make_non_thinking_settings` - and avoids per-call log spam. Also reconcile the taxonomy: the 4.6 adaptive generation (Sonnet 4.6, Opus 4.6) was marked sampling-capable, but the API rejects a non-default temperature with `400 "temperature is deprecated for this model."` (confirmed against both direct Anthropic and Vertex). `accepts_sampling_params` now returns False for the whole adaptive generation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… model) A direct API probe corrected the earlier assumption: Sonnet 4.6 / Opus 4.6 DO accept `temperature` (only Sonnet 5 / Opus 4.7+ deprecated it). The Sonnet 4.6 "temperature is deprecated" 400 seen earlier was the *adaptive-thinking + temperature* incompatibility (ComputerAgent enables adaptive thinking; the probe did not), not model-level deprecation - which is why a budget-thinking model like Sonnet 4.5 replayed fine with temperature. - Revert the taxonomy change: `accepts_sampling_params` returns True again for the 4.6 generation (Sonnet 4.6 / Opus 4.6). - Forward `temperature` only when the model accepts sampling params AND adaptive thinking is not enabled (fixed budget_tokens thinking is compatible); otherwise drop it and warn once per model with the specific reason. Route it via `extra_body` when sent. This keeps temperature working for the 4.5 and 4.6 generations without thinking (and with budget thinking), drops it under adaptive thinking and for Sonnet 5+, and never crashes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR makes the messages API work with the 1.x anthropic client (routes `temperature` through `extra_body` instead of the typed kwarg that 1.x removed), and the lock already resolves 1.2.0. Bump the dependency floor to `>=1` so the declared constraint matches what the code targets, and cap at `<2` so the next major can't silently break us the way 1.x did. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Conflicts: pyproject.toml took this branch's `anthropic>=1,<2` (main still had #313's `<1` stopgap, which this PR supersedes). pdm.lock taken from main and re-locked with --update-reuse, so only anthropic moves (0.116.0 -> 1.13.0, plus its new deps httpcore2, httpx2, httpx2-jsfetch, truststore); every other pin stays on main's versions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
OpenRouter writes versions with a dot (`anthropic/claude-haiku-4.5`), but the thinking/sampling prefixes use dashes (`claude-haiku-4-5`), so dotted ids fell through to the "unknown future model" branch: Haiku 4.5 got adaptive thinking instead of a token budget, and Sonnet 4.6 / Haiku 4.5 had `temperature` dropped. `_normalize` now turns a dot between two digits into a dash. Bedrock's `-v1:0` and other dots stay untouched. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the SDK compatible with the newer
anthropicclient (1.x). Fixes to the Anthropic messages path (src/askui/models/anthropic/messages_api.py), which bothAnthropicVlmProviderandAskUIVlmProviderroute Claude through.The old problem
Our dependency was unbounded (
anthropic>=0.86.0), so a fresh install pulls the latestanthropic(1.2.0). Butanthropic1.x removed thetemperatureparameter frombeta.messages.create, andcreate_messageforwardedtemperature=to it unconditionally — even theomitsentinel when the user set none. Result: the agent crashes on the first model call, no matter the config:This is why it fires for everyone on a fresh install (e.g. it took down a downstream project that never sets
temperatureat all).1.
temperaturecrash → gate by modelRoot cause (confirmed against the real API): the adaptive-thinking Claude generation deprecated sampling params — from Sonnet 5 / Opus 4.7 onward the API rejects
temperatureoutright (400 "temperature is deprecated for this model."), and even on Sonnet 4.6 / Opus 4.6 a non-default temperature is rejected while adaptive thinking is active. Newer clients also removed the typedtemperatureparam entirely.Fix: decide per request whether to send
temperature, using the SDK's per-model capability checkaccepts_sampling_params(model_id)plus a request-level adaptive-thinking check:temperaturein the request body viaextra_body(works regardless of client version).This preserves backward compatibility (incl. the Android agent's deterministic
temperature=0.0viamake_non_thinking_settings) and avoids per-call log spam, while never crashing.2. Omit
nullfields when serializing content blocksfrom_content_blockusedmodel_dump(), serializing unset optionals as explicitnull(cache_control: null,citations: null). Real Anthropic and AskUI tolerate this, but stricter Anthropic-compatible endpoints (e.g. OpenRouter's) reject it withcache_control: expected object, received null. Fixed withexclude_none=True(verified it does not stripNones inside toolinputdicts).Dependency / CI
anthropic>=1,<2(plus thebedrock/vertexextras): the declared constraint now matches the 1.x client the code targets, and<2prevents the next major from silently breaking users the way 1.x did. Lock resolves 1.13.0 (was 1.2.0 before the 2026-10-14 update).anthropicinpdm.lockto 1.13.0 so CI runs against what users actually get on a fresh install (the lock previously pinned 0.116.0, masking the crash).create()kwargs (with/without temperature, sampling & non-sampling models) against the real installed client signature.Update 2026-10-14 — merged
main, tested with Claude Haiku 5.5origin/main(14 commits, now on top of 0.47.0). Conflicts only inpyproject.toml/pdm.lock: fix(deps): cap anthropic to <1 to prevent temperature-kwarg crash #313'santhropic<1cap is merged on main, so this PR now flips it to>=1,<2as planned — fix(deps): cap anthropic to <1 to prevent temperature-kwarg crash #313 is superseded once this lands. The lock was taken from main and re-locked with--update-reuse: onlyanthropicmoves (0.116.0 → 1.13.0, plus its new depshttpcore2,httpx2,httpx2-jsfetch,truststore); every other pin stays on main's versions.format --check,lint,typecheck:all,test:unit(830 passed) — all green.temperature=0.3/ screenshot + tool-use two-turn loop), all PASS:claude-haiku-5-5on dev and prod;claude-sonnet-5,claude-opus-5-5,claude-sonnet-4-6on dev.temperature=0.3case returned 400 on released 0.47.0 for Haiku 5.5 and Sonnet 5 (temperature is deprecated for this model); with this PR it passes.beta.messages.create): Haiku 5.5 +temperature→ dropped, one warning per provider instance; Sonnet 4.6 +temperature→ sent asextra_body.temperature; Sonnet 4.6 + adaptive thinking → dropped with warning; Android non-thinking settings → Haiku 5.5 sends no temperature, Sonnet 4.6 sends0.0. Never a typedtemperaturekwarg.AnthropicVlmProvider/AnthropicImageQAProviderwithbase_url="https://openrouter.ai/api",auth_token=<key>): plain, agent thinking (effort="medium"), Android non-thinking,temperature=0.3, screenshot + tool-use loop andget()all PASS forclaude-haiku-5-5andanthropic/claude-haiku-5.5; plain/thinking/non-thinking/temperature also pass foranthropic/claude-haiku-4.5,anthropic/claude-sonnet-4.6,anthropic/claude-sonnet-5.main/0.47.0 every request with an image (tool loop,get()) fails there with400 invalid_union— the error points at[n].cache_control: expected object, received null, which theexclude_none=Truechange here fixes.4b89cfb):thinking.pymatched dash prefixes only, soanthropic/claude-haiku-4.5got adaptive instead of budget thinking and…-sonnet-4.6/…-haiku-4.5hadtemperaturedropped._normalizenow maps a dot between two digits to a dash (Bedrock's-v1:0untouched); 9 new unit cases.Re-test 2026-10-14 after the dotted-id fix (also contains #318's Haiku 5.5 defaults)
format --check,lint,typecheck:all,test:unit(839 passed) — green.temperature, screenshot + tool loop,get(), pricing) for the defaults (nomodel_id),anthropic/claude-haiku-5.5,anthropic/claude-haiku-4.5,anthropic/claude-sonnet-4.6,anthropic/claude-sonnet-5: 35/35 PASS. Dotted Haiku 4.5 now sends budget thinking andtemperature: 0.0on non-thinking runs.claude-haiku-5-5(plain / thinking /temperature/ tool loop), plusclaude-sonnet-4-6on dev: 22/22 PASS.Testing
extra_bodyfor sampling-capable models, dropped (deprecated model or adaptive thinking) + warn-once for others, never a typed kwarg; serialization emits nonulls; real-signature guard.pdm run qa:fixclean; full unit suite green on anthropic 1.2.0.act()across providers (anthropic/askui/openrouter) × models (Sonnet 4.5/4.6/5) × temperatures — 36/36, with per-cell confirmation of whethertemperaturewas actually sent or safely dropped.🤖 Generated with Claude Code