Skip to content

Commit 3e6aefa

Browse files
cosminachoclaude
andcommitted
Feat: strip unsupported sampling params for claude-opus-4+ across all Anthropic clients
Move detection logic and constant to core (model_family.py) so a single definition covers all LangChain clients. - is_claude_opus_4_or_above() — core model_family.py - CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS — core model_family.py - Re-exported from uipath_langchain_client.utils Per-client strip strategy: - UiPathChatAnthropic → _get_request_payload override - UiPathChatAnthropicBedrock → _get_request_payload override (import from core) - UiPathChatBedrockConverse → _converse_params override (strips temperature/topP from inferenceConfig) - UiPathChatBedrock (INVOKE) → model_validator (nulls self.temperature, filters model_kwargs) - UiPathChat (normalized) → _default_params (strips + existing thinking check) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d84e5ff commit 3e6aefa

11 files changed

Lines changed: 92 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ All notable changes to `uipath_llm_client` (core package) will be documented in
44

55
## [1.9.9] - 2026-04-23
66

7-
### Changed
8-
- Bumped dependency floors to the latest installed versions: `uipath-platform>=0.1.35`.
7+
### Added
8+
- `is_claude_opus_4_or_above(model_name)` and `CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS` added to `uipath.llm_client.utils.model_family` — identifies Claude Opus 4+ reasoning models and the sampling parameters (`temperature`, `top_k`, `top_p`) that the Anthropic API rejects for them.
99

1010
## [1.9.8] - 2026-04-22
1111

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@
22

33
All notable changes to `uipath_langchain_client` will be documented in this file.
44

5-
## [1.9.10] - 2026-04-23
6-
7-
### Fixed
8-
- `UiPathChatAnthropicBedrock` now strips `temperature`, `top_k`, and `top_p` from the request payload when the model name matches `claude-opus-4` (e.g. `anthropic.claude-opus-4-7`). These sampling parameters are not supported by Claude Opus 4+ reasoning models and previously caused a `400 Bad Request` from the gateway. Requests for other models (e.g. claude-haiku) are unaffected.
9-
105
## [1.9.9] - 2026-04-23
116

12-
### Changed
13-
- Bumped dependency floors to the latest installed versions: `langchain-openai>=1.2.0`, `langchain-aws>=1.4.5`.
14-
- Minimum `uipath-llm-client` bumped to 1.9.9 to match the core dependency-floor release.
7+
### Fixed
8+
- Claude Opus 4+ reasoning models (e.g. `anthropic.claude-opus-4-7`) reject `temperature`, `top_k`, and `top_p` with `400 Bad Request`. All five UiPath Anthropic clients now strip those params automatically: `UiPathChatAnthropic` and `UiPathChatAnthropicBedrock` via `_get_request_payload`; `UiPathChatBedrockConverse` via `_converse_params` (strips `temperature`/`topP` from `inferenceConfig`); `UiPathChatBedrock` (INVOKE) via model validator; `UiPathChat` (normalized) via `_default_params`. Requests for other models are unaffected.
9+
- `UiPathChat._default_params` drops `temperature` when `thinking` is also set. Anthropic's extended thinking API requires `temperature=1` (its default) and rejects any other explicit value.
10+
- Detection logic (`is_claude_opus_4_or_above`) and the unsupported-param constant (`CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS`) are defined in core `uipath.llm_client.utils.model_family` and re-exported from `uipath_langchain_client.utils`.
1511

1612
## [1.9.8] - 2026-04-22
1713

@@ -442,3 +438,4 @@ All notable changes to `uipath_langchain_client` will be documented in this file
442438
- Tool/function calling support
443439
- Full compatibility with LangChain's `BaseChatModel` interface
444440
- httpx-based HTTP handling for consistent behavior
441+

packages/uipath_langchain_client/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
requires-python = ">=3.11"
77
dependencies = [
88
"langchain>=1.2.15,<2.0.0",
9-
"uipath-llm-client>=1.9.9,<2.0.0",
9+
"uipath-llm-client>=1.9.10,<2.0.0",
1010
]
1111

1212
[project.optional-dependencies]

packages/uipath_langchain_client/src/uipath_langchain_client/clients/anthropic/chat_models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from functools import cached_property
22
from typing import Any, Self
33

4+
from langchain_core.language_models import LanguageModelInput
45
from pydantic import Field, model_validator
56
from typing_extensions import override
67

@@ -12,6 +13,10 @@
1213
UiPathAPIConfig,
1314
VendorType,
1415
)
16+
from uipath_langchain_client.utils import (
17+
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
18+
is_claude_opus_4_or_above,
19+
)
1520

1621
try:
1722
from anthropic import (
@@ -151,6 +156,20 @@ def _async_anthropic_client(
151156
case _:
152157
raise ValueError("Anthropic models are currently not hosted on any other provider")
153158

159+
@override
160+
def _get_request_payload(
161+
self,
162+
input_: LanguageModelInput,
163+
*,
164+
stop: list[str] | None = None,
165+
**kwargs: Any,
166+
) -> dict:
167+
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
168+
if is_claude_opus_4_or_above(self.model):
169+
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
170+
payload.pop(param, None)
171+
return payload
172+
154173
@override
155174
def _create(self, payload: dict[str, Any]) -> Any:
156175
if "betas" in payload:

packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import re
21
from functools import cached_property
32
from typing import Any, Self
43

@@ -14,6 +13,10 @@
1413
UiPathAPIConfig,
1514
VendorType,
1615
)
16+
from uipath_langchain_client.utils import (
17+
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
18+
is_claude_opus_4_or_above,
19+
)
1720

1821
try:
1922
from anthropic import AnthropicBedrock, AsyncAnthropicBedrock
@@ -51,16 +54,6 @@ def _patched_format_data_content_block(block: dict) -> dict:
5154
) from e
5255

5356

54-
# Sampling parameters that Claude Opus 4+ (reasoning models) do not support.
55-
# The API returns 400 if any of these are present in the request payload.
56-
_CLAUDE_OPUS_4_UNSUPPORTED_PARAMS: frozenset[str] = frozenset({"temperature", "top_k", "top_p"})
57-
58-
59-
def _is_claude_opus_4_or_above(model_name: str) -> bool:
60-
"""Return True for Claude Opus 4+ models that reject sampling parameters."""
61-
return bool(re.search(r"claude-opus-4", model_name, re.IGNORECASE))
62-
63-
6457
class UiPathChatBedrockConverse(UiPathBaseChatModel, ChatBedrockConverse): # type: ignore[override]
6558
api_config: UiPathAPIConfig = UiPathAPIConfig(
6659
api_type=ApiType.COMPLETIONS,
@@ -89,6 +82,16 @@ def setup_uipath_client(self) -> Self:
8982
self.client = WrappedBotoClient(self.uipath_sync_client)
9083
return self
9184

85+
@override
86+
def _converse_params(self, **kwargs: Any) -> dict:
87+
params = super()._converse_params(**kwargs)
88+
if is_claude_opus_4_or_above(self.model_id):
89+
inference = params.get("inferenceConfig")
90+
if isinstance(inference, dict):
91+
inference.pop("temperature", None)
92+
inference.pop("topP", None)
93+
return params
94+
9295

9396
class UiPathChatBedrock(UiPathBaseChatModel, ChatBedrock): # type: ignore[override]
9497
api_config: UiPathAPIConfig = UiPathAPIConfig(
@@ -116,6 +119,14 @@ def setup_model_id(cls, values: Any) -> Any:
116119
@model_validator(mode="after")
117120
def setup_uipath_client(self) -> Self:
118121
self.client = WrappedBotoClient(self.uipath_sync_client)
122+
if is_claude_opus_4_or_above(self.model_id):
123+
self.temperature = None
124+
if self.model_kwargs:
125+
self.model_kwargs = {
126+
k: v
127+
for k, v in self.model_kwargs.items()
128+
if k not in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS
129+
}
119130
return self
120131

121132
@property
@@ -170,7 +181,7 @@ def _get_request_payload(
170181
**kwargs: Any,
171182
) -> dict:
172183
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
173-
if _is_claude_opus_4_or_above(self.model):
174-
for param in _CLAUDE_OPUS_4_UNSUPPORTED_PARAMS:
184+
if is_claude_opus_4_or_above(self.model):
185+
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
175186
payload.pop(param, None)
176187
return payload

packages/uipath_langchain_client/src/uipath_langchain_client/clients/normalized/chat_models.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@
6666

6767
from uipath_langchain_client.base_client import UiPathBaseChatModel
6868
from uipath_langchain_client.settings import ApiType, RoutingMode, UiPathAPIConfig
69-
from uipath_langchain_client.utils import is_anthropic_model_name
69+
from uipath_langchain_client.utils import (
70+
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
71+
is_anthropic_model_name,
72+
is_claude_opus_4_or_above,
73+
)
7074

7175
_DictOrPydanticClass = Union[dict[str, Any], type[BaseModel], type]
7276
_DictOrPydantic = Union[dict[str, Any], BaseModel]
@@ -251,6 +255,11 @@ def _default_params(self) -> dict[str, Any]:
251255
if "thinking" in params:
252256
params.pop("temperature", None)
253257

258+
# Claude Opus 4+ reasoning models reject temperature, top_k and top_p entirely.
259+
if is_claude_opus_4_or_above(self.model_name):
260+
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
261+
params.pop(param, None)
262+
254263
return {**params, **self.model_kwargs}
255264

256265
def _get_usage_metadata(self, json_data: dict[str, Any]) -> UsageMetadata:

packages/uipath_langchain_client/src/uipath_langchain_client/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
)
1616
from uipath.llm_client.utils.model_family import (
1717
ANTHROPIC_MODEL_NAME_KEYWORDS,
18+
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
1819
is_anthropic_model_name,
20+
is_claude_opus_4_or_above,
1921
)
2022
from uipath.llm_client.utils.retry import RetryConfig
2123

@@ -36,4 +38,6 @@
3638
"UiPathTooManyRequestsError",
3739
"ANTHROPIC_MODEL_NAME_KEYWORDS",
3840
"is_anthropic_model_name",
41+
"CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS",
42+
"is_claude_opus_4_or_above",
3943
]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LLM Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services."
3-
__version__ = "1.9.9"
3+
__version__ = "1.9.10"

src/uipath/llm_client/utils/model_family.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
deployments do not expose it. These helpers provide a name-based fallback.
55
"""
66

7+
import re
8+
79
ANTHROPIC_MODEL_NAME_KEYWORDS: tuple[str, ...] = (
810
"anthropic",
911
"claude",
@@ -13,8 +15,23 @@
1315
"mythos",
1416
)
1517

18+
# Sampling parameters that Claude Opus 4+ (and similar reasoning models) do not support.
19+
# The Anthropic API returns 400 Bad Request if any of these appear in the request payload.
20+
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS: frozenset[str] = frozenset(
21+
{"temperature", "top_k", "top_p"}
22+
)
23+
1624

1725
def is_anthropic_model_name(model_name: str) -> bool:
1826
"""Return True if ``model_name`` looks like an Anthropic Claude-family model."""
1927
lower = model_name.lower()
2028
return any(kw in lower for kw in ANTHROPIC_MODEL_NAME_KEYWORDS)
29+
30+
31+
def is_claude_opus_4_or_above(model_name: str) -> bool:
32+
"""Return True for Claude Opus 4+ reasoning models that reject sampling parameters.
33+
34+
These models do not accept ``temperature``, ``top_k``, or ``top_p``; sending
35+
any of them causes a ``400 Bad Request`` from the Anthropic API.
36+
"""
37+
return bool(re.search(r"claude-opus-4", model_name, re.IGNORECASE))

tests/cassettes.db

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)