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.
This commit is contained in:
Daniel Han 2026-05-27 05:43:38 +00:00
commit 22111744a4
3 changed files with 28 additions and 21 deletions

View file

@ -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 "<param> 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 "<param> 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"}

View file

@ -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",

View file

@ -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).
*/