Skip to content

Commit 15dc7c9

Browse files
cosminachoclaude
andcommitted
Feat: use modelDetails.shouldSkipTemperature from discovery to gate sampling params
Add _should_skip_sampling_params cached_property to UiPathBaseLLMClient. It reads shouldSkipTemperature from get_model_info() (authoritative) and falls back to the is_claude_opus_4_or_above() name heuristic when the model is not found in discovery. All five Anthropic clients now use self._should_skip_sampling_params instead of calling is_claude_opus_4_or_above directly, so the logic automatically adapts when the backend sets shouldSkipTemperature on new models without requiring a code change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f756473 commit 15dc7c9

5 files changed

Lines changed: 23 additions & 8 deletions

File tree

packages/uipath_langchain_client/src/uipath_langchain_client/base_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
get_captured_response_headers,
5050
set_captured_response_headers,
5151
)
52+
from uipath.llm_client.utils.model_family import is_claude_opus_4_or_above
5253
from uipath_langchain_client.settings import (
5354
UiPathAPIConfig,
5455
UiPathBaseSettings,
@@ -190,6 +191,19 @@ def uipath_async_client(self) -> UiPathHttpxAsyncClient:
190191
logger=self.logger,
191192
)
192193

194+
@cached_property
195+
def _should_skip_sampling_params(self) -> bool:
196+
"""True if the model's discovery metadata marks temperature/top_k/top_p as unsupported.
197+
198+
Reads ``modelDetails.shouldSkipTemperature`` from the model discovery API.
199+
Falls back to a name-based heuristic when the model is not found in discovery.
200+
"""
201+
try:
202+
info = self.client_settings.get_model_info(model_name=self.model_name)
203+
return bool(info.get("modelDetails", {}).get("shouldSkipTemperature", False))
204+
except Exception:
205+
return is_claude_opus_4_or_above(self.model_name)
206+
193207
def uipath_request(
194208
self,
195209
method: Literal["POST", "GET"] = "POST",

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
)
1616
from uipath_langchain_client.utils import (
1717
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
18-
is_claude_opus_4_or_above,
1918
)
2019

2120
try:
@@ -165,7 +164,7 @@ def _get_request_payload(
165164
**kwargs: Any,
166165
) -> dict:
167166
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
168-
if is_claude_opus_4_or_above(self.model):
167+
if self._should_skip_sampling_params:
169168
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
170169
payload.pop(param, None)
171170
return payload

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
)
1616
from uipath_langchain_client.utils import (
1717
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
18-
is_claude_opus_4_or_above,
1918
)
2019

2120
try:
@@ -85,7 +84,7 @@ def setup_uipath_client(self) -> Self:
8584
@override
8685
def _converse_params(self, **kwargs: Any) -> dict:
8786
params = super()._converse_params(**kwargs)
88-
if is_claude_opus_4_or_above(self.model_id):
87+
if self._should_skip_sampling_params:
8988
inference = params.get("inferenceConfig")
9089
if isinstance(inference, dict):
9190
inference.pop("temperature", None)
@@ -119,7 +118,7 @@ def setup_model_id(cls, values: Any) -> Any:
119118
@model_validator(mode="after")
120119
def setup_uipath_client(self) -> Self:
121120
self.client = WrappedBotoClient(self.uipath_sync_client)
122-
if is_claude_opus_4_or_above(self.model_id):
121+
if self._should_skip_sampling_params:
123122
self.temperature = None
124123
if self.model_kwargs:
125124
self.model_kwargs = {
@@ -181,7 +180,7 @@ def _get_request_payload(
181180
**kwargs: Any,
182181
) -> dict:
183182
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
184-
if is_claude_opus_4_or_above(self.model):
183+
if self._should_skip_sampling_params:
185184
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
186185
payload.pop(param, None)
187186
return payload

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
from uipath_langchain_client.utils import (
7070
CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS,
7171
is_anthropic_model_name,
72-
is_claude_opus_4_or_above,
7372
)
7473

7574
_DictOrPydanticClass = Union[dict[str, Any], type[BaseModel], type]
@@ -256,7 +255,7 @@ def _default_params(self) -> dict[str, Any]:
256255
params.pop("temperature", None)
257256

258257
# Claude Opus 4+ reasoning models reject temperature, top_k and top_p entirely.
259-
if is_claude_opus_4_or_above(self.model_name):
258+
if self._should_skip_sampling_params:
260259
for param in CLAUDE_OPUS_4_UNSUPPORTED_SAMPLING_PARAMS:
261260
params.pop(param, None)
262261

tests/langchain/clients/bedrock/test_unit.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ def test_is_claude_opus_4_or_above(self) -> None:
7373
def test_unsupported_params_stripped_from_payload(
7474
self, opus4_client: UiPathChatAnthropicBedrock
7575
) -> None:
76+
# Force _should_skip_sampling_params without a real discovery call.
77+
opus4_client.__dict__["_should_skip_sampling_params"] = True
7678
with patch.object(opus4_client, "_client") as mock_client:
7779
mock_client.messages.create.return_value = MagicMock(
7880
content=[MagicMock(type="text", text="hi")],
@@ -94,6 +96,8 @@ def test_sampling_params_kept_for_other_models(
9496
settings=client_settings,
9597
temperature=0.5,
9698
)
99+
# Discovery returns False for haiku; ensure params are not stripped.
100+
haiku.__dict__["_should_skip_sampling_params"] = False
97101
with patch.object(haiku, "_client") as mock_client:
98102
mock_client.messages.create.return_value = MagicMock(
99103
content=[MagicMock(type="text", text="hi")],

0 commit comments

Comments
 (0)