diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py index ebd93e3dbd..201f4acff9 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py @@ -17,33 +17,26 @@ logger = logging.getLogger(__name__) -# Anthropic models that have deprecated sampling parameters (`temperature`, -# `top_p`, `top_k`). The patterns are regex-searched against the model id -# after lowercasing and normalizing `.` / `_` to `-`. The match is anchored at -# the trailing edge so that unrelated future ids (`claude-opus-4-70`, -# `claude-opus-4-75`, `claude-opus-4-7verbose`) do not match. A single entry -# covers every encoding of the id we have observed: -# - Native Anthropic `claude-opus-4-7`, `anthropic/claude-opus-4-7` -# - Bedrock foundation model `anthropic.claude-opus-4-7--v1:0` -# - Bedrock cross-region profile `us.anthropic.claude-opus-4-7-...`, -# `eu.`, `apac.`, `global.` variants -# - Bedrock foundation-model ARN `arn:aws:bedrock:::foundation-model/ -# anthropic.claude-opus-4-7-...` -# - Bedrock inference-profile ARN `arn:aws:bedrock::: -# inference-profile/us.anthropic.claude-opus-4-7-...` -# - Vertex AI `vertex_ai/claude-opus-4-7@` -# - Azure AI Foundry deployments whose name embeds `claude-opus-4-7` -# Leading text (route prefixes like `converse/`, `invoke/`, `bedrock/`) passes -# through because the regex is anchored only at the trailing edge. -# Add new entries here when Anthropic deprecates sampling on more models. -# Trailing anchor allows: end-of-string, or one of `-`/`:`/`@`/`/` (the -# delimiters used in date suffixes, ARN paths, Vertex `@`, and the -# `v1:0` tag), or `v` followed by a digit (the version-tag start). A bare -# `v` is intentionally rejected so alpha continuations like `4-7verbose` do -# not silently match. +# Anthropic deprecated the sampling parameters (`temperature`, `top_p`, +# `top_k`) starting with Claude Opus 4.7; sending any of them yields a 400 from +# Anthropic and the providers that proxy it (Bedrock, Azure AI Foundry, Vertex +# AI). This covers every Opus release from 4.7 onwards: Opus 4.7, 4.8, 4.9 and +# every Opus 5+ release. +# +# Patterns are regex-searched against the model id after lowercasing and +# normalizing `.`/`_` to `-`, so they match every encoding we have seen: +# native (`claude-opus-4-8`), Bedrock foundation models / cross-region +# profiles / ARNs (`us.anthropic.claude-opus-4-8--v1:0`), Vertex +# (`vertex_ai/claude-opus-4-8@`), and Azure deployments embedding the id. +# Route prefixes (`converse/`, `invoke/`, `bedrock/`) pass through. +# +# The trailing lookahead (`$`, a `-`/`:`/`@`/`/` delimiter, or `v`) +# anchors the match so `claude-opus-4-70` and `claude-opus-4-7verbose` do NOT +# match while date/ARN/version suffixes still do. # See https://docs.claude.com/en/about-claude/models/whats-new-claude-4-7 _SAMPLING_DEPRECATED_MODEL_PATTERNS: tuple[re.Pattern[str], ...] = ( - re.compile(r"claude-opus-4-7(?=$|[-:@/]|v\d)"), + re.compile(r"claude-opus-4-[789](?=$|[-:@/]|v\d)"), # Opus 4.7, 4.8, 4.9 + re.compile(r"claude-opus-[5-9](?=$|[-:@/]|v\d)"), # Opus 5 and later ) _DEPRECATED_SAMPLING_PARAMS: tuple[str, ...] = ("temperature", "top_p", "top_k") # Fields whose value can carry a model id. `model` is universal; `model_id` is @@ -63,7 +56,7 @@ def _looks_like_opaque_aip_arn(value: str | None) -> bool: Bedrock AIP ARNs do not carry the underlying foundation-model id in the string, so the sampling-strip detector cannot decide whether the call is - bound for Claude Opus 4.7. + bound for Claude Opus 4.7 or later. """ return bool(value) and _OPAQUE_AIP_ARN_MARKER in value @@ -89,7 +82,8 @@ def _has_deprecated_sampling_params(model: str | None) -> bool: from the string. Pass the AIP ARN in `model_id` and keep the standard model id in `model`, or the strip won't fire. - Azure AI Foundry deployment names that omit the model id; rename the - deployment to include `claude-opus-4-7` so detection works. + deployment to include the relevant model id (e.g. `claude-opus-4-8`) + so detection works. """ if not model: return False diff --git a/unstract/sdk1/tests/test_sampling_strip.py b/unstract/sdk1/tests/test_sampling_strip.py index 3e3ce8b405..7ff976f66f 100644 --- a/unstract/sdk1/tests/test_sampling_strip.py +++ b/unstract/sdk1/tests/test_sampling_strip.py @@ -1,5 +1,6 @@ -"""Tests for Claude Opus 4.7 sampling-parameter strip. +"""Tests for the Claude Opus 4.7+ sampling-parameter strip. +Covers every Opus release from 4.7 onwards (4.7, 4.8, 4.9, Opus 5+). Pins the detection regex and the four-adapter wiring against the failure modes that surfaced in PR #1934 review: - prefix collisions (`claude-opus-4-70`, `-75`, `4-7verbose`) @@ -60,6 +61,24 @@ # Version tag accepted only as `v\d` after the trailing edge "claude-opus-4-7v1", "claude-opus-4-7v9", + # Opus 4.8 / 4.9 — same deprecation, representative encodings + "claude-opus-4-8", + "anthropic/claude-opus-4-8", + "anthropic.claude-opus-4-8-20260101-v1:0", + "us.anthropic.claude-opus-4-8-20260101-v1:0", + "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-opus-4-8-20260101-v1:0", + "vertex_ai/claude-opus-4-8@20260101", + "azure_ai/my-claude-opus-4-8-deployment", + "claude.opus.4.8", + "claude-opus-4-9", + # Opus 5 and later + "claude-opus-5", + "claude-opus-5-0", + "anthropic/claude-opus-5-0", + "anthropic.claude-opus-5-0-20270101-v1:0", + "us.anthropic.claude-opus-5-0-20270101-v1:0", + "vertex_ai/claude-opus-5-0@20270101", + "claude-opus-6-0", ] @@ -92,6 +111,13 @@ def test_has_deprecated_sampling_params_positive(model: str) -> None: "claude-opus-4-7verbose", "claude-opus-4-7vnext", "claude-opus-4-7variant", + # Opus 5+ boundary: the major must end at a delimiter, not run into more + # digits or letters. + "claude-opus-50", + "claude-opus-5verbose", + # Opus 4.1–4.6 still accept sampling params; only 4.7+ is deprecated. + "claude-opus-4-1", + "anthropic.claude-opus-4-1-20250805-v1:0", # Opaque Bedrock Application Inference Profile ARN — model id is not # recoverable from the string. Strip-detection is expected to skip; # callers must keep the standard id in `model` or `model_id`.