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
7 changes: 6 additions & 1 deletion src/uipath_langchain/agent/tools/integration_tool.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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")
Comment on lines +157 to +158
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be resolved from bindings overwrites?
this would be the design-time folder captured


activity_metadata = convert_to_activity_metadata(resource)

input_model = create_model(resource.input_schema)
Expand All @@ -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
Expand Down
97 changes: 97 additions & 0 deletions tests/agent/tools/test_integration_tool.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -10,6 +13,7 @@

from uipath_langchain.agent.tools.integration_tool import (
convert_to_activity_metadata,
create_integration_tool,
)


Expand Down Expand Up @@ -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