diff --git a/src/uipath_langchain/agent/tools/integration_tool.py b/src/uipath_langchain/agent/tools/integration_tool.py index eac7523e..9c6e55a8 100644 --- a/src/uipath_langchain/agent/tools/integration_tool.py +++ b/src/uipath_langchain/agent/tools/integration_tool.py @@ -1,7 +1,7 @@ """Process tool creation for UiPath process execution.""" import copy -from typing import Any +from typing import Any, Optional from langchain.tools import BaseTool from langchain_core.messages import ToolCall @@ -153,6 +153,10 @@ def create_integration_tool( ) connection_id: str = resource.properties.connection.id + folder_key: Optional[str] = None + if resource.properties.connection.folder is not None: + folder_key = resource.properties.connection.folder.get("key") + activity_metadata = convert_to_activity_metadata(resource) input_model = create_model(resource.input_schema) @@ -178,6 +182,7 @@ async def integration_tool_fn(**kwargs: Any): activity_metadata=activity_metadata, connection_id=connection_id, activity_input=sanitize_dict_for_serialization(kwargs), + folder_key=folder_key, ) except Exception: raise diff --git a/tests/agent/tools/test_integration_tool.py b/tests/agent/tools/test_integration_tool.py index 04e33de0..964f18ce 100644 --- a/tests/agent/tools/test_integration_tool.py +++ b/tests/agent/tools/test_integration_tool.py @@ -1,5 +1,8 @@ """Tests for integration_tool.py module.""" +from typing import Any +from unittest.mock import AsyncMock, patch + import pytest from uipath.agent.models.agent import ( AgentIntegrationToolParameter, @@ -10,6 +13,7 @@ from uipath_langchain.agent.tools.integration_tool import ( convert_to_activity_metadata, + create_integration_tool, ) @@ -265,3 +269,96 @@ def test_parameter_location_mapping_simple_fields(self, resource_factory): assert "user" in result.parameter_location_info.body_fields assert len(result.parameter_location_info.body_fields) == 1 + + +class TestCreateIntegrationToolFolderKey: + """Test that create_integration_tool correctly extracts and passes folder_key.""" + + def _make_resource( + self, folder: dict[str, Any] | None = None + ) -> AgentIntegrationToolResourceConfig: + """Helper to create a minimal integration tool resource.""" + connection = Connection( + id="test-connection-id", + name="Test Connection", + element_instance_id=12345, + folder=folder, + ) + properties = AgentIntegrationToolProperties( + method="POST", + tool_path="/v2/webSearch", + object_name="v2::webSearch", + tool_display_name="Web Search", + tool_description="Search the web", + connection=connection, + parameters=[ + AgentIntegrationToolParameter( + name="query", type="string", field_location="body" + ), + ], + ) + return AgentIntegrationToolResourceConfig( + name="Web Search", + description="Search the web", + properties=properties, + input_schema={ + "type": "object", + "properties": {"query": {"type": "string"}}, + "required": ["query"], + }, + ) + + @pytest.mark.asyncio + async def test_folder_key_passed_to_invoke_activity(self) -> None: + """Test that folder_key from connection.folder is passed to invoke_activity_async.""" + folder_key = "d6f5c54a-e2b2-4083-be93-623aa670ed40" + resource = self._make_resource(folder={"key": folder_key}) + + mock_invoke = AsyncMock(return_value={"results": []}) + + with patch( + "uipath_langchain.agent.tools.integration_tool.UiPath" + ) as mock_sdk_cls: + mock_sdk_cls.return_value.connections.invoke_activity_async = mock_invoke + tool = create_integration_tool(resource) + await tool.ainvoke({"query": "test search"}) + + mock_invoke.assert_called_once() + call_kwargs = mock_invoke.call_args.kwargs + assert call_kwargs["folder_key"] == folder_key + + @pytest.mark.asyncio + async def test_folder_key_none_when_no_folder(self) -> None: + """Test that folder_key is None when connection has no folder.""" + resource = self._make_resource(folder=None) + + mock_invoke = AsyncMock(return_value={"results": []}) + + with patch( + "uipath_langchain.agent.tools.integration_tool.UiPath" + ) as mock_sdk_cls: + mock_sdk_cls.return_value.connections.invoke_activity_async = mock_invoke + tool = create_integration_tool(resource) + await tool.ainvoke({"query": "test search"}) + + mock_invoke.assert_called_once() + call_kwargs = mock_invoke.call_args.kwargs + assert call_kwargs["folder_key"] is None + + @pytest.mark.asyncio + async def test_folder_key_none_when_folder_has_no_key(self) -> None: + """Test that folder_key is None when folder dict has no 'key' field.""" + resource = self._make_resource(folder={"path": "some/path"}) + + mock_invoke = AsyncMock(return_value={"results": []}) + + with patch( + "uipath_langchain.agent.tools.integration_tool.UiPath" + ) as mock_sdk_cls: + mock_sdk_cls.return_value.connections.invoke_activity_async = mock_invoke + tool = create_integration_tool(resource) + await tool.ainvoke({"query": "test search"}) + + mock_invoke.assert_called_once() + call_kwargs = mock_invoke.call_args.kwargs + assert call_kwargs["folder_key"] is None