fix(anthropic): support the Claude 4.7+ and 5 model generations - #6868
Open
sharkfabri wants to merge 3 commits into
Open
fix(anthropic): support the Claude 4.7+ and 5 model generations#6868sharkfabri wants to merge 3 commits into
sharkfabri wants to merge 3 commits into
Conversation
`LLMStream._run` is re-entered on the same instance on every retry, and the framework only retries when the failed attempt emitted no chunk — which is exactly the case where that attempt left state behind. A stream that dies while Claude is inside a `<thinking>` block leaves `_ignoring_cot` set, and the retry's answer is then swallowed as chain of thought; one that dies mid tool call leaves a half-read call the next attempt keeps appending arguments to. The test module also moves from the `plugin` category to `unit`: it is hermetic (fake client, no API key, no network) and no CI job runs `--plugin`, so nothing in it ever runs. The other hermetic plugin tests (cerebras, aws, openai, google) are already marked `unit`.
The plugin carries assumptions that Anthropic has broken twice since Claude 4.6, and that have to be patched again on every release: - Prefilling. `_NO_PREFILL_PATTERNS` listed two model ids by hand, so Opus 4.7/4.8, Opus 5 and Sonnet 5 still sent a trailing assistant message and got the 400 from livekit#4907 back. The guard now reads the generation out of the model id and applies from 4.6 onwards. - Sampling parameters. `temperature` and `top_k` are rejected with a 400 from 4.7 onwards, so `LLM(model="claude-opus-5", temperature=0.7)` failed outright even though `temperature` is a documented constructor argument. They are now dropped from the request on those models, with a warning logged once per instance, so only the model name has to change. - Thinking. On Sonnet 5 and Opus 5 thinking is on unless it is turned off explicitly, and `max_tokens` caps thinking and the reply together: with the 1024 default a voice agent could spend the whole budget reasoning and return a truncated turn. The request now carries `thinking: {"type": "disabled"}` wherever the model accepts it; `extra_kwargs` still wins for callers that do want the model to reason. Thinking blocks were also dropped silently on the way out: `thinking_delta` and `signature_delta` fell through `_parse_event` while still being generated and billed. They are now recognised, kept out of the caller's text (a voice agent would speak them) and logged, with a one-time warning when thinking shows up although it was not requested. `models.py` gains the models released since the list was last touched (Opus 4.5/4.7/4.8, Opus 5, Sonnet 4.5, Sonnet 5, Haiku 4.5) and marks the ids that now return 404.
sharkfabri
force-pushed
the
fix/anthropic-claude-5-compat
branch
from
August 15, 2026 10:32
9fee89c to
d600ab7
Compare
The request now carries `thinking` for every model that parses to generation 4.6 or later, including the default `claude-sonnet-4-6`. `messages.create()` takes explicit keyword arguments and no `**kwargs`, so an Anthropic client older than 0.47 raises `TypeError` before the call leaves the process; `_run` turns that into an `APIConnectionError`, and the user gets an error on every request rather than an answer. `anthropic>=0.41` predates extended thinking. 0.47.0 is the first release whose `messages.create()` and `beta.messages.create()` accept `thinking`, and the same release adds the `thinking_delta`, `signature_delta` and `redacted_thinking` event shapes `_parse_event` reads, so the floor moves there.
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.
Problem
The Anthropic plugin decides what to put in a request from hardcoded model ids, and Anthropic has changed the request contract twice since Claude 4.6. Every model released after that list was written now sends a request the API rejects.
Prefilling.
_NO_PREFILL_PATTERNSlistsclaude-sonnet-4-6andclaude-opus-4-6literally, so on Opus 4.7/4.8, Opus 5 and Sonnet 5 the plugin still sends a trailing assistant message and Anthropic 400 Error on Claude 4.6 - "Prefilling assistant messages is no longer supported" #4907 ("Prefilling assistant messages is no longer supported") comes back.Sampling parameters.
temperature,top_pandtop_kare rejected with a 400 from Claude 4.7 onwards, so a documented constructor argument breaks every request:Thinking. On Sonnet 5 and Opus 5 thinking is on unless the request turns it off, and
max_tokenscaps thinking and the reply together — with the plugin's 1024 default a voice turn can spend most of its budget reasoning and come back truncated. On the way out,thinking_deltaandsignature_deltafall through_parse_event, so the reasoning is generated and billed but never surfaces anywhere.Separately,
LLMStream._runis re-entered on the same instance on every retry and nothing clears the state of the failed attempt.Fix
Two commits.
reset per-attempt state before a stream retry— the framework only retries when the attempt emitted no chunk, which is exactly when a<thinking>block or a tool call was left open._ignoring_cotleft set makes the retry's answer get swallowed as chain of thought; a half-read tool call keeps collecting arguments from the next attempt. Both are cleared at the top of_run.support the Claude 4.7+ and 5 generations— the hardcoded id list is replaced by the generation read out of the model id, and the three behaviours are gated on it:temperature/top_p/top_kare dropped, warned once per instancethinking: {"type": "disabled"}is sent;extra_kwargsstill winsVersion parsing anchors on the family name, so gateway and snapshot ids keep resolving to the underlying model:
anthropic.claude-opus-5,claude-opus-4-5@20251101,claude-opus-5[1m],gw-1-claude-opus-5. When the family cannot be found in the id — a proxy alias such asmy-claude-proxy— no version is returned and every guard keeps the behaviour it had before, rather than guessing at the model's capabilities.thinking_deltaandsignature_deltaare now recognised: the reasoning is kept out of the caller's text (a voice agent would speak it) and logged at debug level, with a one-time warning if thinking appears when it was not requested.models.pygains Opus 4.5/4.7/4.8, Opus 5, Sonnet 4.5, Sonnet 5 and Haiku 4.5, and marks the ids that now return 404.claude-fable-5andclaude-mythos-5are deliberately left out — thinking cannot be turned off on them and a single request can run for minutes, which does not fit a voice session — but the runtime guard still recognises them, sincemodelaccepts any string.Behaviour change
Requests to 4.6+ models now carry
thinking: {"type": "disabled"}where they previously carried nothing. That is a no-op on 4.6/4.7/4.8, where thinking was already off by default, and a real change on Sonnet 5 / Opus 5, where it was on. Callers who want the model to reason opt back in withchat(extra_kwargs={"thinking": {"type": "adaptive"}}); note that thinking blocks are not kept in the chat context, so a turn carrying a tool call is replayed without them, which Anthropic can reject — the plugin logs a warning when that combination is used.Tests
tests/test_plugin_anthropic.pymoves from theplugincategory tounit. It is hermetic — fake client, no API key, no network — and no CI job runs--plugin, so nothing in it was running; the other hermetic plugin tests (cerebras, aws, openai, google) are already markedunit.Coverage: version parsing including gateway/snapshot/unparseable ids, the prefill guard as a regression test for #4907, sampling parameters from both the constructor and
extra_kwargs, the thinking configuration per model family,max_tokensdefaults, the stream-side thinking blocks, and the per-attempt state reset.