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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "uipath-langchain"
version = "0.16.15"
version = "0.16.16"
description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.6, <2.15.0",
"uipath-core>=0.5.29, <0.6.0",
"uipath-platform>=0.2.22, <0.3.0",
"uipath-platform>=0.2.24, <0.3.0",
"uipath-runtime>=0.13.0, <0.14.0",
"uipath-llm-client>=1.18.0, <1.19.0",
"langgraph>=1.1.8, <2.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@

BASE_SYSTEM_PROMPT = "base_system_prompt"

# Flag routing Data Fabric entity-metadata resolution to the V3 API.
ENTITY_V3_API_FF = "EnableEntityV3API"


@dataclass(frozen=True, slots=True)
class _DataFabricToolConfig:
Expand Down Expand Up @@ -82,12 +85,21 @@ async def _ensure_datafabric_graph(self) -> CompiledStateGraph[Any]:
if self._compiled is not None:
return self._compiled

from uipath.core.feature_flags import FeatureFlags
from uipath.platform import UiPath

from .datafabric_subgraph import DataFabricGraph

sdk = UiPath()
resolution = await sdk.entities.resolve_entity_set_async(self._entity_set)
# Flag on: resolve via the V3 API method; off: the default method.
if FeatureFlags.is_flag_enabled(ENTITY_V3_API_FF, default=False):
resolution = await sdk.entities.resolve_entity_set_v3_async(
self._entity_set
)
Comment thread
milind-jain-uipath marked this conversation as resolved.
else:
resolution = await sdk.entities.resolve_entity_set_async(
self._entity_set
)
Comment thread
milind-jain-uipath marked this conversation as resolved.
if not resolution.entities:
raise ValueError(
"No Data Fabric entity schemas could be fetched. "
Expand Down
61 changes: 61 additions & 0 deletions tests/agent/tools/test_datafabric_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest
from langchain_core.messages import AIMessage, ToolMessage
from uipath.agent.models.agent import AgentContextResourceConfig
from uipath.core.feature_flags import FeatureFlags
from uipath.platform.entities import DataFabricEntityItem

import uipath_langchain.agent.tools as agent_tools
Expand All @@ -13,6 +14,7 @@
)
from uipath_langchain.agent.tools.context_tool import create_context_tool
from uipath_langchain.agent.tools.datafabric_tool.datafabric_tool import (
ENTITY_V3_API_FF,
DataFabricTextQueryHandler,
create_datafabric_tool,
)
Expand Down Expand Up @@ -215,3 +217,62 @@ async def test_datafabric_handler_prefers_terminal_ai_message():
result = await handler("find missing row")

assert result == "I could not find any matching rows."


def _sdk_with_both_resolvers() -> MagicMock:
"""A UiPath SDK mock exposing both the current and V3 resolution methods."""
resolution = SimpleNamespace(entities=[MagicMock()], entities_service=MagicMock())
sdk = MagicMock()
sdk.entities.resolve_entity_set_async = AsyncMock(return_value=resolution)
sdk.entities.resolve_entity_set_v3_async = AsyncMock(return_value=resolution)
return sdk


@pytest.mark.asyncio
async def test_entity_resolution_uses_v1_when_v3_flag_disabled():
"""Default (flag off): resolve via the current method, never the V3 one."""
entity = _entity()
sdk = _sdk_with_both_resolvers()
handler = DataFabricTextQueryHandler(entity_set=[entity], llm=MagicMock())

# Set the flag off explicitly (programmatic value beats any
# UIPATH_FEATURE_EnableEntityV3API env var) so the assertion is deterministic.
FeatureFlags.configure_flags({ENTITY_V3_API_FF: False})
try:
with (
patch("uipath.platform.UiPath", return_value=sdk),
patch(
"uipath_langchain.agent.tools.datafabric_tool.datafabric_subgraph.DataFabricGraph.create",
return_value=_FakeCompiledGraph({"messages": []}),
),
):
await handler._ensure_datafabric_graph()
finally:
FeatureFlags.reset_flags()

sdk.entities.resolve_entity_set_async.assert_awaited_once_with([entity])
sdk.entities.resolve_entity_set_v3_async.assert_not_called()


@pytest.mark.asyncio
async def test_entity_resolution_uses_v3_when_v3_flag_enabled():
"""Flag on: resolve via the dedicated V3 method, never the current one."""
entity = _entity()
sdk = _sdk_with_both_resolvers()
handler = DataFabricTextQueryHandler(entity_set=[entity], llm=MagicMock())

FeatureFlags.configure_flags({ENTITY_V3_API_FF: True})
try:
with (
patch("uipath.platform.UiPath", return_value=sdk),
patch(
"uipath_langchain.agent.tools.datafabric_tool.datafabric_subgraph.DataFabricGraph.create",
return_value=_FakeCompiledGraph({"messages": []}),
),
):
await handler._ensure_datafabric_graph()
finally:
FeatureFlags.reset_flags()

sdk.entities.resolve_entity_set_v3_async.assert_awaited_once_with([entity])
sdk.entities.resolve_entity_set_async.assert_not_called()
20 changes: 10 additions & 10 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading