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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 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.10.1] - 2026-05-08

### Added
- `agenthub_config` kwarg on `get_chat_model` and `get_embedding_model`. When set, overrides `client_settings.agenthub_config` for that call via `model_copy`, so the caller's settings instance is not mutated. Lets callers (e.g. low-code agent runtimes) pass the per-execution AgentHub config (`agentsruntime`, `agentsplayground`, `agentsevals`, …) without rebuilding settings.

## [1.10.0] - 2026-04-23

### Added
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.10.0"
__version__ = "1.10.1"
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def get_chat_model(
vendor_type: VendorType | str | None = None,
api_flavor: ApiFlavor | str | None = None,
custom_class: type[UiPathBaseChatModel] | None = None,
agenthub_config: str | None = None,
**model_kwargs: Any,
) -> UiPathBaseChatModel:
"""Factory function to create the appropriate LangChain chat model for a given model name.
Expand All @@ -70,6 +71,9 @@ def get_chat_model(
custom_class: A custom class to use for instantiating the chat model instead of the
auto-detected one. Must be a subclass of UiPathBaseChatModel. When provided,
the factory skips vendor detection and uses this class directly.
agenthub_config: AgentHub config header value to send with requests. When set,
overrides ``client_settings.agenthub_config`` for this call. Returns a copy
of ``client_settings`` so the caller's instance is not mutated.
**model_kwargs: Additional keyword arguments to pass to the model constructor.

Returns:
Expand All @@ -79,6 +83,8 @@ def get_chat_model(
ValueError: If the model is not found in available models or vendor is not supported.
"""
client_settings = client_settings or get_default_client_settings()
if agenthub_config is not None and hasattr(client_settings, "agenthub_config"):
client_settings = client_settings.model_copy(update={"agenthub_config": agenthub_config})
model_info = client_settings.get_model_info(
model_name,
byo_connection_id=byo_connection_id,
Expand Down Expand Up @@ -238,6 +244,7 @@ def get_embedding_model(
routing_mode: RoutingMode | str = RoutingMode.PASSTHROUGH,
vendor_type: VendorType | str | None = None,
custom_class: type[UiPathBaseEmbeddings] | None = None,
agenthub_config: str | None = None,
**model_kwargs: Any,
) -> UiPathBaseEmbeddings:
"""Factory function to create the appropriate LangChain embeddings model.
Expand All @@ -255,6 +262,9 @@ def get_embedding_model(
custom_class: A custom class to use for instantiating the embedding model instead of
the auto-detected one. Must be a subclass of UiPathBaseEmbeddings. When provided,
the factory skips vendor detection and uses this class directly.
agenthub_config: AgentHub config header value to send with requests. When set,
overrides ``client_settings.agenthub_config`` for this call. Returns a copy
of ``client_settings`` so the caller's instance is not mutated.
**model_kwargs: Additional arguments passed to the embeddings constructor.

Returns:
Expand All @@ -269,6 +279,8 @@ def get_embedding_model(
>>> vectors = embeddings.embed_documents(["Hello world"])
"""
client_settings = client_settings or get_default_client_settings()
if agenthub_config is not None and hasattr(client_settings, "agenthub_config"):
client_settings = client_settings.model_copy(update={"agenthub_config": agenthub_config})
model_info = client_settings.get_model_info(
model_name,
byo_connection_id=byo_connection_id,
Expand Down
79 changes: 79 additions & 0 deletions tests/langchain/features/test_factory_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,82 @@ def test_openai_chat_respects_discovered_byom_chat_completions(
},
)
assert captured["api_flavor"] == ApiFlavor.CHAT_COMPLETIONS


class TestFactoryAgentHubConfig:
"""The ``agenthub_config`` factory kwarg overrides ``client_settings.agenthub_config``
via ``model_copy`` so the caller's instance is not mutated."""

def _capture_settings(
self,
monkeypatch: pytest.MonkeyPatch,
model_info: dict,
original_settings,
**factory_kwargs,
):
captured: dict = {}

class _StubModel:
def __init__(self, **kwargs):
captured.update(kwargs)

monkeypatch.setattr(
"uipath_langchain_client.clients.openai.chat_models.UiPathAzureChatOpenAI",
_StubModel,
)
get_chat_model(
model_name=model_info["modelName"],
client_settings=original_settings,
**factory_kwargs,
)
return captured

def _make_settings(self, agenthub_config: str | None):
settings = MagicMock()
settings.get_model_info.return_value = {
"modelName": "gpt-4o",
"vendor": "OpenAi",
"apiFlavor": None,
"modelFamily": "OpenAi",
}
settings.agenthub_config = agenthub_config

def _model_copy(*, update):
copied = MagicMock()
copied.get_model_info.return_value = settings.get_model_info.return_value
copied.agenthub_config = update.get("agenthub_config", agenthub_config)
return copied

settings.model_copy.side_effect = _model_copy
return settings

def test_kwarg_overrides_settings_value(self, monkeypatch: pytest.MonkeyPatch):
original = self._make_settings(agenthub_config="agentsruntime")
captured = self._capture_settings(
monkeypatch,
original.get_model_info.return_value,
original,
agenthub_config="agentsplayground",
)
assert captured["settings"].agenthub_config == "agentsplayground"
original.model_copy.assert_called_once_with(update={"agenthub_config": "agentsplayground"})

def test_caller_settings_not_mutated(self, monkeypatch: pytest.MonkeyPatch):
original = self._make_settings(agenthub_config="agentsruntime")
self._capture_settings(
monkeypatch,
original.get_model_info.return_value,
original,
agenthub_config="agentsplayground",
)
assert original.agenthub_config == "agentsruntime"

def test_no_kwarg_keeps_settings_value(self, monkeypatch: pytest.MonkeyPatch):
original = self._make_settings(agenthub_config="agentsruntime")
captured = self._capture_settings(
monkeypatch,
original.get_model_info.return_value,
original,
)
assert captured["settings"] is original
original.model_copy.assert_not_called()