From 006c22f670bb53a39d1b1e656ef8b876ca325523 Mon Sep 17 00:00:00 2001 From: Maxwell Du <60411452+maxduu@users.noreply.github.com> Date: Fri, 24 Jul 2026 16:43:58 -0700 Subject: [PATCH] fix: remove run-as-me propagation --- pyproject.toml | 2 +- .../agent/tools/process_tool.py | 6 +- .../agent/tools/tool_factory.py | 29 +----- tests/agent/tools/test_process_tool.py | 97 ------------------- tests/agent/tools/test_tool_factory.py | 6 +- uv.lock | 2 +- 6 files changed, 7 insertions(+), 135 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ac43b3b36..dc93a7151 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-langchain" -version = "0.14.16" +version = "0.14.17" 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" diff --git a/src/uipath_langchain/agent/tools/process_tool.py b/src/uipath_langchain/agent/tools/process_tool.py index 52b158ad2..e91f77151 100644 --- a/src/uipath_langchain/agent/tools/process_tool.py +++ b/src/uipath_langchain/agent/tools/process_tool.py @@ -42,10 +42,7 @@ } -def create_process_tool( - resource: AgentProcessToolResourceConfig, - run_as_me: bool = False, -) -> StructuredTool: +def create_process_tool(resource: AgentProcessToolResourceConfig) -> StructuredTool: """Uses interrupt() to suspend graph execution until process completes (handled by runtime).""" # Import here to avoid circular dependency from uipath_langchain.agent.wrappers import get_job_attachment_wrapper @@ -86,7 +83,6 @@ async def start_job(): attachments=attachments, parent_span_id=parent_span_id, parent_operation_id=parent_operation_id, - run_as_me=True if run_as_me else None, ) except EnrichedException as e: raise_for_enriched( diff --git a/src/uipath_langchain/agent/tools/tool_factory.py b/src/uipath_langchain/agent/tools/tool_factory.py index f6a7fb4b7..1e49a03ac 100644 --- a/src/uipath_langchain/agent/tools/tool_factory.py +++ b/src/uipath_langchain/agent/tools/tool_factory.py @@ -31,33 +31,11 @@ logger = getLogger(__name__) -def _is_user_token() -> bool: - """Check if the current token is a user token (sub_type == 'user').""" - try: - from uipath._cli._utils._common import get_claim_from_token - - sub_type = get_claim_from_token("sub_type") - logger.info("Token sub_type=%r", sub_type) - return sub_type == "user" - except Exception as e: - logger.info("Token sub_type check failed: %s", e) - return False - - async def create_tools_from_resources( agent: LowCodeAgentDefinition, llm: BaseChatModel ) -> list[BaseTool]: tools: list[BaseTool] = [] - is_user = _is_user_token() - run_as_me = agent.is_conversational and is_user - logger.info( - "RunAsMe decision: is_conversational=%s, is_user_token=%s, run_as_me=%s", - agent.is_conversational, - is_user, - run_as_me, - ) - logger.info("Creating tools for agent '%s' from resources", agent.name) for resource in agent.resources: @@ -74,9 +52,7 @@ async def create_tools_from_resources( resource.name, type(resource).__name__, ) - tool = await _build_tool_for_resource( - resource, llm, agent=agent, run_as_me=run_as_me - ) + tool = await _build_tool_for_resource(resource, llm, agent=agent) if tool is not None: if isinstance(tool, list): tools.extend(tool) @@ -99,10 +75,9 @@ async def _build_tool_for_resource( resource: BaseAgentResourceConfig, llm: BaseChatModel, agent: LowCodeAgentDefinition | None = None, - run_as_me: bool = False, ) -> BaseTool | list[BaseTool] | None: if isinstance(resource, AgentProcessToolResourceConfig): - return create_process_tool(resource, run_as_me=run_as_me) + return create_process_tool(resource) elif isinstance(resource, AgentContextResourceConfig): return create_context_tool(resource, llm=llm, agent=agent) diff --git a/tests/agent/tools/test_process_tool.py b/tests/agent/tools/test_process_tool.py index 4da3d92c2..bcc07344f 100644 --- a/tests/agent/tools/test_process_tool.py +++ b/tests/agent/tools/test_process_tool.py @@ -179,7 +179,6 @@ async def test_invoke_calls_processes_invoke_async( attachments=[], parent_span_id=None, parent_operation_id=None, - run_as_me=None, ) @pytest.mark.asyncio @@ -393,100 +392,6 @@ async def test_span_context_defaults_to_none_when_empty( assert call_kwargs["parent_span_id"] is None -class TestProcessToolRunAsMe: - """Test RunAsMe propagation passed top-down from tool factory.""" - - @pytest.mark.asyncio - @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") - @patch("uipath_langchain.agent.tools.process_tool.UiPath") - async def test_run_as_me_true_passed_to_invoke( - self, - mock_uipath_class, - mock_interrupt, - process_resource, - ): - """Test RunAsMe=True is forwarded to invoke_async when set.""" - mock_job = MagicMock(spec=Job) - mock_job.key = "job-key" - mock_job.folder_key = "folder-key" - - mock_resumed_job = MagicMock(spec=Job) - mock_resumed_job.state = "successful" - - mock_client = MagicMock() - mock_client.processes.invoke_async = AsyncMock(return_value=mock_job) - mock_client.jobs.extract_output_async = AsyncMock(return_value=None) - mock_uipath_class.return_value = mock_client - - mock_interrupt.return_value = mock_resumed_job - - tool = create_process_tool(process_resource, run_as_me=True) - await tool.ainvoke({}) - - call_kwargs = mock_client.processes.invoke_async.call_args[1] - assert call_kwargs["run_as_me"] is True - - @pytest.mark.asyncio - @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") - @patch("uipath_langchain.agent.tools.process_tool.UiPath") - async def test_run_as_me_false_sends_none( - self, - mock_uipath_class, - mock_interrupt, - process_resource, - ): - """Test RunAsMe=None when run_as_me=False (default).""" - mock_job = MagicMock(spec=Job) - mock_job.key = "job-key" - mock_job.folder_key = "folder-key" - - mock_resumed_job = MagicMock(spec=Job) - mock_resumed_job.state = "successful" - - mock_client = MagicMock() - mock_client.processes.invoke_async = AsyncMock(return_value=mock_job) - mock_client.jobs.extract_output_async = AsyncMock(return_value=None) - mock_uipath_class.return_value = mock_client - - mock_interrupt.return_value = mock_resumed_job - - tool = create_process_tool(process_resource, run_as_me=False) - await tool.ainvoke({}) - - call_kwargs = mock_client.processes.invoke_async.call_args[1] - assert call_kwargs["run_as_me"] is None - - @pytest.mark.asyncio - @patch("uipath_langchain._utils.durable_interrupt.decorator.interrupt") - @patch("uipath_langchain.agent.tools.process_tool.UiPath") - async def test_run_as_me_default_sends_none( - self, - mock_uipath_class, - mock_interrupt, - process_resource, - ): - """Test RunAsMe=None when run_as_me not specified (default).""" - mock_job = MagicMock(spec=Job) - mock_job.key = "job-key" - mock_job.folder_key = "folder-key" - - mock_resumed_job = MagicMock(spec=Job) - mock_resumed_job.state = "successful" - - mock_client = MagicMock() - mock_client.processes.invoke_async = AsyncMock(return_value=mock_job) - mock_client.jobs.extract_output_async = AsyncMock(return_value=None) - mock_uipath_class.return_value = mock_client - - mock_interrupt.return_value = mock_resumed_job - - tool = create_process_tool(process_resource) - await tool.ainvoke({}) - - call_kwargs = mock_client.processes.invoke_async.call_args[1] - assert call_kwargs["run_as_me"] is None - - class TestProcessToolFlowType: """Test that a Flow-type resource flows through create_process_tool correctly.""" @@ -534,7 +439,6 @@ async def test_flow_tool_invokes_processes_invoke_async( attachments=[], parent_span_id=None, parent_operation_id=None, - run_as_me=None, ) @pytest.mark.asyncio @@ -615,7 +519,6 @@ async def test_function_tool_invokes_processes_invoke_async( attachments=[], parent_span_id=None, parent_operation_id=None, - run_as_me=None, ) @pytest.mark.asyncio diff --git a/tests/agent/tools/test_tool_factory.py b/tests/agent/tools/test_tool_factory.py index 333f731d8..c44c2e068 100644 --- a/tests/agent/tools/test_tool_factory.py +++ b/tests/agent/tools/test_tool_factory.py @@ -432,7 +432,7 @@ async def test_flow_resource_routes_through_process_tool_path( ) tool = await _build_tool_for_resource(flow_resource, mock_llm) - mock_create_process_tool.assert_called_once_with(flow_resource, run_as_me=False) + mock_create_process_tool.assert_called_once_with(flow_resource) assert tool is not None async def test_quick_form_resource_routes_through_escalation_tool_path( @@ -466,9 +466,7 @@ async def test_function_resource_routes_through_process_tool_path( ) tool = await _build_tool_for_resource(function_resource, mock_llm) - mock_create_process_tool.assert_called_once_with( - function_resource, run_as_me=False - ) + mock_create_process_tool.assert_called_once_with(function_resource) assert tool is not None @pytest.mark.asyncio diff --git a/uv.lock b/uv.lock index a90039156..eedd4ee6f 100644 --- a/uv.lock +++ b/uv.lock @@ -4498,7 +4498,7 @@ wheels = [ [[package]] name = "uipath-langchain" -version = "0.14.16" +version = "0.14.17" source = { editable = "." } dependencies = [ { name = "a2a-sdk" },