Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

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

## [1.4.0] - 2026-03-13

### New client
- Added UiPathChatAnthropicBedrock

### Tests
- added sqlite serializer
- test updates
- new cassettes

### Fixes
- Added constants for VendorType and ApiFlavor

## [1.3.0] - 2026-03-10

### Version Bump
Expand Down
8 changes: 8 additions & 0 deletions packages/uipath_langchain_client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

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

## [1.4.0] - 2026-03-13

### New client
- Added UiPathChatAnthropicBedrock
- refactored factory function to se the new client
- brought the enums from the base client


## [1.3.1] - 2026-03-12

### Fix
Expand Down
4 changes: 3 additions & 1 deletion packages/uipath_langchain_client/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from uipath_langchain_client import get_chat_model, get_embedding_model
from uipath_langchain_client.settings import get_default_client_settings

from uipath.llm_client.settings.constants import RoutingMode


def demo_basic_chat():
"""Demonstrate basic chat completion using the factory function."""
Expand Down Expand Up @@ -199,7 +201,7 @@ def demo_normalized_api():
chat_model = get_chat_model(
model_name="gpt-4o-2024-11-20",
client_settings=settings,
client_type="normalized", # Use normalized API
routing_mode=RoutingMode.NORMALIZED, # Use normalized API
)

response = chat_model.invoke("What is machine learning in one sentence?")
Expand Down
14 changes: 7 additions & 7 deletions packages/uipath_langchain_client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ dynamic = ["version"]
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"langchain>=1.2.10",
"uipath-llm-client>=1.3.0",
"langchain>=1.2.12",
"uipath-llm-client>=1.4.0",
]

[project.optional-dependencies]
openai = [
"langchain-openai>=1.1.11",
]
aws = [
"langchain-aws>=1.4.0",
]
google = [
"langchain-google-genai>=4.2.1",
]
anthropic = [
"langchain-anthropic>=1.3.4",
"anthropic[bedrock,vertex]>=0.84.0",
]
azure = [
"langchain-azure-ai>=1.1.0",
aws = [
"langchain-aws[anthropic]>=1.4.0",
]
vertexai = [
"langchain-google-vertexai>=3.2.2",
]
azure = [
"langchain-azure-ai>=1.1.0",
]
fireworks = [
"langchain-fireworks>=1.1.0",
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__title__ = "UiPath LangChain Client"
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
__version__ = "1.3.1"
__version__ = "1.4.0"
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
>>> client = UiPathBaseLLMClient(
... model="gpt-4o-2024-11-20",
... api_config=UiPathAPIConfig(
... api_type="completions",
... client_type="passthrough",
... api_type=ApiType.COMPLETIONS,
... routing_mode=RoutingMode.PASSTHROUGH,
... vendor_type="openai",
... ),
... client_settings=get_default_client_settings(),
Expand Down Expand Up @@ -67,7 +67,7 @@ class UiPathBaseLLMClient(BaseModel, ABC):
model_name: Name of the LLM model to use (aliased as "model")
byo_connection_id: Optional connection ID for Bring Your Own (BYO) models enrolled
in LLMGateway. When provided, routes requests to your custom-enrolled model.
api_config: API configuration (api_type, client_type, vendor_type, etc.)
api_config: API configuration (api_type, routing_mode, vendor_type, etc.)
client_settings: Client configuration (base URL, auth headers, etc.)
default_headers: Additional headers to include in requests
request_timeout: Client-side request timeout in seconds
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from functools import cached_property
from typing import Any, Literal, Self
from typing import Any, Self

from pydantic import Field, model_validator
from typing_extensions import override

from uipath_langchain_client.base_client import UiPathBaseChatModel
from uipath_langchain_client.settings import UiPathAPIConfig
from uipath_langchain_client.settings import (
ApiFlavor,
ApiType,
RoutingMode,
UiPathAPIConfig,
VendorType,
)

try:
from anthropic import (
Expand All @@ -28,22 +34,22 @@

class UiPathChatAnthropic(UiPathBaseChatModel, ChatAnthropic):
api_config: UiPathAPIConfig = UiPathAPIConfig(
api_type="completions",
client_type="passthrough",
vendor_type="awsbedrock",
api_type=ApiType.COMPLETIONS,
routing_mode=RoutingMode.PASSTHROUGH,
vendor_type=VendorType.ANTHROPIC,
freeze_base_url=True,
)
vendor_type: Literal["anthropic", "azure", "vertexai", "awsbedrock"] = "awsbedrock"
vendor_type: VendorType = VendorType.ANTHROPIC

@model_validator(mode="after")
def setup_api_flavor_and_version(self) -> Self:
self.api_config.vendor_type = self.vendor_type
match self.vendor_type:
case "vertexai":
self.api_config.api_flavor = "anthropic-claude"
case VendorType.VERTEXAI:
self.api_config.api_flavor = ApiFlavor.ANTHROPIC_CLAUDE
self.api_config.api_version = "v1beta1"
case "awsbedrock":
self.api_config.api_flavor = "invoke"
case VendorType.AWSBEDROCK:
self.api_config.api_flavor = ApiFlavor.INVOKE
case _:
raise ValueError(
"anthropic and azure vendors are currently not supported by UiPath"
Expand All @@ -60,15 +66,23 @@ def _anthropic_client(
self,
) -> Anthropic | AnthropicVertex | AnthropicBedrock | AnthropicFoundry:
match self.vendor_type:
case "azure":
case VendorType.ANTHROPIC:
return Anthropic(
api_key="PLACEHOLDER",
base_url=str(self.uipath_sync_client.base_url),
default_headers=dict(self.uipath_sync_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_sync_client,
)
case VendorType.AZURE:
return AnthropicFoundry(
api_key="PLACEHOLDER",
base_url=str(self.uipath_sync_client.base_url),
default_headers=dict(self.uipath_sync_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_sync_client,
)
case "vertexai":
case VendorType.VERTEXAI:
return AnthropicVertex(
region="PLACEHOLDER",
project_id="PLACEHOLDER",
Expand All @@ -78,7 +92,7 @@ def _anthropic_client(
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_sync_client,
)
case "awsbedrock":
case VendorType.AWSBEDROCK:
return AnthropicBedrock(
aws_access_key="PLACEHOLDER",
aws_secret_key="PLACEHOLDER",
Expand All @@ -88,29 +102,31 @@ def _anthropic_client(
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_sync_client,
)
case "anthropic":
return Anthropic(
api_key="PLACEHOLDER",
base_url=str(self.uipath_sync_client.base_url),
default_headers=dict(self.uipath_sync_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_sync_client,
)
case _:
raise ValueError("Anthropic models are currently not hosted on any other provider")

@cached_property
def _async_anthropic_client(
self,
) -> AsyncAnthropic | AsyncAnthropicVertex | AsyncAnthropicBedrock | AsyncAnthropicFoundry:
match self.vendor_type:
case "azure":
case VendorType.ANTHROPIC:
return AsyncAnthropic(
api_key="PLACEHOLDER",
base_url=str(self.uipath_async_client.base_url),
default_headers=dict(self.uipath_async_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_async_client,
)
case VendorType.AZURE:
return AsyncAnthropicFoundry(
api_key="PLACEHOLDER",
base_url=str(self.uipath_async_client.base_url),
default_headers=dict(self.uipath_async_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_async_client,
)
case "vertexai":
case VendorType.VERTEXAI:
return AsyncAnthropicVertex(
region="PLACEHOLDER",
project_id="PLACEHOLDER",
Expand All @@ -120,7 +136,7 @@ def _async_anthropic_client(
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_async_client,
)
case "awsbedrock":
case VendorType.AWSBEDROCK:
return AsyncAnthropicBedrock(
aws_access_key="PLACEHOLDER",
aws_secret_key="PLACEHOLDER",
Expand All @@ -131,13 +147,7 @@ def _async_anthropic_client(
http_client=self.uipath_async_client,
)
case _:
return AsyncAnthropic(
api_key="PLACEHOLDER",
base_url=str(self.uipath_async_client.base_url),
default_headers=dict(self.uipath_async_client.headers),
max_retries=0, # handled by the UiPathBaseChatModel
http_client=self.uipath_async_client,
)
raise ValueError("Anthropic models are currently not hosted on any other provider")

@override
def _create(self, payload: dict) -> Any:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
from pydantic import Field, model_validator

from uipath_langchain_client.base_client import UiPathBaseChatModel
from uipath_langchain_client.settings import UiPathAPIConfig
from uipath_langchain_client.settings import (
ApiFlavor,
ApiType,
RoutingMode,
UiPathAPIConfig,
VendorType,
)

try:
from azure.core.credentials import AzureKeyCredential, TokenCredential
Expand All @@ -20,9 +26,9 @@

class UiPathAzureAIChatCompletionsModel(UiPathBaseChatModel, AzureAIOpenAIApiChatModel): # type: ignore[override]
api_config: UiPathAPIConfig = UiPathAPIConfig(
api_type="completions",
client_type="passthrough",
vendor_type="azure",
api_type=ApiType.COMPLETIONS,
routing_mode=RoutingMode.PASSTHROUGH,
vendor_type=VendorType.AZURE,
freeze_base_url=False,
)

Expand All @@ -39,17 +45,17 @@ def setup_uipath_client(self) -> Self:
def fix_url_and_api_flavor_header(request: Request):
url_suffix = str(request.url).split(base_url)[-1]
if "responses" in url_suffix:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "responses"
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = ApiFlavor.RESPONSES.value
else:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "chat-completions"
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = ApiFlavor.CHAT_COMPLETIONS.value
request.url = URL(base_url)

async def fix_url_and_api_flavor_header_async(request: Request):
url_suffix = str(request.url).split(base_url)[-1]
if "responses" in url_suffix:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "responses"
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = ApiFlavor.RESPONSES.value
else:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "chat-completions"
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = ApiFlavor.CHAT_COMPLETIONS.value
request.url = URL(base_url)

self.uipath_sync_client.event_hooks["request"].append(fix_url_and_api_flavor_header)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import Field, model_validator

from uipath_langchain_client.base_client import UiPathBaseEmbeddings
from uipath_langchain_client.settings import UiPathAPIConfig
from uipath_langchain_client.settings import ApiType, RoutingMode, UiPathAPIConfig, VendorType

try:
from azure.core.credentials import AzureKeyCredential, TokenCredential
Expand All @@ -19,9 +19,9 @@

class UiPathAzureAIEmbeddingsModel(UiPathBaseEmbeddings, AzureAIOpenAIApiEmbeddingsModel): # type: ignore[override]
api_config: UiPathAPIConfig = UiPathAPIConfig(
api_type="embeddings",
client_type="passthrough",
vendor_type="azure",
api_type=ApiType.EMBEDDINGS,
routing_mode=RoutingMode.PASSTHROUGH,
vendor_type=VendorType.AZURE,
freeze_base_url=True,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from uipath_langchain_client.clients.bedrock.chat_models import (
UiPathChatAnthropicBedrock,
UiPathChatBedrock,
UiPathChatBedrockConverse,
)
from uipath_langchain_client.clients.bedrock.embeddings import UiPathBedrockEmbeddings

__all__ = ["UiPathChatBedrock", "UiPathChatBedrockConverse", "UiPathBedrockEmbeddings"]
__all__ = [
"UiPathChatBedrock",
"UiPathChatBedrockConverse",
"UiPathChatAnthropicBedrock",
"UiPathBedrockEmbeddings",
]
Loading