Skip to content

Commit b0db28e

Browse files
cosminachoclaude
andcommitted
Fix: BYOM Claude detection in LiteLLM client via shared name helper
BYOM discovery does not expose modelFamily, so the LiteLLM client's is_claude checks silently fell through for custom-named Claude deployments. Extract the keyword heuristic into a shared is_anthropic_model_name() helper in core and use it as a fallback whenever modelFamily is None. Refactor the two langchain call sites to consume the same helper. Core 1.9.0 -> 1.9.1. Langchain now requires core >= 1.9.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 969fc1f commit b0db28e

9 files changed

Lines changed: 53 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to `uipath_llm_client` (core package) will be documented in this file.
44

5+
## [1.9.1] - 2026-04-17
6+
7+
### Added
8+
- `is_anthropic_model_name()` helper and `ANTHROPIC_MODEL_NAME_KEYWORDS` tuple in `settings.constants` — name-based Claude detection for BYOM deployments where discovery does not expose `modelFamily`
9+
10+
### Fixed
11+
- `UiPathLiteLLM` now detects Claude-family models by name when `modelFamily` is unavailable (BYOM), correctly routing Bedrock/Vertex provider selection and default flavors
12+
513
## [1.9.0] - 2026-04-17
614

715
### Added

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ All notable changes to `uipath_langchain_client` will be documented in this file
55
## [1.9.1] - 2026-04-17
66

77
### Fixed
8-
- Detect Anthropic-family models by additional name keywords (`anthropic`, `opus`, `sonnet`, `haiku`, `mythos`) alongside `claude` — applies to Bedrock INVOKE factory routing and the normalized client's empty tool-call content workaround
8+
- Detect Anthropic-family models by additional name keywords (`anthropic`, `opus`, `sonnet`, `haiku`, `mythos`) alongside `claude` — applies to Bedrock INVOKE factory routing and the normalized client's empty tool-call content workaround. Uses the shared `is_anthropic_model_name()` helper from core 1.9.1.
9+
10+
### Changed
11+
- Minimum `uipath-llm-client` bumped to 1.9.1 for the shared `is_anthropic_model_name()` helper
912

1013
## [1.9.0] - 2026-04-17
1114

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",
9-
"uipath-llm-client>=1.9.0",
9+
"uipath-llm-client>=1.9.1",
1010
]
1111

1212
[project.optional-dependencies]

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@
6565
from pydantic import AliasChoices, BaseModel, Field
6666

6767
from uipath_langchain_client.base_client import UiPathBaseChatModel
68-
from uipath_langchain_client.settings import ApiType, RoutingMode, UiPathAPIConfig
68+
from uipath_langchain_client.settings import (
69+
ApiType,
70+
RoutingMode,
71+
UiPathAPIConfig,
72+
is_anthropic_model_name,
73+
)
6974

7075
_DictOrPydanticClass = Union[dict[str, Any], type[BaseModel], type]
7176
_DictOrPydantic = Union[dict[str, Any], BaseModel]
@@ -412,10 +417,7 @@ def _preprocess_request(
412417
converted_message["content"] = ""
413418
if (
414419
self.model_name
415-
and any(
416-
kw in self.model_name.lower()
417-
for kw in ("anthropic", "claude", "opus", "sonnet", "haiku", "mythos")
418-
)
420+
and is_anthropic_model_name(self.model_name)
419421
and not converted_message["content"]
420422
):
421423
converted_message["content"] = "tool_call"

packages/uipath_langchain_client/src/uipath_langchain_client/factory.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
UiPathBaseSettings,
3636
VendorType,
3737
get_default_client_settings,
38+
is_anthropic_model_name,
3839
)
3940

4041

@@ -174,13 +175,7 @@ def get_chat_model(
174175
case VendorType.AWSBEDROCK:
175176
if (
176177
model_family == ModelFamily.ANTHROPIC_CLAUDE and api_flavor != ApiFlavor.CONVERSE
177-
) or (
178-
api_flavor == ApiFlavor.INVOKE
179-
and any(
180-
kw in model_name.lower()
181-
for kw in ("anthropic", "claude", "opus", "sonnet", "haiku", "mythos")
182-
)
183-
):
178+
) or (api_flavor == ApiFlavor.INVOKE and is_anthropic_model_name(model_name)):
184179
from uipath_langchain_client.clients.bedrock.chat_models import (
185180
UiPathChatAnthropicBedrock,
186181
)

packages/uipath_langchain_client/src/uipath_langchain_client/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ModelFamily,
3232
RoutingMode,
3333
VendorType,
34+
is_anthropic_model_name,
3435
)
3536

3637
__all__ = [
@@ -47,4 +48,5 @@
4748
"VendorType",
4849
"API_FLAVOR_TO_VENDOR_TYPE",
4950
"BYOM_TO_ROUTING_FLAVOR",
51+
"is_anthropic_model_name",
5052
]
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.0"
3+
__version__ = "1.9.1"

src/uipath/llm_client/clients/litellm/client.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ModelFamily,
3535
RoutingMode,
3636
VendorType,
37+
is_anthropic_model_name,
3738
)
3839
from uipath.llm_client.utils.retry import RetryConfig
3940

@@ -191,7 +192,6 @@ def _discover_and_build_api_config(
191192
model_info = self._client_settings.get_model_info(self._model_name, vendor_type=vendor_type)
192193

193194
model_family = model_info.get("modelFamily", None)
194-
195195
discovered_vendor = model_info.get("vendor", None)
196196
discovered_flavor = model_info.get("apiFlavor", None)
197197

@@ -217,20 +217,18 @@ def _discover_and_build_api_config(
217217
if resolved_flavor is None and resolved_vendor in ("openai", "azure"):
218218
resolved_flavor = ApiFlavor.CHAT_COMPLETIONS
219219

220+
# Claude detection: modelFamily from discovery, or name heuristic for BYOM
221+
# (BYOM discovery does not expose modelFamily).
222+
is_claude = model_family == ModelFamily.ANTHROPIC_CLAUDE or (
223+
model_family is None and is_anthropic_model_name(self._model_name)
224+
)
225+
220226
# Claude on Bedrock defaults to invoke
221-
if (
222-
resolved_flavor is None
223-
and resolved_vendor == "awsbedrock"
224-
and model_family == ModelFamily.ANTHROPIC_CLAUDE
225-
):
227+
if resolved_flavor is None and resolved_vendor == "awsbedrock" and is_claude:
226228
resolved_flavor = ApiFlavor.INVOKE
227229

228230
# Claude on Vertex defaults to anthropic-claude
229-
if (
230-
resolved_flavor is None
231-
and resolved_vendor == "vertexai"
232-
and model_family == ModelFamily.ANTHROPIC_CLAUDE
233-
):
231+
if resolved_flavor is None and resolved_vendor == "vertexai" and is_claude:
234232
resolved_flavor = ApiFlavor.ANTHROPIC_CLAUDE
235233

236234
api_config = UiPathAPIConfig(
@@ -247,7 +245,9 @@ def _resolve_llm_provider(self) -> str:
247245
The model_family disambiguates cases where the same vendor hosts
248246
models from different providers (e.g. Claude on Vertex AI or Bedrock).
249247
"""
250-
is_claude = self._model_family == ModelFamily.ANTHROPIC_CLAUDE
248+
is_claude = self._model_family == ModelFamily.ANTHROPIC_CLAUDE or (
249+
self._model_family is None and is_anthropic_model_name(self._model_name)
250+
)
251251
vendor = str(self._api_config.vendor_type or "openai")
252252

253253
# Claude on Vertex AI → vertex_ai (uses VertexAIAnthropicConfig)

src/uipath/llm_client/settings/constants.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,19 @@ class ByomApiFlavor(StrEnum):
7070
ByomApiFlavor.AWS_BEDROCK_INVOKE: ApiFlavor.INVOKE,
7171
ByomApiFlavor.AWS_BEDROCK_CONVERSE: ApiFlavor.CONVERSE,
7272
}
73+
74+
75+
ANTHROPIC_MODEL_NAME_KEYWORDS: tuple[str, ...] = (
76+
"anthropic",
77+
"claude",
78+
"opus",
79+
"sonnet",
80+
"haiku",
81+
"mythos",
82+
)
83+
84+
85+
def is_anthropic_model_name(model_name: str) -> bool:
86+
"""Heuristic fallback for when discovery's ``modelFamily`` is unavailable (e.g. BYOM)."""
87+
lower = model_name.lower()
88+
return any(kw in lower for kw in ANTHROPIC_MODEL_NAME_KEYWORDS)

0 commit comments

Comments
 (0)