Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ LLMGW_ACCESS_TOKEN=""
LLMGW_CLIENT_ID=""
LLMGW_CLIENT_SECRET=""

# Optional tracking and tracing
# Optional for tracking and BYOM
LLMGW_SEMANTIC_USER_ID=""

LLMGW_ACTION_ID=""
LLMGW_ADDITIONAL_HEADERS=""
LLMGW_OPERATION_CODE=""
LlMGW_ADDITIONAL_HEADERS=""
9 changes: 7 additions & 2 deletions packages/uipath_langchain_client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

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

## [1.1.2] - 2026-02-12

### Refactor
- Rename normalized client for better comaptibility with other packages

## [1.1.1] - 2026-02-11

### Fixes
Expand Down Expand Up @@ -135,15 +140,15 @@ All notable changes to `uipath_langchain_client` will be documented in this file
- `UiPathChatBedrock` - AWS Bedrock models
- `UiPathChatBedrockConverse` - AWS Bedrock Converse API
- `UiPathAzureAIChatCompletionsModel` - Azure AI models (non-OpenAI)
- `UiPathNormalizedChatModel` - Provider-agnostic normalized API
- `UiPathChat` - Provider-agnostic normalized API

### Embeddings Classes
- `UiPathOpenAIEmbeddings` - OpenAI embeddings via direct API
- `UiPathAzureOpenAIEmbeddings` - OpenAI embeddings via Azure
- `UiPathGoogleGenerativeAIEmbeddings` - Google embeddings
- `UiPathBedrockEmbeddings` - AWS Bedrock embeddings
- `UiPathAzureAIEmbeddingsModel` - Azure AI embeddings
- `UiPathNormalizedEmbeddings` - Provider-agnostic normalized API
- `UiPathEmbeddings` - Provider-agnostic normalized API

### Features
- Support for BYO (Bring Your Own) model connections
Expand Down
8 changes: 4 additions & 4 deletions packages/uipath_langchain_client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ For more control, instantiate provider-specific classes directly:
from uipath_langchain_client.openai.chat_models import UiPathAzureChatOpenAI
from uipath_langchain_client.google.chat_models import UiPathChatGoogleGenerativeAI
from uipath_langchain_client.anthropic.chat_models import UiPathChatAnthropic
from uipath_langchain_client.normalized.chat_models import UiPathNormalizedChatModel
from uipath_langchain_client.normalized.chat_models import UiPathChat
from uipath_langchain_client.settings import get_default_client_settings

settings = get_default_client_settings()
Expand All @@ -75,7 +75,7 @@ claude_chat = UiPathChatAnthropic(
)

# Normalized (provider-agnostic)
normalized_chat = UiPathNormalizedChatModel(model="gpt-4o-2024-11-20", settings=settings)
normalized_chat = UiPathChat(model="gpt-4o-2024-11-20", settings=settings)
```

## Available Client Types
Expand All @@ -99,8 +99,8 @@ Uses UiPath's normalized API for a consistent interface across all providers.

| Class | Description |
|-------|-------------|
| `UiPathNormalizedChatModel` | Provider-agnostic chat completions |
| `UiPathNormalizedEmbeddings` | Provider-agnostic embeddings |
| `UiPathChat` | Provider-agnostic chat completions |
| `UiPathEmbeddings` | Provider-agnostic embeddings |

## Features

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.1.1"
__version__ = "1.1.2"
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,21 @@ class UiPathChatAnthropic(UiPathBaseLLMClient, ChatAnthropic):
vendor_type="anthropic",
freeze_base_url=True,
)
vendor_type: Literal["anthropic", "azure", "vertexai", "awsbedrock"] = "awsbedrock"
vendor_type: Literal["anthropic", "azure", "vertexai", "awsbedrock"] = "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"
self.api_config.api_version = "v1beta1"
case "awsbedrock":
self.api_config.api_flavor = "invoke"
case _:
raise ValueError("Those vendors are currently not supported")
self.api_config.vendor_type = self.vendor_type
raise ValueError(
"anthropic and azure vendors are currently not supported by UiPath"
)
return self

# Override fields to avoid typing issues and fix stuff
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from uipath_langchain_client.clients.normalized.chat_models import UiPathNormalizedChatModel
from uipath_langchain_client.clients.normalized.embeddings import UiPathNormalizedEmbeddings
from uipath_langchain_client.clients.normalized.chat_models import UiPathChat
from uipath_langchain_client.clients.normalized.embeddings import UiPathEmbeddings

__all__ = ["UiPathNormalizedChatModel", "UiPathNormalizedEmbeddings"]
__all__ = ["UiPathChat", "UiPathEmbeddings"]
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
- Extended thinking/reasoning parameters for supported models

Example:
>>> from uipath_langchain_client.normalized.chat_models import UiPathNormalizedChatModel
>>> from uipath_langchain_client.normalized.chat_models import UiPathChat
>>> from uipath_langchain_client.settings import get_default_client_settings
>>>
>>> settings = get_default_client_settings()
>>> chat = UiPathNormalizedChatModel(
>>> chat = UiPathChat(
... model="gpt-4o-2024-11-20",
... settings=settings,
... )
Expand Down Expand Up @@ -60,7 +60,7 @@
from uipath_langchain_client.settings import UiPathAPIConfig


class UiPathNormalizedChatModel(UiPathBaseLLMClient, BaseChatModel):
class UiPathChat(UiPathBaseLLMClient, BaseChatModel):
"""LangChain chat model using UiPath's normalized (provider-agnostic) API.

This model provides a consistent interface across all LLM providers supported
Expand All @@ -86,7 +86,7 @@ class UiPathNormalizedChatModel(UiPathBaseLLMClient, BaseChatModel):
include_thoughts: Whether to include thinking in Gemini responses.

Example:
>>> chat = UiPathNormalizedChatModel(
>>> chat = UiPathChat(
... model="gpt-4o-2024-11-20",
... settings=settings,
... temperature=0.7,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from uipath_langchain_client.settings import UiPathAPIConfig


class UiPathNormalizedEmbeddings(UiPathBaseLLMClient, Embeddings):
class UiPathEmbeddings(UiPathBaseLLMClient, Embeddings):
"""LangChain embeddings using the UiPath's normalized embeddings API.

Provides a consistent interface for generating text embeddings across all
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Awaitable, Callable
from typing import Self

from httpx import Request
from pydantic import Field, SecretStr, model_validator
from uipath_langchain_client.base_client import UiPathBaseLLMClient
from uipath_langchain_client.settings import UiPathAPIConfig
Expand All @@ -21,25 +22,36 @@ class UiPathChatOpenAI(UiPathBaseLLMClient, ChatOpenAI): # type: ignore[overrid
api_type="completions",
client_type="passthrough",
vendor_type="openai",
freeze_base_url=True,
api_version="2025-03-01-preview",
freeze_base_url=False,
)

# Override fields to avoid errors when instantiating the class
openai_api_key: SecretStr | None | Callable[[], str] | Callable[[], Awaitable[str]] = Field(
alias="api_key", default=SecretStr("PLACEHOLDER")
)

@model_validator(mode="after")
def setup_uipath_api_flavor_and_version(self) -> Self:
self.api_config.api_version = "2025-03-01-preview"
if self._use_responses_api({}):
self.api_config.api_flavor = "responses"
else:
self.api_config.api_flavor = "chat-completions"
return self

@model_validator(mode="after")
def setup_uipath_client(self) -> Self:
def fix_url_and_api_flavor_header(request: Request):
url_suffix = str(request.url).split(str(self.uipath_sync_client.base_url))[-1]
if "responses" in url_suffix:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "responses"
else:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "chat-completions"
request.url = self.uipath_sync_client.base_url

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

self.uipath_sync_client.event_hooks["request"].append(fix_url_and_api_flavor_header)
self.uipath_async_client.event_hooks["request"].append(fix_url_and_api_flavor_header_async)

self.root_client = OpenAI(
api_key="PLACEHOLDER",
timeout=None, # handled by the UiPath client
Expand All @@ -62,25 +74,35 @@ class UiPathAzureChatOpenAI(UiPathBaseLLMClient, AzureChatOpenAI): # type: igno
api_type="completions",
client_type="passthrough",
vendor_type="openai",
freeze_base_url=True,
api_version="2025-03-01-preview",
freeze_base_url=False,
)

# Override fields to avoid errors when instantiating the class
azure_endpoint: str | None = Field(default="PLACEHOLDER")
openai_api_version: str | None = Field(default="PLACEHOLDER", alias="api_version")
openai_api_key: SecretStr | None = Field(default=SecretStr("PLACEHOLDER"), alias="api_key")

@model_validator(mode="after")
def setup_uipath_api_flavor_and_version(self) -> Self:
self.api_config.api_version = "2025-03-01-preview"
if self._use_responses_api({}):
self.api_config.api_flavor = "responses"
else:
self.api_config.api_flavor = "chat-completions"
return self

@model_validator(mode="after")
def setup_uipath_client(self) -> Self:
def fix_url_and_api_flavor_header(request: Request):
url_suffix = str(request.url).split(str(self.uipath_sync_client.base_url))[-1]
if "responses" in url_suffix:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "responses"
else:
request.headers["X-UiPath-LlmGateway-ApiFlavor"] = "chat-completions"
request.url = self.uipath_sync_client.base_url

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

self.uipath_sync_client.event_hooks["request"].append(fix_url_and_api_flavor_header)
self.uipath_async_client.event_hooks["request"].append(fix_url_and_api_flavor_header_async)
self.root_client = AzureOpenAI(
azure_endpoint="PLACEHOLDER",
api_version="PLACEHOLDER",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ def get_chat_model(

if client_type == "normalized":
from uipath_langchain_client.clients.normalized.chat_models import (
UiPathNormalizedChatModel,
UiPathChat,
)

return UiPathNormalizedChatModel(
return UiPathChat(
model=model_name,
settings=client_settings,
byo_connection_id=byo_connection_id,
Expand Down Expand Up @@ -239,10 +239,10 @@ def get_embedding_model(

if client_type == "normalized":
from uipath_langchain_client.clients.normalized.embeddings import (
UiPathNormalizedEmbeddings,
UiPathEmbeddings,
)

return UiPathNormalizedEmbeddings(
return UiPathEmbeddings(
model=model_name,
settings=client_settings,
byo_connection_id=byo_connection_id,
Expand Down
2 changes: 1 addition & 1 deletion src/uipath_llm_client/httpx_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class UiPathHttpxAsyncClient(AsyncClient):

_streaming_header: str = "X-UiPath-Streaming-Enabled"
_default_headers: Mapping[str, str] = {
"X-UiPath-LLMGateway-TimeoutSeconds": "30", # server side timeout, default is 10, maximum is 300
"X-UiPath-LLMGateway-TimeoutSeconds": "295", # server side timeout, default is 10, maximum is 300
"X-UiPath-LLMGateway-AllowFull4xxResponse": "true", # allow full 4xx responses (default is false)
}

Expand Down
22 changes: 11 additions & 11 deletions tests/langchain/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from uipath_langchain_client.clients.bedrock.embeddings import UiPathBedrockEmbeddings
from uipath_langchain_client.clients.google.chat_models import UiPathChatGoogleGenerativeAI
from uipath_langchain_client.clients.google.embeddings import UiPathGoogleGenerativeAIEmbeddings
from uipath_langchain_client.clients.normalized.chat_models import UiPathNormalizedChatModel
from uipath_langchain_client.clients.normalized.chat_models import UiPathChat
from uipath_langchain_client.clients.openai.chat_models import (
UiPathAzureChatOpenAI,
UiPathChatOpenAI,
Expand All @@ -28,7 +28,7 @@
GPT_MODELS_NON_REASONING_CONFIGS = [
{"model_class": UiPathAzureChatOpenAI},
{"model_class": UiPathAzureChatOpenAI, "model_kwargs": {"use_responses_api": True}},
# {"model_class": UiPathNormalizedChatModel},
# {"model_class": UiPathChat},
]

GPT_MODELS_WITH_REASONING_CONFIGS = [
Expand All @@ -43,7 +43,7 @@
"verbosity": "low",
},
},
# {"model_class": UiPathNormalizedChatModel, "model_kwargs": {"reasoning_effort": "low"}},
# {"model_class": UiPathChat, "model_kwargs": {"reasoning_effort": "low"}},
]

GEMINI_2_5_CONFIGS = [
Expand All @@ -56,7 +56,7 @@
"model_kwargs": {"thinking_budget": 128, "include_thoughts": True},
},
# {
# "model_class": UiPathNormalizedChatModel,
# "model_class": UiPathChat,
# "model_kwargs": {"thinking_budget": 128, "include_thoughts": True},
# },
]
Expand All @@ -71,7 +71,7 @@
"model_kwargs": {"thinking_level": "low", "include_thoughts": True},
},
# {
# "model_class": UiPathNormalizedChatModel,
# "model_class": UiPathChat,
# "model_kwargs": {"thinking_level": "low", "include_thoughts": True},
# },
]
Expand Down Expand Up @@ -101,7 +101,7 @@
"thinking": {"type": "enabled", "budget_tokens": 1024},
},
},
# {"model_class": UiPathNormalizedChatModel},
# {"model_class": UiPathChat},
]

CLAUDE_MODELS_AWSBEDROCK_CONFIGS = [
Expand Down Expand Up @@ -139,7 +139,7 @@
"thinking": {"type": "enabled", "budget_tokens": 1024},
},
},
# {"model_class": UiPathNormalizedChatModel},
# {"model_class": UiPathChat},
]

COMPLETIONS_MODELS_WITH_CONFIGS = {
Expand All @@ -164,7 +164,7 @@


COMPLETION_CLIENTS_CLASSES = [
UiPathNormalizedChatModel,
UiPathChat,
UiPathChatOpenAI,
UiPathAzureChatOpenAI,
# UiPathAzureAIChatCompletionsModel,
Expand All @@ -175,7 +175,7 @@
# UiPathChatBedrockConverse,
]
EMBEDDINGS_CLIENTS_CLASSES = [
# UiPathNormalizedEmbeddings,
# UiPathEmbeddings,
UiPathOpenAIEmbeddings,
UiPathAzureOpenAIEmbeddings,
# UiPathAzureAIEmbeddingsModel,
Expand Down Expand Up @@ -214,11 +214,11 @@ def completions_config(
EMBEDDINGS_MODELS_WITH_CONFIGS = {
"text-embedding-3-large": [
{"model_class": UiPathAzureOpenAIEmbeddings},
# {"model_class": UiPathNormalizedEmbeddings},
# {"model_class": UiPathEmbeddings},
],
"gemini-embedding-001": [
{"model_class": UiPathGoogleGenerativeAIEmbeddings},
# {"model_class": UiPathNormalizedEmbeddings},
# {"model_class": UiPathEmbeddings},
],
}

Expand Down
Loading
Loading