From b1387869b1e18b01c9075ecc25c7e56c6f0680ff Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Fri, 3 Jul 2026 11:41:15 +0300 Subject: [PATCH 1/2] fix: lazy bedrock import and BYO Anthropic token counting --- .github/workflows/ci.yml | 28 ++++++++++ packages/uipath_langchain_client/CHANGELOG.md | 7 +++ .../uipath_langchain_client/__version__.py | 2 +- .../clients/bedrock/chat_models.py | 15 +++++ .../src/uipath_langchain_client/factory.py | 7 ++- .../features/test_factory_function.py | 25 +++++++++ .../features/test_optional_extras.py | 56 +++++++++++++++++++ 7 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 tests/langchain/features/test_optional_extras.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fd87675..c7961fd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,9 @@ on: - "uv.lock" - ".python-version" +permissions: + contents: read + jobs: type_check: name: Type Check and Lint @@ -41,6 +44,31 @@ jobs: - name: Pyright run: uv run pyright . + import_without_extras: + name: Import Without Extras + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "0.9.27" + enable-cache: true + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version-file: ".python-version" + + - name: Install without extras + run: uv sync --no-dev --package uipath-langchain-client + + - name: Import packages + run: | + uv run --no-sync python -c "import uipath.llm_client" + uv run --no-sync python -c "import uipath_langchain_client" + test: name: Test runs-on: ubuntu-latest diff --git a/packages/uipath_langchain_client/CHANGELOG.md b/packages/uipath_langchain_client/CHANGELOG.md index e55a3e8a..a8954979 100644 --- a/packages/uipath_langchain_client/CHANGELOG.md +++ b/packages/uipath_langchain_client/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to `uipath_langchain_client` will be documented in this file. +## [1.16.1] - 2026-07-03 + +### Fixed +- `import uipath_langchain_client` no longer requires the `bedrock` extra (regression in 1.15.1). +- `UiPathChatBedrock.get_num_tokens_from_messages` no longer crashes with `AttributeError` for BYO models backed by Anthropic models (regression in 1.15.1). +- `UiPathChatBedrockConverse.get_num_tokens_from_messages` skips the count-tokens API the same way instead of logging a `count_tokens API failed` warning on every call. + ## [1.16.0] - 2026-07-03 ### Changed diff --git a/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py b/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py index f8d61b09..67ef7a71 100644 --- a/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py +++ b/packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py @@ -1,3 +1,3 @@ __title__ = "UiPath LangChain Client" __description__ = "A Python client for interacting with UiPath's LLM services via LangChain." -__version__ = "1.16.0" +__version__ = "1.16.1" diff --git a/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py b/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py index e0b45bb3..befcd0c0 100644 --- a/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py +++ b/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py @@ -1,6 +1,9 @@ +from collections.abc import Sequence from functools import cached_property from typing import Any, Self +from langchain_core.language_models import BaseLanguageModel +from langchain_core.messages import BaseMessage from pydantic import Field, model_validator from uipath_langchain_client.base_client import UiPathBaseChatModel @@ -80,6 +83,12 @@ def setup_uipath_client(self) -> Self: self.client = WrappedBotoClient(self.uipath_sync_client) return self + def get_num_tokens_from_messages( + self, messages: list[BaseMessage], tools: Sequence[Any] | None = None + ) -> int: + """Skip the Bedrock count-tokens API; WrappedBotoClient does not support it.""" + return BaseLanguageModel.get_num_tokens_from_messages(self, messages, tools) + class UiPathChatBedrock(UiPathBaseChatModel, ChatBedrock): # type: ignore[override] api_config: UiPathAPIConfig = UiPathAPIConfig( @@ -109,6 +118,12 @@ def setup_uipath_client(self) -> Self: def _as_converse(self) -> UiPathChatBedrockConverse: raise NotImplementedError("You must instantiate the converse client directly") + def get_num_tokens_from_messages( + self, messages: list[BaseMessage], tools: Sequence[Any] | None = None + ) -> int: + """Skip the Bedrock count-tokens API; WrappedBotoClient does not support it.""" + return BaseLanguageModel.get_num_tokens_from_messages(self, messages, tools) + class UiPathChatAnthropicBedrock(UiPathBaseChatModel, ChatAnthropicBedrock): api_config: UiPathAPIConfig = UiPathAPIConfig( diff --git a/packages/uipath_langchain_client/src/uipath_langchain_client/factory.py b/packages/uipath_langchain_client/src/uipath_langchain_client/factory.py index a7e36be8..aac3fb99 100644 --- a/packages/uipath_langchain_client/src/uipath_langchain_client/factory.py +++ b/packages/uipath_langchain_client/src/uipath_langchain_client/factory.py @@ -26,9 +26,6 @@ UiPathBaseChatModel, UiPathBaseEmbeddings, ) -from uipath_langchain_client.clients.bedrock.model_resolution import ( - apply_backing_model_detection_hints, -) from uipath_langchain_client.settings import ( API_FLAVOR_TO_VENDOR_TYPE, BYOM_TO_ROUTING_FLAVOR, @@ -221,6 +218,10 @@ def get_chat_model( **model_kwargs, ) + from uipath_langchain_client.clients.bedrock.model_resolution import ( + apply_backing_model_detection_hints, + ) + apply_backing_model_detection_hints(model_kwargs, model_info) if api_flavor == ApiFlavor.INVOKE: diff --git a/tests/langchain/features/test_factory_function.py b/tests/langchain/features/test_factory_function.py index 566e28d6..abd92f5a 100644 --- a/tests/langchain/features/test_factory_function.py +++ b/tests/langchain/features/test_factory_function.py @@ -477,3 +477,28 @@ def test_invoke_byo_alias_gets_provider(self, client_settings): assert model.base_model_id == "anthropic.claude-sonnet-4-5-20250929-v1:0" assert model.provider == "anthropic" assert model._get_provider() == "anthropic" + + def test_invoke_byo_alias_token_counting_uses_fallback(self, client_settings): + from langchain_core.messages import HumanMessage + + self._seed(client_settings, _BYO_BEDROCK_INVOKE) + model = get_chat_model( + "AWS - Bedrock", + byo_connection_id="conn-x", + client_settings=client_settings, + custom_get_token_ids=lambda text: list(range(len(text.split()))), + ) + assert model.get_num_tokens_from_messages([HumanMessage("hello there")]) > 0 + + def test_converse_byo_alias_token_counting_uses_fallback(self, client_settings, caplog): + from langchain_core.messages import HumanMessage + + self._seed(client_settings, _BYO_BEDROCK_CONVERSE) + model = get_chat_model( + "AWS - Bedrock", + byo_connection_id="conn-x", + client_settings=client_settings, + custom_get_token_ids=lambda text: list(range(len(text.split()))), + ) + assert model.get_num_tokens_from_messages([HumanMessage("hello there")]) > 0 + assert "count_tokens API failed" not in caplog.text diff --git a/tests/langchain/features/test_optional_extras.py b/tests/langchain/features/test_optional_extras.py new file mode 100644 index 00000000..580597cc --- /dev/null +++ b/tests/langchain/features/test_optional_extras.py @@ -0,0 +1,56 @@ +"""The package must import without optional provider extras installed.""" + +import subprocess +import sys + +_OPTIONAL_MODULES = [ + "anthropic", + "google.genai", + "langchain_anthropic", + "langchain_aws", + "langchain_azure_ai", + "langchain_fireworks", + "langchain_google_genai", + "langchain_google_vertexai", + "langchain_litellm", + "langchain_openai", + "litellm", + "openai", +] + +_IMPORT_WITH_BLOCKED_EXTRAS = """ +import sys + +blocked = set(sys.argv[1:]) + + +class _BlockOptionalDeps: + def find_spec(self, name, path=None, target=None): + if any(name == module or name.startswith(module + ".") for module in blocked): + raise ImportError(f"blocked optional dependency: {name}") + return None + + +sys.meta_path.insert(0, _BlockOptionalDeps()) + +import uipath_langchain_client + +providers = [ + name + for name in sys.modules + if name.startswith("uipath_langchain_client.clients.") + and not name.startswith("uipath_langchain_client.clients.normalized") +] +assert not providers, providers +print("OK") +""" + + +def test_package_imports_without_optional_extras(): + result = subprocess.run( + [sys.executable, "-c", _IMPORT_WITH_BLOCKED_EXTRAS, *_OPTIONAL_MODULES], + capture_output=True, + text=True, + ) + assert result.returncode == 0, result.stderr + assert result.stdout.strip() == "OK" From ae5baf9c9ad6c316bbcfec16a5c2ad97e39fdde3 Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Fri, 3 Jul 2026 18:31:35 +0300 Subject: [PATCH 2/2] chore: revert get_num_tokens_from_messages overrides --- packages/uipath_langchain_client/CHANGELOG.md | 2 -- .../clients/bedrock/chat_models.py | 15 ----------- .../features/test_factory_function.py | 25 ------------------- 3 files changed, 42 deletions(-) diff --git a/packages/uipath_langchain_client/CHANGELOG.md b/packages/uipath_langchain_client/CHANGELOG.md index a8954979..276b7681 100644 --- a/packages/uipath_langchain_client/CHANGELOG.md +++ b/packages/uipath_langchain_client/CHANGELOG.md @@ -6,8 +6,6 @@ All notable changes to `uipath_langchain_client` will be documented in this file ### Fixed - `import uipath_langchain_client` no longer requires the `bedrock` extra (regression in 1.15.1). -- `UiPathChatBedrock.get_num_tokens_from_messages` no longer crashes with `AttributeError` for BYO models backed by Anthropic models (regression in 1.15.1). -- `UiPathChatBedrockConverse.get_num_tokens_from_messages` skips the count-tokens API the same way instead of logging a `count_tokens API failed` warning on every call. ## [1.16.0] - 2026-07-03 diff --git a/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py b/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py index befcd0c0..e0b45bb3 100644 --- a/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py +++ b/packages/uipath_langchain_client/src/uipath_langchain_client/clients/bedrock/chat_models.py @@ -1,9 +1,6 @@ -from collections.abc import Sequence from functools import cached_property from typing import Any, Self -from langchain_core.language_models import BaseLanguageModel -from langchain_core.messages import BaseMessage from pydantic import Field, model_validator from uipath_langchain_client.base_client import UiPathBaseChatModel @@ -83,12 +80,6 @@ def setup_uipath_client(self) -> Self: self.client = WrappedBotoClient(self.uipath_sync_client) return self - def get_num_tokens_from_messages( - self, messages: list[BaseMessage], tools: Sequence[Any] | None = None - ) -> int: - """Skip the Bedrock count-tokens API; WrappedBotoClient does not support it.""" - return BaseLanguageModel.get_num_tokens_from_messages(self, messages, tools) - class UiPathChatBedrock(UiPathBaseChatModel, ChatBedrock): # type: ignore[override] api_config: UiPathAPIConfig = UiPathAPIConfig( @@ -118,12 +109,6 @@ def setup_uipath_client(self) -> Self: def _as_converse(self) -> UiPathChatBedrockConverse: raise NotImplementedError("You must instantiate the converse client directly") - def get_num_tokens_from_messages( - self, messages: list[BaseMessage], tools: Sequence[Any] | None = None - ) -> int: - """Skip the Bedrock count-tokens API; WrappedBotoClient does not support it.""" - return BaseLanguageModel.get_num_tokens_from_messages(self, messages, tools) - class UiPathChatAnthropicBedrock(UiPathBaseChatModel, ChatAnthropicBedrock): api_config: UiPathAPIConfig = UiPathAPIConfig( diff --git a/tests/langchain/features/test_factory_function.py b/tests/langchain/features/test_factory_function.py index abd92f5a..566e28d6 100644 --- a/tests/langchain/features/test_factory_function.py +++ b/tests/langchain/features/test_factory_function.py @@ -477,28 +477,3 @@ def test_invoke_byo_alias_gets_provider(self, client_settings): assert model.base_model_id == "anthropic.claude-sonnet-4-5-20250929-v1:0" assert model.provider == "anthropic" assert model._get_provider() == "anthropic" - - def test_invoke_byo_alias_token_counting_uses_fallback(self, client_settings): - from langchain_core.messages import HumanMessage - - self._seed(client_settings, _BYO_BEDROCK_INVOKE) - model = get_chat_model( - "AWS - Bedrock", - byo_connection_id="conn-x", - client_settings=client_settings, - custom_get_token_ids=lambda text: list(range(len(text.split()))), - ) - assert model.get_num_tokens_from_messages([HumanMessage("hello there")]) > 0 - - def test_converse_byo_alias_token_counting_uses_fallback(self, client_settings, caplog): - from langchain_core.messages import HumanMessage - - self._seed(client_settings, _BYO_BEDROCK_CONVERSE) - model = get_chat_model( - "AWS - Bedrock", - byo_connection_id="conn-x", - client_settings=client_settings, - custom_get_token_ids=lambda text: list(range(len(text.split()))), - ) - assert model.get_num_tokens_from_messages([HumanMessage("hello there")]) > 0 - assert "count_tokens API failed" not in caplog.text