From 22111744a4382d8f95357667d353870facbf4528 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 27 May 2026 05:43:38 +0000 Subject: [PATCH] Narrow Anthropic 4.7 sampling-removed gate to Opus only (PR #5711) The 4.7 generation only shipped Claude Opus 4.7; Sonnet stops at 4.6 and Haiku at 4.5 per https://platform.claude.com/docs/en/about-claude/models/overview. The earlier `^claude-(?:opus|sonnet|haiku)-4-7` regex on both the backend strip (_ANTHROPIC_4_7_SAMPLING_REMOVED in external_provider.py) and the frontend mirror (ANTHROPIC_4_7_SAMPLING_REMOVED_REGEX in provider-capabilities.ts) would have pre-emptively hidden temperature / top_p / top_k for any future claude-sonnet-4-7 or claude-haiku-4-7 id, even though Anthropic has explicitly not extended the sampling removal beyond Opus. Tighten both regexes to `^claude-opus-4-7(?:[-.]|$)` and update the routing-test pin so claude-sonnet-4-7 and claude-haiku-4-7 are in `should_not_match`. If those ids ever ship and adopt the same removal, widening the regex is one-line. --- .../core/inference/external_provider.py | 20 +++++++++---------- .../tests/test_sampling_params_routing.py | 10 +++++++--- .../features/chat/provider-capabilities.ts | 19 ++++++++++-------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/studio/backend/core/inference/external_provider.py b/studio/backend/core/inference/external_provider.py index ab6c5fc1d6..b9f8d459ba 100644 --- a/studio/backend/core/inference/external_provider.py +++ b/studio/backend/core/inference/external_provider.py @@ -71,14 +71,16 @@ def _normalize_stop_for_provider( return None -# Claude 4.7 (Opus/Sonnet/Haiku) removed temperature, top_p, and top_k — -# the API returns 400 " is deprecated for this model" if any of -# them is set to a non-default value. The "Sampling parameters removed" -# section of the 4.7 release notes is the authoritative reference: +# Claude 4.7 Opus removed temperature, top_p, and top_k — the API +# returns 400 " is deprecated for this model" if any of them is +# set to a non-default value. The "Sampling parameters removed" section +# of the 4.7 release notes is the authoritative reference: # https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7 -# 3.x and 4.5/4.6 still accept all three; match the 4-7 line strictly so -# the knobs keep working on earlier families. The trailing -4-7[-.]/EOL -# anchor keeps future versions (e.g. claude-opus-5) unaffected. +# Only Opus shipped in the 4.7 generation (Sonnet stops at 4.6, Haiku at +# 4.5 per https://platform.claude.com/docs/en/about-claude/models/overview), +# so the regex is anchored to opus-4-7 only. 3.x and 4.5/4.6 still accept +# all three knobs; the trailing -4-7[-.]/EOL anchor keeps future versions +# (e.g. claude-opus-5) unaffected. def _is_openai_family_cloud(base_url: Optional[str]) -> bool: """True iff ``base_url`` points at OpenAI cloud or Azure OpenAI Foundry. @@ -107,9 +109,7 @@ def _is_openai_family_cloud(base_url: Optional[str]) -> bool: return host == "api.openai.com" or host.endswith(".openai.azure.com") -_ANTHROPIC_4_7_SAMPLING_REMOVED = re.compile( - r"^claude-(?:opus|sonnet|haiku)-4-7(?:[-.]|$)" -) +_ANTHROPIC_4_7_SAMPLING_REMOVED = re.compile(r"^claude-opus-4-7(?:[-.]|$)") _OPENAI_REASONING_SUMMARY_UNSUPPORTED = re.compile(r"^o3(?:[-.]|$)") _OPENAI_REASONING_STATUSES = {"in_progress", "completed", "incomplete"} diff --git a/studio/backend/tests/test_sampling_params_routing.py b/studio/backend/tests/test_sampling_params_routing.py index 19fd9daf34..3671034f23 100644 --- a/studio/backend/tests/test_sampling_params_routing.py +++ b/studio/backend/tests/test_sampling_params_routing.py @@ -1105,14 +1105,18 @@ def test_anthropic_4_7_sampling_removed_regex_matches_expected_ids(): _ANTHROPIC_4_7_SAMPLING_REMOVED as RX, ) + # Only Opus shipped in the 4.7 generation per + # platform.claude.com/docs/en/about-claude/models/overview; Sonnet + # stops at 4.6 and Haiku at 4.5. Pin both directions explicitly so + # the regex never widens by accident. should_match = [ "claude-opus-4-7", - "claude-sonnet-4-7", - "claude-haiku-4-7", "claude-opus-4-7-20260418", - "claude-sonnet-4-7.1", + "claude-opus-4-7.1", ] should_not_match = [ + "claude-sonnet-4-7", + "claude-haiku-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", diff --git a/studio/frontend/src/features/chat/provider-capabilities.ts b/studio/frontend/src/features/chat/provider-capabilities.ts index d64415de1f..b4e60552ce 100644 --- a/studio/frontend/src/features/chat/provider-capabilities.ts +++ b/studio/frontend/src/features/chat/provider-capabilities.ts @@ -686,12 +686,14 @@ function isOpenAIReasoningModelId(modelId: string | null | undefined): boolean { } // Mirror of backend _ANTHROPIC_4_7_SAMPLING_REMOVED in -// studio/backend/core/inference/external_provider.py:110. Claude 4.7 -// (Opus/Sonnet/Haiku) removed temperature, top_p, and top_k entirely; -// surfacing the sliders would let the user move a control that the -// backend silently strips. The trailing -4-7[-.]/EOL anchor keeps future -// families (claude-opus-5 etc.) unaffected. -const ANTHROPIC_4_7_SAMPLING_REMOVED_REGEX = /^claude-(?:opus|sonnet|haiku)-4-7(?:[-.]|$)/i; +// studio/backend/core/inference/external_provider.py:110. Claude Opus +// 4.7 removed temperature, top_p, and top_k entirely; surfacing the +// sliders would let the user move a control that the backend silently +// strips. Only Opus shipped in the 4.7 generation (Sonnet stops at 4.6, +// Haiku at 4.5 per platform.claude.com/docs/en/about-claude/models/ +// overview), so the regex is opus-only. The trailing -4-7[-.]/EOL +// anchor keeps future families (claude-opus-5 etc.) unaffected. +const ANTHROPIC_4_7_SAMPLING_REMOVED_REGEX = /^claude-opus-4-7(?:[-.]|$)/i; function isClaude47SamplingRemoved(modelId: string | null | undefined): boolean { const normalized = modelId?.trim().toLowerCase() ?? ""; @@ -849,8 +851,9 @@ const DEFAULT_EXTERNAL_CAPABILITIES = OPENAI_COMPAT_BASE; * gpt-3.5-turbo): full sampling surface (OPENAI_CHAT_CAPABILITIES). * - openai + reasoning model (gpt-5.x, o1, o3, o4): restrictive * (OPENAI_REASONING_CAPABILITIES). - * - anthropic + claude-*-4-7: temperature/top_p/top_k stripped to - * match the backend 400-avoidance regex. + * - anthropic + claude-opus-4-7: temperature/top_p/top_k stripped to + * match the backend 400-avoidance regex (Sonnet/Haiku 4.7 do not + * ship; only Opus does in the 4.7 generation). * - deepseek + reasoning model (deepseek-reasoner / r1): hides * temperature/top_p (silently ignored upstream). */