fix(anthropic): make the SDK compatible with the anthropic 1.x client (temperature crash + strict-endpoint nulls) - #312
Open
philipph-askui wants to merge 8 commits into
Open
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>
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.2.0.anthropicinpdm.lockto 1.2.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.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