From 5abd85e7a6cf803d043ee1b280f0746137523391 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Sun, 28 Jun 2026 00:43:05 +0300 Subject: [PATCH] feat: support composite interrupt resume triggers --- .github/labeler.yml | 2 + packages/uipath-platform/pyproject.toml | 2 +- .../platform/resume_triggers/_protocol.py | 21 ++++++++++ .../tests/services/test_hitl.py | 39 +++++++++++++++++++ packages/uipath-platform/uv.lock | 2 +- packages/uipath/pyproject.toml | 4 +- packages/uipath/uv.lock | 4 +- 7 files changed, 68 insertions(+), 6 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index d83420502..32d610bd5 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -17,3 +17,5 @@ test:uipath-integrations: test:uipath-runtime: - changed-files: - any-glob-to-any-file: ['packages/uipath-core/src/**/*.py'] + - changed-files: + - any-glob-to-any-file: ['packages/uipath/pyproject.toml'] diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index d1601847e..7a4d93b11 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.1.91" +version = "0.2.0" description = "HTTP client library for programmatic access to UiPath Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py b/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py index e27f18025..784e9f2ae 100644 --- a/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py +++ b/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py @@ -453,6 +453,23 @@ class UiPathResumeTriggerCreator: Implements UiPathResumeTriggerCreatorProtocol. """ + async def create_triggers(self, suspend_value: Any) -> list[UiPathResumeTrigger]: + """Create resume triggers from a suspend value. + + Most values create a single trigger. A list or tuple creates sibling + triggers for the same interrupt; whichever one fires first resumes it. + """ + if isinstance(suspend_value, (list, tuple)): + if not suspend_value: + raise ValueError("At least one interrupt model is required.") + return [ + await self.create_trigger(child_suspend_value) + for child_suspend_value in suspend_value + ] + + resume_trigger = await self.create_trigger(suspend_value) + return [resume_trigger] + async def create_trigger(self, suspend_value: Any) -> UiPathResumeTrigger: """Create a resume trigger from a suspend value. @@ -1040,6 +1057,10 @@ async def create_trigger(self, suspend_value: Any) -> UiPathResumeTrigger: """ return await self._creator.create_trigger(suspend_value) + async def create_triggers(self, suspend_value: Any) -> list[UiPathResumeTrigger]: + """Create resume triggers from a suspend value.""" + return await self._creator.create_triggers(suspend_value) + async def read_trigger(self, trigger: UiPathResumeTrigger) -> Any | None: """Read a resume trigger and convert it to runtime-compatible input. diff --git a/packages/uipath-platform/tests/services/test_hitl.py b/packages/uipath-platform/tests/services/test_hitl.py index 5151d7a7a..134d75fc0 100644 --- a/packages/uipath-platform/tests/services/test_hitl.py +++ b/packages/uipath-platform/tests/services/test_hitl.py @@ -2109,6 +2109,45 @@ def test_wait_until_requires_timezone_aware_resume_time(self) -> None: with pytest.raises(ValueError, match="resume_time must include timezone"): WaitUntil(resume_time=datetime(2026, 6, 27, 20, 14, 49)) + @pytest.mark.anyio + async def test_create_resume_triggers_for_interrupt_list( + self, + ) -> None: + """Test an interrupt list creates sibling triggers for the same interrupt.""" + job_key = "test-job-key" + wait_job = WaitJob( + job=Job( + id=1234, + key=job_key, + folder_key="d0e09040-5997-44e1-93b7-4087689521b7", + ), + process_folder_path="/test/path", + ) + wait_until = WaitUntil( + resume_time=datetime(2026, 6, 27, 23, 14, 49, tzinfo=timezone.utc) + ) + + processor = UiPathResumeTriggerCreator() + triggers = await processor.create_triggers([wait_job, wait_until]) + + assert len(triggers) == 2 + job_trigger, timer_trigger = triggers + assert job_trigger.trigger_type == UiPathResumeTriggerType.JOB + assert job_trigger.item_key == job_key + assert timer_trigger.trigger_type == UiPathResumeTriggerType.TIMER + assert timer_trigger.trigger_name == UiPathResumeTriggerName.TIMER + assert timer_trigger.resume_time == datetime( + 2026, 6, 27, 23, 14, 49, tzinfo=timezone.utc + ) + + @pytest.mark.anyio + async def test_create_resume_triggers_rejects_empty_interrupt_list(self) -> None: + """Test an interrupt list must include at least one model.""" + processor = UiPathResumeTriggerCreator() + + with pytest.raises(ValueError, match="At least one interrupt model"): + await processor.create_triggers([]) + class TestDocumentExtractionModels: """Tests for document extraction models.""" diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index c369fe90e..dceb4dbda 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1095,7 +1095,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.91" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index a0a6d9e43..f6d962ab2 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "uipath" -version = "2.12.8" +version = "2.13.0" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ "uipath-core>=0.5.26, <0.6.0", "uipath-runtime>=0.12.1, <0.13.0", - "uipath-platform>=0.1.91, <0.2.0", + "uipath-platform>=0.2.0, <0.3.0", "click>=8.3.1", "httpx>=0.28.1", "pyjwt>=2.10.1", diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 50d89c69e..e56bdafcf 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2598,7 +2598,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.12.8" +version = "2.13.0" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2741,7 +2741,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.91" +version = "0.2.0" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },