diff --git a/pyproject.toml b/pyproject.toml index efc3d55..7d1eb4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-runtime" -version = "0.10.0" +version = "0.10.1" description = "Runtime abstractions and interfaces for building agents and automation scripts in the UiPath ecosystem" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/src/uipath/runtime/chat/protocol.py b/src/uipath/runtime/chat/protocol.py index cc86957..9fcf379 100644 --- a/src/uipath/runtime/chat/protocol.py +++ b/src/uipath/runtime/chat/protocol.py @@ -5,7 +5,6 @@ from uipath.core.chat import ( UiPathConversationMessageEvent, ) -from uipath.core.triggers import UiPathResumeTrigger class UiPathChatProtocol(Protocol): @@ -32,17 +31,6 @@ async def emit_message_event( """ ... - async def emit_interrupt_event( - self, - resume_trigger: UiPathResumeTrigger, - ) -> None: - """Wrap and send an interrupt event. - - Args: - resume_trigger: UiPathResumeTrigger to wrap and send - """ - ... - async def emit_exchange_end_event(self) -> None: """Send an exchange end event.""" ... @@ -51,6 +39,6 @@ async def emit_exchange_error_event(self, error: Exception) -> None: """Emit an exchange error event.""" ... - async def wait_for_resume(self) -> dict[str, Any]: - """Wait for the interrupt_end event to be received.""" + async def wait_for_tool_confirmation(self) -> dict[str, Any]: + """Wait for a confirmToolCall event to be received.""" ... diff --git a/src/uipath/runtime/chat/runtime.py b/src/uipath/runtime/chat/runtime.py index 07753a2..2d3d68e 100644 --- a/src/uipath/runtime/chat/runtime.py +++ b/src/uipath/runtime/chat/runtime.py @@ -102,11 +102,7 @@ async def stream( resume_map: dict[str, Any] = {} for trigger in api_triggers: - await self.chat_bridge.emit_interrupt_event(trigger) - - resume_data = ( - await self.chat_bridge.wait_for_resume() - ) + resume_data = await self.chat_bridge.wait_for_tool_confirmation() assert trigger.interrupt_id is not None, ( "Trigger interrupt_id cannot be None" diff --git a/tests/test_chat.py b/tests/test_chat.py index 8110ab8..da095d5 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -33,8 +33,7 @@ def make_chat_bridge_mock() -> UiPathChatProtocol: bridge_mock.connect = AsyncMock() bridge_mock.disconnect = AsyncMock() bridge_mock.emit_message_event = AsyncMock() - bridge_mock.emit_interrupt_event = AsyncMock() - bridge_mock.wait_for_resume = AsyncMock() + bridge_mock.wait_for_tool_confirmation = AsyncMock() return cast(UiPathChatProtocol, bridge_mock) @@ -309,7 +308,7 @@ async def test_chat_runtime_handles_api_trigger_suspension(): runtime_impl = SuspendingMockRuntime(suspend_at_message=0) bridge = make_chat_bridge_mock() - cast(AsyncMock, bridge.wait_for_resume).return_value = {"approved": True} + cast(AsyncMock, bridge.wait_for_tool_confirmation).return_value = {"approved": True} chat_runtime = UiPathChatRuntime( delegate=runtime_impl, @@ -331,8 +330,7 @@ async def test_chat_runtime_handles_api_trigger_suspension(): cast(AsyncMock, bridge.connect).assert_awaited_once() cast(AsyncMock, bridge.disconnect).assert_awaited_once() - cast(AsyncMock, bridge.emit_interrupt_event).assert_awaited_once() - cast(AsyncMock, bridge.wait_for_resume).assert_awaited_once() + cast(AsyncMock, bridge.wait_for_tool_confirmation).assert_awaited_once() # Message events emitted (one before suspend, one after resume) assert cast(AsyncMock, bridge.emit_message_event).await_count == 2 @@ -345,8 +343,8 @@ async def test_chat_runtime_yields_events_during_suspension_flow(): runtime_impl = SuspendingMockRuntime(suspend_at_message=0) bridge = make_chat_bridge_mock() - # wait_for_resume returns approval data - cast(AsyncMock, bridge.wait_for_resume).return_value = {"approved": True} + # wait_for_tool_confirmation returns approval data + cast(AsyncMock, bridge.wait_for_tool_confirmation).return_value = {"approved": True} chat_runtime = UiPathChatRuntime( delegate=runtime_impl, @@ -533,7 +531,7 @@ async def test_chat_runtime_handles_multiple_api_triggers(): bridge = make_chat_bridge_mock() # Bridge returns approval for each trigger - cast(AsyncMock, bridge.wait_for_resume).side_effect = [ + cast(AsyncMock, bridge.wait_for_tool_confirmation).side_effect = [ {"approved": True}, # email-confirm {"approved": True}, # file-delete {"approved": True}, # api-call @@ -563,14 +561,7 @@ async def test_chat_runtime_handles_multiple_api_triggers(): assert resume_input["api-call"] == {"approved": True} # Bridge should have been called 3 times (once per trigger) - assert cast(AsyncMock, bridge.emit_interrupt_event).await_count == 3 - assert cast(AsyncMock, bridge.wait_for_resume).await_count == 3 - - # Verify each emit_interrupt_event received a trigger - emit_calls = cast(AsyncMock, bridge.emit_interrupt_event).await_args_list - assert emit_calls[0][0][0].interrupt_id == "email-confirm" - assert emit_calls[1][0][0].interrupt_id == "file-delete" - assert emit_calls[2][0][0].interrupt_id == "api-call" + assert cast(AsyncMock, bridge.wait_for_tool_confirmation).await_count == 3 @pytest.mark.asyncio @@ -581,7 +572,7 @@ async def test_chat_runtime_filters_non_api_triggers(): bridge = make_chat_bridge_mock() # Bridge returns approval for API triggers only - cast(AsyncMock, bridge.wait_for_resume).side_effect = [ + cast(AsyncMock, bridge.wait_for_tool_confirmation).side_effect = [ {"approved": True}, # email-confirm {"approved": True}, # file-delete ] @@ -603,12 +594,4 @@ async def test_chat_runtime_filters_non_api_triggers(): assert result.triggers[0].trigger_type == UiPathResumeTriggerType.QUEUE_ITEM # Bridge should have been called only 2 times (for 2 API triggers) - assert cast(AsyncMock, bridge.emit_interrupt_event).await_count == 2 - assert cast(AsyncMock, bridge.wait_for_resume).await_count == 2 - - # Verify only API triggers were emitted - emit_calls = cast(AsyncMock, bridge.emit_interrupt_event).await_args_list - assert emit_calls[0][0][0].interrupt_id == "email-confirm" - assert emit_calls[0][0][0].trigger_type == UiPathResumeTriggerType.API - assert emit_calls[1][0][0].interrupt_id == "file-delete" - assert emit_calls[1][0][0].trigger_type == UiPathResumeTriggerType.API + assert cast(AsyncMock, bridge.wait_for_tool_confirmation).await_count == 2 diff --git a/uv.lock b/uv.lock index f514162..dea324b 100644 --- a/uv.lock +++ b/uv.lock @@ -991,21 +991,21 @@ wheels = [ [[package]] name = "uipath-core" -version = "0.5.2" +version = "0.5.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/d3/7b3cd984ca5530892a2dac945408d50f64f3f21c1fa6c5a68d9625d685f0/uipath_core-0.5.2.tar.gz", hash = "sha256:a7fd2d5c6d49117bea060c162cd380ae59fe5f2ed74bb0e4e508d51cd57e9de1", size = 119081, upload-time = "2026-02-23T10:16:48.567Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/f2/9b6d5eb0a4e5b2a4a80c777bc4b7a22efb8e343ef48766ed3ab73d6b11eb/uipath_core-0.5.11.tar.gz", hash = "sha256:9ed987360e7439f53b07e4d10d2381cacc80443f43f3fcf7f721d46ac3320c95", size = 117024, upload-time = "2026-04-06T14:31:38.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/24/3b65d78f8028b4bf49f76ed9fc08257c0941bf64fc597caf69e2c0605584/uipath_core-0.5.2-py3-none-any.whl", hash = "sha256:7a675074e2c6b2ec00995051d1c9850f6f70543cce3161b6a6b8c2911dc16ee0", size = 42844, upload-time = "2026-02-23T10:16:47.271Z" }, + { url = "https://files.pythonhosted.org/packages/68/bd/f28d89dcaec4ea040efb17ad9f8229675f293cff5f0a310351003d7f92b7/uipath_core-0.5.11-py3-none-any.whl", hash = "sha256:8c107dd9597d20c4ca7d8e770e703d36b0e21a8a1d80e13d585f125eb64d54bf", size = 43283, upload-time = "2026-04-06T14:31:36.908Z" }, ] [[package]] name = "uipath-runtime" -version = "0.10.0" +version = "0.10.1" source = { editable = "." } dependencies = [ { name = "uipath-core" },