Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions unstract/sdk1/src/unstract/sdk1/adapters/base1.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
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:
# `top_p`, `top_k`) — sending any of them yields a 400. Anthropic began this
# with Claude Opus 4.7 and continued it across the Claude 5 family (Opus 4.8,
# Sonnet 5, Fable 5, Mythos 5); adjacent older families (Opus 4.6, Sonnet 4.6,
# and earlier) still ACCEPT these params and must NOT be listed here.
# The patterns are regex-searched against the model id after lowercasing and
# normalizing `.` / `_` to `-`. Each match is anchored at the trailing edge so
# that unrelated ids (`claude-opus-4-70`, `claude-sonnet-50`,
# `claude-sonnet-5verbose`) do not match. One entry per model covers every
# encoding of that id we have observed (shown here for `claude-opus-4-7`):
# - Native Anthropic `claude-opus-4-7`, `anthropic/claude-opus-4-7`
# - Bedrock foundation model `anthropic.claude-opus-4-7-<date>-v1:0`
# - Bedrock cross-region profile `us.anthropic.claude-opus-4-7-...`,
Expand All @@ -44,6 +48,10 @@
# 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-8(?=$|[-:@/]|v\d)"),
re.compile(r"claude-sonnet-5(?=$|[-:@/]|v\d)"),
Comment thread
pk-zipstack marked this conversation as resolved.
re.compile(r"claude-fable-5(?=$|[-:@/]|v\d)"),
re.compile(r"claude-mythos-5(?=$|[-:@/]|v\d)"),
)
_DEPRECATED_SAMPLING_PARAMS: tuple[str, ...] = ("temperature", "top_p", "top_k")
# Fields whose value can carry a model id. `model` is universal; `model_id` is
Expand Down
81 changes: 79 additions & 2 deletions unstract/sdk1/tests/test_sampling_strip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Tests for Claude Opus 4.7 sampling-parameter strip.
"""Tests for the Anthropic sampling-parameter strip.

Covers Claude Opus 4.7 and the Claude 5 family (Opus 4.8, Sonnet 5, Fable 5,
Mythos 5) — every model that rejects `temperature`/`top_p`/`top_k` with a 400.

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`)
- prefix collisions (`claude-opus-4-70`, `-75`, `4-7verbose`, `claude-sonnet-50`)
- Bedrock Application Inference Profile ARN fallback via `model_id`
- mutate-and-return regression (input dict must be preserved)
- silent skip with sampling params present must emit a debug breadcrumb
Expand Down Expand Up @@ -68,6 +71,44 @@ def test_has_deprecated_sampling_params_positive(model: str) -> None:
assert _has_deprecated_sampling_params(model)


# Claude 5 family — the deprecation continued from Opus 4.7 to Opus 4.8,
# Sonnet 5, Fable 5, and Mythos 5. Sonnet 5 on Azure AI Foundry is the
# reported case that motivated adding these entries.
CLAUDE_5_POSITIVES: list[str] = [
# Opus 4.8
"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",
"vertex_ai/claude-opus-4-8@20260101",
"azure_ai/claude-opus-4-8",
"claude-opus-4-8v1",
# Sonnet 5 (reported: Azure AI Foundry)
"claude-sonnet-5",
"anthropic/claude-sonnet-5",
"anthropic.claude-sonnet-5",
"us.anthropic.claude-sonnet-5-20260101-v1:0",
"vertex_ai/claude-sonnet-5",
"azure_ai/claude-sonnet-5",
"azure_ai/my-claude-sonnet-5-deployment",
"claude.sonnet.5",
"claude_sonnet_5",
"claude-sonnet-5v1",
# Fable 5
"claude-fable-5",
"anthropic/claude-fable-5",
"azure_ai/claude-fable-5",
# Mythos 5
"claude-mythos-5",
"vertex_ai/claude-mythos-5",
]


@pytest.mark.parametrize("model", CLAUDE_5_POSITIVES)
def test_has_deprecated_sampling_params_claude5_positive(model: str) -> None:
assert _has_deprecated_sampling_params(model)


# ── detection: negatives ────────────────────────────────────────────────────

NEGATIVES: list[str | None] = [
Expand All @@ -92,6 +133,17 @@ def test_has_deprecated_sampling_params_positive(model: str) -> None:
"claude-opus-4-7verbose",
"claude-opus-4-7vnext",
"claude-opus-4-7variant",
"claude-sonnet-5verbose",
# Claude 5 prefix collisions — trailing digit is not a boundary.
"claude-opus-4-80",
"claude-sonnet-50",
"claude-fable-50",
"claude-mythos-50",
# Adjacent Claude families that still accept sampling params.
"claude-sonnet-4-6",
"claude-sonnet-4-5",
"claude-haiku-4-6",
"claude-fable-4",
# 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`.
Expand Down Expand Up @@ -277,6 +329,31 @@ def test_vertex_validate_strips_temperature_for_opus_4_7() -> None:
assert param not in result, f"vertex: {param} should be stripped"


@pytest.mark.parametrize(
"name,cls,extra",
ADAPTER_CASES,
ids=lambda v: v if isinstance(v, str) else "",
)
def test_validate_strips_temperature_for_sonnet_5(
name: str, cls: type, extra: dict[str, Any]
) -> None:
"""Reported case: Claude Sonnet 5 on Azure AI Foundry (and its peers)."""
model = {
"anthropic": "claude-sonnet-5",
"bedrock": "anthropic.claude-sonnet-5",
"azure_ai_foundry": "claude-sonnet-5",
}[name]
result = cls.validate({"model": model, "temperature": 0.5, **extra})
for param in _DEPRECATED_SAMPLING_PARAMS:
assert param not in result, f"{name}: {param} should be stripped"


def test_vertex_validate_strips_temperature_for_sonnet_5() -> None:
result = VertexAILLMParameters.validate(_vertex_metadata("claude-sonnet-5"))
for param in _DEPRECATED_SAMPLING_PARAMS:
assert param not in result, f"vertex: {param} should be stripped"


@pytest.mark.parametrize(
"name,cls,extra",
ADAPTER_CASES,
Expand Down
Loading