Skip to content

Commit 427d8ed

Browse files
cosminachoclaude
andcommitted
Fix: strip temperature/top_k/top_p for claude-opus-4+ in UiPathChatAnthropicBedrock
Claude Opus 4+ (e.g. anthropic.claude-opus-4-7) are reasoning models that reject temperature, top_k, and top_p with a 400 Bad Request. Override _get_request_payload in UiPathChatAnthropicBedrock to pop those params when the model name matches the claude-opus-4 pattern. Params are untouched for all other models. Also add anthropic.claude-opus-4-7 to the bedrock integration test matrix (UiPathChatAnthropicBedrock, non-thinking configs only, as thinking cassettes are not yet recorded for this model). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 581bcbc commit 427d8ed

5 files changed

Lines changed: 103 additions & 1 deletion

File tree

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
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+
510
## [1.9.9] - 2026-04-23
611

712
### Fixed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LangChain Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
3-
__version__ = "1.9.9"
3+
__version__ = "1.9.10"

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import re
12
from functools import cached_property
23
from typing import Any, Self
34

5+
from langchain_core.language_models import LanguageModelInput
46
from pydantic import Field, model_validator
7+
from typing_extensions import override
58

69
from uipath_langchain_client.base_client import UiPathBaseChatModel
710
from uipath_langchain_client.settings import (
@@ -48,6 +51,16 @@ def _patched_format_data_content_block(block: dict) -> dict:
4851
) from e
4952

5053

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+
5164
class UiPathChatBedrockConverse(UiPathBaseChatModel, ChatBedrockConverse): # type: ignore[override]
5265
api_config: UiPathAPIConfig = UiPathAPIConfig(
5366
api_type=ApiType.COMPLETIONS,
@@ -147,3 +160,17 @@ def _async_client(self) -> AsyncAnthropicBedrock:
147160
max_retries=0, # handled by the UiPathBaseChatModel
148161
http_client=self.uipath_async_client,
149162
)
163+
164+
@override
165+
def _get_request_payload(
166+
self,
167+
input_: LanguageModelInput,
168+
*,
169+
stop: list[str] | None = None,
170+
**kwargs: Any,
171+
) -> dict:
172+
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:
175+
payload.pop(param, None)
176+
return payload

tests/langchain/clients/bedrock/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,18 @@
4646
},
4747
]
4848

49+
CLAUDE_ANTHROPIC_BEDROCK_CONFIGS_NO_THINKING = [
50+
c
51+
for c in CLAUDE_BEDROCK_CONFIGS
52+
if c["model_class"] is UiPathChatAnthropicBedrock
53+
and "thinking" not in c.get("model_kwargs", {})
54+
]
55+
4956
COMPLETIONS_MODELS_WITH_CONFIGS = {
5057
"anthropic.claude-haiku-4-5-20251001-v1:0": CLAUDE_BEDROCK_CONFIGS,
58+
# claude-opus-4-7 via Bedrock: tested with UiPathChatAnthropicBedrock only (no thinking;
59+
# thinking cassettes not yet recorded for this model).
60+
"anthropic.claude-opus-4-7": CLAUDE_ANTHROPIC_BEDROCK_CONFIGS_NO_THINKING,
5161
}
5262

5363

tests/langchain/clients/bedrock/test_unit.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
"""LangChain unit tests for Bedrock provider clients."""
22

33
from typing import Any
4+
from unittest.mock import MagicMock, patch
45

56
import pytest
67
from langchain_core.embeddings import Embeddings
78
from langchain_core.language_models.chat_models import BaseChatModel
9+
from langchain_core.messages import HumanMessage
810
from langchain_tests.unit_tests import ChatModelUnitTests, EmbeddingsUnitTests
911
from uipath_langchain_client.clients.bedrock.chat_models import (
12+
_CLAUDE_OPUS_4_UNSUPPORTED_PARAMS,
1013
UiPathChatAnthropicBedrock,
1114
UiPathChatBedrock,
1215
UiPathChatBedrockConverse,
16+
_is_claude_opus_4_or_above,
1317
)
1418
from uipath_langchain_client.clients.bedrock.embeddings import UiPathBedrockEmbeddings
1519

@@ -40,6 +44,62 @@ def chat_model_params(self) -> dict[str, Any]:
4044
def test_serdes(self, *args: Any, **kwargs: Any) -> None: ...
4145

4246

47+
class TestClaudeOpus4SamplingParamFiltering:
48+
"""UiPathChatAnthropicBedrock must strip temperature/top_k/top_p for claude-opus-4+ models."""
49+
50+
@pytest.fixture()
51+
def opus4_client(self, client_settings: UiPathBaseSettings) -> UiPathChatAnthropicBedrock:
52+
return UiPathChatAnthropicBedrock(
53+
model="anthropic.claude-opus-4-7",
54+
settings=client_settings,
55+
temperature=0.7,
56+
top_k=40,
57+
top_p=0.9,
58+
)
59+
60+
def test_is_claude_opus_4_or_above(self) -> None:
61+
assert _is_claude_opus_4_or_above("anthropic.claude-opus-4-7")
62+
assert _is_claude_opus_4_or_above("claude-opus-4-5-20250514")
63+
assert not _is_claude_opus_4_or_above("anthropic.claude-3-5-sonnet-20240620-v1:0")
64+
assert not _is_claude_opus_4_or_above("anthropic.claude-haiku-4-5-20251001-v1:0")
65+
66+
def test_unsupported_params_stripped_from_payload(
67+
self, opus4_client: UiPathChatAnthropicBedrock
68+
) -> None:
69+
with patch.object(opus4_client, "_client") as mock_client:
70+
mock_client.messages.create.return_value = MagicMock(
71+
content=[MagicMock(type="text", text="hi")],
72+
stop_reason="end_turn",
73+
usage=MagicMock(input_tokens=10, output_tokens=5),
74+
model="anthropic.claude-opus-4-7",
75+
id="msg_123",
76+
)
77+
opus4_client.invoke([HumanMessage(content="hi")])
78+
call_kwargs = mock_client.messages.create.call_args.kwargs
79+
for param in _CLAUDE_OPUS_4_UNSUPPORTED_PARAMS:
80+
assert param not in call_kwargs, f"{param} must be stripped for claude-opus-4"
81+
82+
def test_sampling_params_kept_for_other_models(
83+
self, client_settings: UiPathBaseSettings
84+
) -> None:
85+
haiku = UiPathChatAnthropicBedrock(
86+
model="anthropic.claude-haiku-4-5-20251001-v1:0",
87+
settings=client_settings,
88+
temperature=0.5,
89+
)
90+
with patch.object(haiku, "_client") as mock_client:
91+
mock_client.messages.create.return_value = MagicMock(
92+
content=[MagicMock(type="text", text="hi")],
93+
stop_reason="end_turn",
94+
usage=MagicMock(input_tokens=10, output_tokens=5),
95+
model="anthropic.claude-haiku-4-5-20251001-v1:0",
96+
id="msg_123",
97+
)
98+
haiku.invoke([HumanMessage(content="hi")])
99+
call_kwargs = mock_client.messages.create.call_args.kwargs
100+
assert call_kwargs.get("temperature") == 0.5, "temperature must be kept for haiku"
101+
102+
43103
class TestBedrockEmbeddings(EmbeddingsUnitTests):
44104
@pytest.fixture(autouse=True, params=BEDROCK_EMBEDDINGS_CLASSES)
45105
def setup_models(self, request: pytest.FixtureRequest, client_settings: UiPathBaseSettings):

0 commit comments

Comments
 (0)