From ce83277d0b4baf9e41da72ce42a66d64c8df84b7 Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Fri, 3 Jul 2026 13:46:46 +0300 Subject: [PATCH 1/6] ci(platform): enforce uipath.platform layering with import-linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an import-linter layers contract over uipath.platform, enforced as a CI step in the lint-uipath-platform job. The contract statically forbids import cycles and upward imports between domain packages — the class of bug behind the 0.1.89 identity/common incident (#1787). Type-checking imports are excluded; the contract is exhaustive so new subpackages must be assigned a layer explicitly. The contract is intentionally committed without ignores: lint-imports fails at this commit on the pre-existing upward imports from common/interrupt_models.py, fixed in the next commit. Co-Authored-By: Claude Fable 5 --- .github/workflows/lint-packages.yml | 5 + .gitignore | 1 + packages/uipath-platform/CLAUDE.md | 1 + packages/uipath-platform/pyproject.toml | 20 ++++ packages/uipath-platform/uv.lock | 122 ++++++++++++++++++++++++ 5 files changed, 149 insertions(+) diff --git a/.github/workflows/lint-packages.yml b/.github/workflows/lint-packages.yml index d530e25a9..94367f552 100644 --- a/.github/workflows/lint-packages.yml +++ b/.github/workflows/lint-packages.yml @@ -139,6 +139,11 @@ jobs: working-directory: packages/uipath-platform run: uv run ruff format --check . + - name: Check import contracts + if: steps.check.outputs.skip != 'true' + working-directory: packages/uipath-platform + run: uv run lint-imports + lint-uipath: name: Lint uipath needs: detect-changed-packages diff --git a/.gitignore b/.gitignore index 6b24bbd06..fd06e77db 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ wheels/ .cache/* .cache +.import_linter_cache/ site diff --git a/packages/uipath-platform/CLAUDE.md b/packages/uipath-platform/CLAUDE.md index 21896676f..a13260c41 100644 --- a/packages/uipath-platform/CLAUDE.md +++ b/packages/uipath-platform/CLAUDE.md @@ -17,6 +17,7 @@ pytest tests/services/test_assets_service.py # Single test file ruff check . # Lint ruff format --check . # Format check mypy src tests # Type check +uv run lint-imports # Import-graph layering contract ``` No justfile exists for this package — run commands directly. diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index d1601847e..c56ea764f 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -47,6 +47,7 @@ dev = [ "pytest-cov>=4.1.0", "pytest-mock>=3.11.1", "pre-commit>=4.1.0", + "import-linter>=2.0", ] [tool.hatch.build.targets.wheel] @@ -126,6 +127,25 @@ exclude_lines = [ "@(abc\\.)?abstractmethod", ] +[tool.importlinter] +root_package = "uipath.platform" +exclude_type_checking_imports = true + +[[tool.importlinter.contracts]] +id = "platform-layers" +name = "uipath.platform layered architecture" +type = "layers" +containers = ["uipath.platform"] +exhaustive = true +layers = [ + "_uipath : guardrails : resume_triggers", + "agenthub : automation_ops : automation_tracker : connections : context_grounding : entities : external_applications : governance : memory : pii_detection : portal : resource_catalog : semantic_proxy", + "orchestrator", + "action_center : attachments : chat : documents : identity", + "common", + "constants : errors", +] + [tool.uv] exclude-newer = "2 days" diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index c369fe90e..2ae2b7efd 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -99,6 +99,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -230,6 +242,99 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331, upload-time = "2026-02-19T00:48:18.465Z" }, ] +[[package]] +name = "grimp" +version = "3.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/46/79764cfb61a3ac80dadae5d94fb10acdb7800e31fecf4113cf3d345e4952/grimp-3.14.tar.gz", hash = "sha256:645fbd835983901042dae4e1b24fde3a89bf7ac152f9272dd17a97e55cb4f871", size = 830882, upload-time = "2025-12-10T17:55:01.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/31/d4a86207c38954b6c3d859a1fc740a80b04bbe6e3b8a39f4e66f9633dfa4/grimp-3.14-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f1c91e3fa48c2196bf62e3c71492140d227b2bfcd6d15e735cbc0b3e2d5308e0", size = 2185572, upload-time = "2025-12-10T17:53:41.287Z" }, + { url = "https://files.pythonhosted.org/packages/f5/61/ed4cba5bd75d37fe46e17a602f616619a9e4f74ad8adfcf560ce4b2a1697/grimp-3.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6291c8f1690a9fe21b70923c60b075f4a89676541999e3d33084cbc69ac06a1", size = 2118002, upload-time = "2025-12-10T17:53:18.546Z" }, + { url = "https://files.pythonhosted.org/packages/77/6a/688f6144d0b207d7845bd8ab403820a83630ce3c9420cbbc7c9e9282f9c0/grimp-3.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ec312383935c2d09e4085c8435780ada2e13ebef14e105609c2988a02a5b2ce", size = 2283939, upload-time = "2025-12-10T17:52:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/a5/98/4c540de151bf3fd58d6d7b3fe2269b6a6af6c61c915de1bc991802bfaff8/grimp-3.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f43cbf640e73ee703ad91639591046828d20103a1c363a02516e77a66a4ac07", size = 2233693, upload-time = "2025-12-10T17:52:18.938Z" }, + { url = "https://files.pythonhosted.org/packages/3e/7b/84b4b52b6c6dd5bf083cb1a72945748f56ea2e61768bbebf87e8d9d0ef75/grimp-3.14-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a93c9fddccb9ff16f5c6b5fca44227f5f86cba7cffc145d2176119603d2d7c7", size = 2389745, upload-time = "2025-12-10T17:53:00.659Z" }, + { url = "https://files.pythonhosted.org/packages/a7/33/31b96907c7dd78953df5e1ce67c558bd6057220fa1203d28d52566315a2e/grimp-3.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5653a2769fdc062cb7598d12200352069c9c6559b6643af6ada3639edb98fcc3", size = 2569055, upload-time = "2025-12-10T17:52:33.556Z" }, + { url = "https://files.pythonhosted.org/packages/b2/24/ce1a8110f3d5b178153b903aafe54b6a9216588b5bff3656e30af43e9c29/grimp-3.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:071c7ddf5e5bb7b2fdf79aefdf6e1c237cd81c095d6d0a19620e777e85bf103c", size = 2358044, upload-time = "2025-12-10T17:52:47.545Z" }, + { url = "https://files.pythonhosted.org/packages/05/7f/16d98c02287bc99884843478b9a68b04a2ef13b5cb8b9f36a9ca7daea75b/grimp-3.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e01b7a4419f535b667dfdcb556d3815b52981474f791fb40d72607228389a31", size = 2310304, upload-time = "2025-12-10T17:53:09.679Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8c/0fde9781b0f6b4f9227d485685f48f6bcc70b95af22e2f85ff7f416cbfc1/grimp-3.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c29682f336151d1d018d0c3aa9eeaa35734b970e4593fa396b901edca7ef5c79", size = 2463682, upload-time = "2025-12-10T17:53:49.185Z" }, + { url = "https://files.pythonhosted.org/packages/51/cb/2baff301c2c2cc2792b6e225ea0784793ca587c81b97572be0bad122cfc8/grimp-3.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a5c4fd71f363ea39e8aab0630010ced77a8de9789f27c0acdd0d7e6269d4a8ef", size = 2500573, upload-time = "2025-12-10T17:54:03.899Z" }, + { url = "https://files.pythonhosted.org/packages/96/69/797e4242f42d6665da5fe22cb250cae3f14ece4cb22ad153e9cd97158179/grimp-3.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766911e3ba0b13d833fdd03ad1f217523a8a2b2527b5507335f71dca1153183d", size = 2503005, upload-time = "2025-12-10T17:54:32.993Z" }, + { url = "https://files.pythonhosted.org/packages/fd/45/da1a27a6377807ca427cd56534231f0920e1895e16630204f382a0df14c5/grimp-3.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:154e84a2053e9f858ae48743de23a5ad4eb994007518c29371276f59b8419036", size = 2515776, upload-time = "2025-12-10T17:54:47.962Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8d/b918a29ce98029cd7a9e33a584be43a93288d5283fb7ccef5b6b2ba39ede/grimp-3.14-cp311-cp311-win32.whl", hash = "sha256:3189c86c3e73016a1907ee3ba9f7a6ca037e3601ad09e60ce9bf12b88877f812", size = 1873189, upload-time = "2025-12-10T17:55:11.872Z" }, + { url = "https://files.pythonhosted.org/packages/90/d7/2327c203f83a25766fbd62b0df3b24230d422b6e53518ff4d1c5e69793f1/grimp-3.14-cp311-cp311-win_amd64.whl", hash = "sha256:201f46a6a4e5ee9dfba4a2f7d043f7deab080d1d84233f4a1aee812678c25307", size = 2014277, upload-time = "2025-12-10T17:55:04.144Z" }, + { url = "https://files.pythonhosted.org/packages/75/d6/a35ff62f35aa5fd148053506eddd7a8f2f6afaed31870dc608dd0eb38e4f/grimp-3.14-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ffabc6940301214753bad89ec0bfe275892fa1f64b999e9a101f6cebfc777133", size = 2178573, upload-time = "2025-12-10T17:53:42.836Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/bd2e80273da4d46110969fc62252e5372e0249feb872bc7fe76fdc7f1818/grimp-3.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:075d9a1c78d607792d0ed8d4d3d7754a621ef04c8a95eaebf634930dc9232bb2", size = 2110452, upload-time = "2025-12-10T17:53:19.831Z" }, + { url = "https://files.pythonhosted.org/packages/44/c3/7307249c657d34dca9d250d73ba027d6cfe15a98fb3119b6e5210bc388b7/grimp-3.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06ff52addeb20955a4d6aa097bee910573ffc9ef0d3c8a860844f267ad958156", size = 2283064, upload-time = "2025-12-10T17:52:07.673Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d2/cae4cf32dc8d4188837cc4ab183300d655f898969b0f169e240f3b7c25be/grimp-3.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d10e0663e961fcbe8d0f54608854af31f911f164c96a44112d5173050132701f", size = 2235893, upload-time = "2025-12-10T17:52:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/3f58bc3064fc305dac107d08003ba65713a5bc89a6d327f1c06b30cce752/grimp-3.14-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ab874d7ddddc7a1291259cf7c31a4e7b5c612e9da2e24c67c0eb1a44a624e67", size = 2393376, upload-time = "2025-12-10T17:53:02.397Z" }, + { url = "https://files.pythonhosted.org/packages/06/b8/f476f30edf114f04cb58e8ae162cb4daf52bda0ab01919f3b5b7edb98430/grimp-3.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54fec672ec83355636a852177f5a470c964bede0f6730f9ba3c7b5c8419c9eab", size = 2571342, upload-time = "2025-12-10T17:52:35.214Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ae/2e44d3c4f591f95f86322a8f4dbb5aac17001d49e079f3a80e07e7caaf09/grimp-3.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9e221b5e8070a916c780e88c877fee2a61c95a76a76a2a076396e459511b0bb", size = 2359022, upload-time = "2025-12-10T17:52:49.063Z" }, + { url = "https://files.pythonhosted.org/packages/69/ac/42b4d6bc0ea119ce2e91e1788feabf32c5433e9617dbb495c2a3d0dc7f12/grimp-3.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea6b495f9b4a8d82f5ce544921e76d0d12017f5d1ac3a3bd2f5ac88ab055b1c", size = 2309424, upload-time = "2025-12-10T17:53:11.069Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c7/6a731989625c1790f4da7602dcbf9d6525512264e853cda77b3b3602d5e0/grimp-3.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:655e8d3f79cd99bb859e09c9dd633515150e9d850879ca71417d5ac31809b745", size = 2462754, upload-time = "2025-12-10T17:53:50.886Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4d/3d1571c0a39a59dd68be4835f766da64fe64cbab0d69426210b716a8bdf0/grimp-3.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a14f10b1b71c6c37647a76e6a49c226509648107abc0f48c1e3ecd158ba05531", size = 2501356, upload-time = "2025-12-10T17:54:06.014Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d1/8950b8229095ebda5c54c8784e4d1f0a6e19423f2847289ef9751f878798/grimp-3.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:81685111ee24d3e25f8ed9e77ed00b92b58b2414e1a1c2937236026900972744", size = 2504631, upload-time = "2025-12-10T17:54:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/23bed3da9206138d36d01890b656c7fb7adfb3a37daac8842d84d8777ade/grimp-3.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce8352a8ea0e27b143136ea086582fc6653419aa8a7c15e28ed08c898c42b185", size = 2514751, upload-time = "2025-12-10T17:54:49.384Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/6f1f55c97ee982f133ec5ccb22fc99bf5335aee70c208f4fb86cd833b8d5/grimp-3.14-cp312-cp312-win32.whl", hash = "sha256:3fc0f98b3c60d88e9ffa08faff3200f36604930972f8b29155f323b76ea25a06", size = 1875041, upload-time = "2025-12-10T17:55:13.326Z" }, + { url = "https://files.pythonhosted.org/packages/cf/cf/03ba01288e2a41a948bc8526f32c2eeaddd683ed34be1b895e31658d5a4c/grimp-3.14-cp312-cp312-win_amd64.whl", hash = "sha256:6bca77d1d50c8dc402c96af21f4e28e2f1e9938eeabd7417592a22bd83cde3c3", size = 2013868, upload-time = "2025-12-10T17:55:05.907Z" }, + { url = "https://files.pythonhosted.org/packages/3b/bd/d12a9c821b79ba31fc52243e564712b64140fc6d011c2bdbb483d9092a12/grimp-3.14-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:af8a625554beea84530b98cc471902155b5fc042b42dc47ec846fa3e32b0c615", size = 2178632, upload-time = "2025-12-10T17:53:44.55Z" }, + { url = "https://files.pythonhosted.org/packages/96/8c/d6620dbc245149d5a5a7a9342733556ba91a672f358259c0ab31d889b56b/grimp-3.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0dd1942ffb419ad342f76b0c3d3d2d7f312b264ddc578179d13ce8d5acec1167", size = 2110288, upload-time = "2025-12-10T17:53:21.662Z" }, + { url = "https://files.pythonhosted.org/packages/60/9d/ea51edc4eb295c99786040051c66466bfa235fd1def9f592057b36e03d0f/grimp-3.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537f784ce9b4acf8657f0b9714ab69a6c72ffa752eccc38a5a85506103b1a194", size = 2282197, upload-time = "2025-12-10T17:52:09.304Z" }, + { url = "https://files.pythonhosted.org/packages/28/6e/7db27818ced6a797f976ca55d981a3af5c12aec6aeda12d63965847cd028/grimp-3.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:78ab18c08770aa005bef67b873bc3946d33f65727e9f3e508155093db5fa57d6", size = 2235720, upload-time = "2025-12-10T17:52:21.806Z" }, + { url = "https://files.pythonhosted.org/packages/37/26/0e3bbae4826bd6eaabf404738400414071e73ddb1e65bf487dcce17858c4/grimp-3.14-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28ca58728c27e7292c99f964e6ece9295c2f9cfdefc37c18dea0679c783ffb6f", size = 2393023, upload-time = "2025-12-10T17:53:04.149Z" }, + { url = "https://files.pythonhosted.org/packages/49/f2/7da91db5703da34c7ef4c7cddcbb1a8fc30cd85fe54756eba942c6fb27d8/grimp-3.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b5577de29c6c5ae6e08d4ca0ac361b45dba323aa145796e6b320a6ea35414b7", size = 2571108, upload-time = "2025-12-10T17:52:36.523Z" }, + { url = "https://files.pythonhosted.org/packages/25/5e/4d6278f18032c7208696edf8be24a4b5f7fad80acc20ffca737344bcecb5/grimp-3.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d7d1f9f42306f455abcec34db877e4887ff15f2777a43491f7ccbd6936c449b", size = 2358531, upload-time = "2025-12-10T17:52:50.521Z" }, + { url = "https://files.pythonhosted.org/packages/24/fb/231c32493161ac82f27af6a56965daefa0ec6030fdaf5b948ddd5d68d000/grimp-3.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39bd5c9b7cef59ee30a05535e9cb4cbf45a3c503f22edce34d0aa79362a311a9", size = 2308831, upload-time = "2025-12-10T17:53:12.587Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/f6db325bf5efbbebc9c85cad0af865e821a12a0ba58ee309e938cbd5fedf/grimp-3.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7fec3116b4f780a1bc54176b19e6b9f2e36e2ef3164b8fc840660566af35df88", size = 2462138, upload-time = "2025-12-10T17:53:52.403Z" }, + { url = "https://files.pythonhosted.org/packages/41/2e/cc3fe29cf07f70364018086840c228a190539ab8105147e34588db590792/grimp-3.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0233a35a5bbb23688d63e1736b54415fa9994ace8dfeb7de8514ed9dee212968", size = 2501393, upload-time = "2025-12-10T17:54:22.486Z" }, + { url = "https://files.pythonhosted.org/packages/e5/eb/54cada9a726455148da23f64577b5cd164164d23a6449e3fa14551157356/grimp-3.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e46b2fef0f1da7e7e2f8129eb93c7e79db716ff7810140a22ce5504e10ed86df", size = 2504514, upload-time = "2025-12-10T17:54:36.34Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c7/e6afe4f0652df07e8762f61899d1202b73c22c559c804d0a09e5aab2ff17/grimp-3.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e6d9b50623ee1c3d2a1927ec3f5d408995ea1f92f3e91ed996c908bb40e856f", size = 2514018, upload-time = "2025-12-10T17:54:50.76Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/2b8550acc1f010301f02c4fe9664810929fd9277cd032ab608b8534a96fb/grimp-3.14-cp313-cp313-win32.whl", hash = "sha256:fd57c56f5833c99320ec77e8ba5508d56f6fb48ec8032a942f7931cc6ebb80ce", size = 1874922, upload-time = "2025-12-10T17:55:15.239Z" }, + { url = "https://files.pythonhosted.org/packages/46/c7/bc9db5a54ef22972cd17d15ad80a8fee274a471bd3f02300405702d29ea5/grimp-3.14-cp313-cp313-win_amd64.whl", hash = "sha256:173307cf881a126fe5120b7bbec7d54384002e3c83dcd8c4df6ce7f0fee07c53", size = 2013705, upload-time = "2025-12-10T17:55:07.488Z" }, + { url = "https://files.pythonhosted.org/packages/80/7e/02710bf5e50997168c84ac622b10dd41d35515efd0c67549945ad20996a0/grimp-3.14-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe29f8f13fbd7c314908ed535183a36e6db71839355b04869b27f23c58fa082", size = 2281868, upload-time = "2025-12-10T17:52:10.589Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/2e440c6762cc78bd50582e1b092357d2255f0852ccc6218d8db25170ab31/grimp-3.14-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073d285b00100153fd86064c7726bb1b6d610df1356d33bb42d3fd8809cb6e72", size = 2230917, upload-time = "2025-12-10T17:52:23.212Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bb/2e7dce129b88f07fc525fe5c97f28cfb7ed7b62c59386d39226b4d08969c/grimp-3.14-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6d6efc37e1728bbfcd881b89467be5f7b046292597b3ebe5f8e44e89ea8b6cb", size = 2571371, upload-time = "2025-12-10T17:52:37.84Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2b/8f1be8294af60c953687db7dec25525d87ed9c2aa26b66dcbe5244abaca2/grimp-3.14-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5337d65d81960b712574c41e85b480d4480bbb5c6f547c94e634f6c60d730889", size = 2356980, upload-time = "2025-12-10T17:52:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/35/ca/ead91e04b3ddd4774ae74601860ea0f0f21bcf6b970b6769ba9571eb2904/grimp-3.14-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:84a7fea63e352b325daa89b0b7297db411b7f0036f8d710c32f8e5090e1fc3ca", size = 2461540, upload-time = "2025-12-10T17:53:53.749Z" }, + { url = "https://files.pythonhosted.org/packages/94/aa/f8a085ff73c37d6e6a37de9f58799a3fea9e16badf267aaef6f11c9a53a3/grimp-3.14-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d0b19a3726377165fe1f7184a8af317734d80d32b371b6c5578747867ab53c0b", size = 2497925, upload-time = "2025-12-10T17:54:23.842Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a3/db3c2d6df07fe74faf5a28fcf3b44fad2831d323ba4a3c2ff66b77a6520c/grimp-3.14-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9caa4991f530750f88474a3f5ecf6ef9f0d064034889d92db00cfb4ecb78aa24", size = 2501794, upload-time = "2025-12-10T17:54:38.05Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/095f4e3765e7b60425a41e9fbd2b167f8b0acb957cc88c387f631778a09d/grimp-3.14-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1876efc119b99332a5cc2b08a6bdaada2f0ad94b596f0372a497e2aa8bda4d94", size = 2515203, upload-time = "2025-12-10T17:54:52.555Z" }, + { url = "https://files.pythonhosted.org/packages/c6/5f/ee02a3a1237282d324f596a50923bf9d2cb1b1230ef2fef49fb4d3563c2c/grimp-3.14-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3ccf03e65864d6bc7bf1c003c319f5330a7627b3677f31143f11691a088464c2", size = 2177150, upload-time = "2025-12-10T17:53:46.145Z" }, + { url = "https://files.pythonhosted.org/packages/f2/64/2a92889e5fc78e8ef5c548e6a5c6fed78b817eeb0253aca586c28108393a/grimp-3.14-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9ecd58fa58a270e7523f8bec9e6452f4fdb9c21e4cd370640829f1e43fa87a69", size = 2109280, upload-time = "2025-12-10T17:53:23.345Z" }, + { url = "https://files.pythonhosted.org/packages/69/02/5d0b9ab54821e7fbdeb02f3919fa2cb8b9f0c3869fa6e4b969a5766f0ffa/grimp-3.14-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d75d1f8f7944978b39b08d870315174f1ffcd5123be6ccff8ce90467ace648a", size = 2283367, upload-time = "2025-12-10T17:52:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/c2/96/a77c40c92faf7500f42ac019ab8de108b04ffe3db8ec8d6f90416d2322ce/grimp-3.14-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f70bbb1dd6055d08d29e39a78a11c4118c1778b39d17cd8271e18e213524ca7", size = 2237125, upload-time = "2025-12-10T17:52:24.606Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5e/3e1483721c83057bff921cf454dd5ff3e661ae1d2e63150a380382d116c2/grimp-3.14-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f21b7c003626c902669dc26ede83a91220cf0a81b51b27128370998c2f247b4", size = 2391735, upload-time = "2025-12-10T17:53:05.619Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cb/25fad4a174fe672d42f3e5616761a8120a3b03c8e9e2ae3f31159561968a/grimp-3.14-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80d9f056415c936b45561310296374c4319b5df0003da802c84d2830a103792a", size = 2571388, upload-time = "2025-12-10T17:52:39.337Z" }, + { url = "https://files.pythonhosted.org/packages/29/7e/456df7f6a765ce3f160eb32a0f64ed0c1c3cd39b518555dde02087f9b6e4/grimp-3.14-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0332963cd63a45863775d4237e59dedf95455e0a1ea50c356be23100c5fc1d7c", size = 2359637, upload-time = "2025-12-10T17:52:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/3e5005ef21a4e2243f0da489aba86aaaff0bc11d5240d67113482cba88e0/grimp-3.14-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4144350d074f2058fe7c89230a26b34296b161f085b0471a692cb2fe27036f", size = 2308335, upload-time = "2025-12-10T17:53:13.893Z" }, + { url = "https://files.pythonhosted.org/packages/8a/03/4e055f756946d6f71ab7e9d1f8536a9e476777093dd7a050f40412d1a2b1/grimp-3.14-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e148e67975e92f90a8435b1b4c02180b9a3f3d725b7a188ba63793f1b1e445a0", size = 2463680, upload-time = "2025-12-10T17:53:55.507Z" }, + { url = "https://files.pythonhosted.org/packages/26/b9/3c76b7c2e1587e4303a6eff6587c2117c3a7efe1b100cd13d8a4a5613572/grimp-3.14-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1093f7770cb5f3ca6f99fb152f9c949381cc0b078dfdfe598c8ab99abaccda3b", size = 2502808, upload-time = "2025-12-10T17:54:25.383Z" }, + { url = "https://files.pythonhosted.org/packages/20/80/ada10b85ad3125ebedea10256d9c568b6bf28339d2f79d2d196a7b94f633/grimp-3.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a213f45ec69e9c2b28ffd3ba5ab12cc9859da17083ba4dc39317f2083b618111", size = 2504013, upload-time = "2025-12-10T17:54:39.762Z" }, + { url = "https://files.pythonhosted.org/packages/05/45/7c369f749d50b0ceac23cd6874ca4695cc1359a96091c7010301e5c8b619/grimp-3.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f003ac3f226d2437a49af0b6036f26edba57f8a32d329275dbde1b2b2a00a56", size = 2515043, upload-time = "2025-12-10T17:54:54.437Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/85135fe83826ce11ae56a340d32a1391b91eed94d25ce7bc318019f735de/grimp-3.14-cp314-cp314-win32.whl", hash = "sha256:eec81be65a18f4b2af014b1e97296cc9ee20d1115529bf70dd7e06f457eac30b", size = 1877509, upload-time = "2025-12-10T17:55:17.062Z" }, + { url = "https://files.pythonhosted.org/packages/db/61/e4a2234edecb3bb3cff8963bc4ec5cc482a9e3c54f8df0946d7d90003830/grimp-3.14-cp314-cp314-win_amd64.whl", hash = "sha256:cd3bab6164f1d5e313678f0ab4bf45955afe7f5bdb0f2f481014aa9cca7e81ba", size = 2014364, upload-time = "2025-12-10T17:55:08.896Z" }, + { url = "https://files.pythonhosted.org/packages/16/be/3d304443fbf1df4d60c09668846d0c8a605c6c95646226e41d8f5c3254da/grimp-3.14-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b1df33de479be4d620f69633d1876858a8e64a79c07907d47cf3aaf896af057", size = 2281385, upload-time = "2025-12-10T17:52:13.668Z" }, + { url = "https://files.pythonhosted.org/packages/fe/13/493e2648dbb83b3fc517ee675e464beb0154551d726053c7982a3138c6a8/grimp-3.14-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07096d4402e9d5a2c59c402ea3d601f4b7f99025f5e32f077468846fc8d3821b", size = 2231470, upload-time = "2025-12-10T17:52:26.104Z" }, + { url = "https://files.pythonhosted.org/packages/80/84/e772b302385a6b7ec752c88f84ffe35c33d14076245ae27a635aed9c63a2/grimp-3.14-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:712bc28f46b354316af50c469c77953ba3d6cb4166a62b8fb086436a8b05d301", size = 2571579, upload-time = "2025-12-10T17:52:40.889Z" }, + { url = "https://files.pythonhosted.org/packages/69/92/5b23aa7b89c5f4f2cfa636cbeaf33e784378a6b0a823d77a3448670dfacc/grimp-3.14-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abe2bbef1cf8e27df636c02f60184319f138dee4f3a949405c21a4b491980397", size = 2356545, upload-time = "2025-12-10T17:52:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/15/af/bcf2116f4b1c3939ab35f9cdddd9ca59e953e57e9a0ac0c143deaf9f29cc/grimp-3.14-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2f9ae3fabb7a7a8468ddc96acc84ecabd84f168e7ca508ee94d8f32ea9bd5de2", size = 2461022, upload-time = "2025-12-10T17:53:56.923Z" }, + { url = "https://files.pythonhosted.org/packages/81/ce/1a076dce6bc22bca4b9ad5d1bbcd7e1023dcf7bf20ea9404c6462d78f049/grimp-3.14-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:efaf11ea73f7f12d847c54a5d6edcbe919e0369dce2d1aabae6c50792e16f816", size = 2498256, upload-time = "2025-12-10T17:54:27.214Z" }, + { url = "https://files.pythonhosted.org/packages/45/ea/ac735bed202c1c5c019e611b92d3861779e0cfbe2d20fdb0dec94266d248/grimp-3.14-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e089c9ab8aa755ff5af88c55891727783b4eb6b228e7bdf278e17209d954aa1e", size = 2502056, upload-time = "2025-12-10T17:54:41.537Z" }, + { url = "https://files.pythonhosted.org/packages/80/8f/774ce522de6a7e70fbeceeaeb6fbe502f5dfb8365728fb3bb4cb23463da8/grimp-3.14-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a424ad14d5deb56721ac24ab939747f72ab3d378d42e7d1f038317d33b052b77", size = 2515157, upload-time = "2025-12-10T17:54:55.874Z" }, + { url = "https://files.pythonhosted.org/packages/65/cc/dbc00210d0324b8fc1242d8e857757c7e0b62ff0fc0c1bc8dcc42342da85/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c8a8aab9b4310a7e69d7d845cac21cf14563aa0520ea322b948eadeae56d303", size = 2284804, upload-time = "2025-12-10T17:52:16.379Z" }, + { url = "https://files.pythonhosted.org/packages/80/89/851d3d345342e9bcec3fe85d3997db29501fa59f958c1566bf3e24d9d7d9/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d781943b27e5875a41c8f9cfc80f8f0a349f864379192b8c3faa0e6a22593313", size = 2235176, upload-time = "2025-12-10T17:52:30.795Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/5f94702a8d5c121cafcdc9664de34c34f19d0d91a1127bf3946a2631f7a3/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9630d4633607aff94d0ac84b9c64fef1382cdb05b00d9acbde47f8745e264871", size = 2391258, upload-time = "2025-12-10T17:53:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a2/df8c79de5c9e227856d048cc1551c4742a5f97660c40304ac278bd48607f/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb00e1bcca583668554a8e9e1e4229a1d11b0620969310aae40148829ff6a32", size = 2571443, upload-time = "2025-12-10T17:52:43.853Z" }, + { url = "https://files.pythonhosted.org/packages/f0/21/747b7ed9572bbdc34a76dfec12ce510e80164b1aa06d3b21b34994e5f567/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3389da4ceaaa7f7de24a668c0afc307a9f95997bd90f81ec359a828a9bd1d270", size = 2357767, upload-time = "2025-12-10T17:52:57.84Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e6/485c5e3b64933e71f72f0cc45b0d7130418a6a5a13cedc2e8411bd76f290/grimp-3.14-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd7a32970ef97e42d4e7369397c7795287d84a736d788ccb90b6c14f0561d975", size = 2309069, upload-time = "2025-12-10T17:53:15.203Z" }, + { url = "https://files.pythonhosted.org/packages/31/bd/12024a8cba1c77facc1422a7b48cd0d04c252fc9178fd6f99dc05a8af57b/grimp-3.14-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:fd1278623fa09f62abc0fd8a6500f31b421a1fd479980f44c2926020a0becf02", size = 2466429, upload-time = "2025-12-10T17:54:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7f/0e5977887e1c8f00f84bb4125217534806ffdcef9cf52f3580aa3b151f4b/grimp-3.14-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:9cfa52c89333d3d8fe9dc782529e888270d060231c3783e036d424044671dde0", size = 2501190, upload-time = "2025-12-10T17:54:30.107Z" }, + { url = "https://files.pythonhosted.org/packages/42/6b/06acb94b6d0d8c7277bb3e33f93224aa3be5b04643f853479d3bf7b23ace/grimp-3.14-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:48a5be4a12fca6587e6885b4fc13b9e242ab8bf874519292f0f13814aecf52cc", size = 2503440, upload-time = "2025-12-10T17:54:44.444Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4d/2e531370d12e7a564f67f680234710bbc08554238a54991cd244feb61fb6/grimp-3.14-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3fcc332466783a12a42cd317fd344c30fe734ba4fa2362efff132dc3f8d36da7", size = 2516525, upload-time = "2025-12-10T17:54:58.987Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -285,6 +390,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] +[[package]] +name = "import-linter" +version = "2.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "grimp" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/a7/5ae6dc1caf8229f9bcd05b0dd71aba6d5b699b81e5d599fd05bb36046e2a/import_linter-2.12.tar.gz", hash = "sha256:217f0f0f0890b8625389765039d52c215f4c6d4a047f42ea78b6d00ceba1900e", size = 1173831, upload-time = "2026-06-23T08:58:35.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2b/3d9ff17662d01aa83fad076f4881d95136c72781b6b7ef8bf0464a5037a7/import_linter-2.12-py3-none-any.whl", hash = "sha256:b346eeb30cabd5b0630773d42cca4eac8dd459ac5d507ac2ecadc3f99462f231", size = 637947, upload-time = "2026-06-23T08:58:33.969Z" }, +] + [[package]] name = "importlib-metadata" version = "8.7.1" @@ -1109,6 +1229,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "bandit" }, + { name = "import-linter" }, { name = "mypy" }, { name = "pre-commit" }, { name = "pytest" }, @@ -1134,6 +1255,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "bandit", specifier = ">=1.8.2" }, + { name = "import-linter", specifier = ">=2.0" }, { name = "mypy", specifier = ">=1.19.0" }, { name = "pre-commit", specifier = ">=4.1.0" }, { name = "pytest", specifier = ">=7.4.0" }, From baeb82bc51e00f4c41038178234b3a6f41abfa30 Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Fri, 3 Jul 2026 13:52:30 +0300 Subject: [PATCH 2/6] fix(platform): move interrupt models from common to resume_triggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit common/interrupt_models.py imported action_center, attachments, context_grounding, documents, and orchestrator at runtime, forming a cycle cluster around common — the same layering inversion class that broke 0.1.89 (only masked by import ordering). The models are HITL suspend/resume payloads consumed solely by resume_triggers, so they move into that package; all former upward edges now point downward and the import-linter contract passes with zero ignores. The models are importable from uipath.platform.resume_triggers. No re-export shim in common — an eager one recreates the cycle and a lazy one still violates the layering contract. Known external usages (uipath-langchain-python docs/samples/test mocks, POC repos) import from uipath.platform.common and need a coordinated update; none are published package sources. Co-Authored-By: Claude Fable 5 --- packages/uipath-platform/pyproject.toml | 2 +- .../src/uipath/platform/common/__init__.py | 54 ------------------- .../platform/resume_triggers/__init__.py | 54 +++++++++++++++++++ .../platform/resume_triggers/_protocol.py | 37 ++++++------- .../interrupt_models.py | 0 .../tests/services/test_hitl.py | 48 ++++++++--------- packages/uipath-platform/uv.lock | 2 +- packages/uipath/pyproject.toml | 4 +- packages/uipath/uv.lock | 5 +- 9 files changed, 103 insertions(+), 103 deletions(-) rename packages/uipath-platform/src/uipath/platform/{common => resume_triggers}/interrupt_models.py (100%) diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index c56ea764f..604e3cc59 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.1.92" 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/common/__init__.py b/packages/uipath-platform/src/uipath/platform/common/__init__.py index 8f4b23d57..0e51f1f12 100644 --- a/packages/uipath-platform/src/uipath/platform/common/__init__.py +++ b/packages/uipath-platform/src/uipath/platform/common/__init__.py @@ -33,34 +33,6 @@ from ._user_agent import user_agent_value from .auth import TokenData from .dynamic_schema import jsonschema_to_pydantic -from .interrupt_models import ( - CreateBatchTransform, - CreateDeepRag, - CreateDeepRagRaw, - CreateEphemeralIndex, - CreateEphemeralIndexRaw, - CreateEscalation, - CreateTask, - DocumentExtraction, - DocumentExtractionValidation, - InvokeProcess, - InvokeProcessRaw, - InvokeSystemAgent, - WaitBatchTransform, - WaitDeepRag, - WaitDeepRagRaw, - WaitDocumentExtraction, - WaitDocumentExtractionValidation, - WaitEphemeralIndex, - WaitEphemeralIndexRaw, - WaitEscalation, - WaitIntegrationEvent, - WaitJob, - WaitJobRaw, - WaitSystemAgent, - WaitTask, - WaitUntil, -) from .paging import PagedResult __all__ = [ @@ -72,33 +44,7 @@ "FolderContext", "TokenData", "UiPathConfig", - "CreateTask", - "CreateEscalation", - "WaitEscalation", - "InvokeProcess", - "InvokeProcessRaw", - "WaitTask", - "WaitJob", - "WaitJobRaw", "PagedResult", - "CreateDeepRag", - "CreateDeepRagRaw", - "WaitDeepRag", - "WaitDeepRagRaw", - "CreateBatchTransform", - "WaitBatchTransform", - "DocumentExtraction", - "WaitDocumentExtraction", - "InvokeSystemAgent", - "WaitSystemAgent", - "CreateEphemeralIndex", - "CreateEphemeralIndexRaw", - "WaitEphemeralIndex", - "WaitEphemeralIndexRaw", - "DocumentExtractionValidation", - "WaitDocumentExtractionValidation", - "WaitIntegrationEvent", - "WaitUntil", "RequestSpec", "Endpoint", "UiPathUrl", diff --git a/packages/uipath-platform/src/uipath/platform/resume_triggers/__init__.py b/packages/uipath-platform/src/uipath/platform/resume_triggers/__init__.py index 47c35f5b7..13ab154ee 100644 --- a/packages/uipath-platform/src/uipath/platform/resume_triggers/__init__.py +++ b/packages/uipath-platform/src/uipath/platform/resume_triggers/__init__.py @@ -6,6 +6,34 @@ UiPathResumeTriggerHandler, UiPathResumeTriggerReader, ) +from .interrupt_models import ( + CreateBatchTransform, + CreateDeepRag, + CreateDeepRagRaw, + CreateEphemeralIndex, + CreateEphemeralIndexRaw, + CreateEscalation, + CreateTask, + DocumentExtraction, + DocumentExtractionValidation, + InvokeProcess, + InvokeProcessRaw, + InvokeSystemAgent, + WaitBatchTransform, + WaitDeepRag, + WaitDeepRagRaw, + WaitDocumentExtraction, + WaitDocumentExtractionValidation, + WaitEphemeralIndex, + WaitEphemeralIndexRaw, + WaitEscalation, + WaitIntegrationEvent, + WaitJob, + WaitJobRaw, + WaitSystemAgent, + WaitTask, + WaitUntil, +) __all__ = [ "UiPathResumeTriggerReader", @@ -14,4 +42,30 @@ "PropertyName", "TriggerMarker", "is_no_content_marker", + "CreateBatchTransform", + "CreateDeepRag", + "CreateDeepRagRaw", + "CreateEphemeralIndex", + "CreateEphemeralIndexRaw", + "CreateEscalation", + "CreateTask", + "DocumentExtraction", + "DocumentExtractionValidation", + "InvokeProcess", + "InvokeProcessRaw", + "InvokeSystemAgent", + "WaitBatchTransform", + "WaitDeepRag", + "WaitDeepRagRaw", + "WaitDocumentExtraction", + "WaitDocumentExtractionValidation", + "WaitEphemeralIndex", + "WaitEphemeralIndexRaw", + "WaitEscalation", + "WaitIntegrationEvent", + "WaitJob", + "WaitJobRaw", + "WaitSystemAgent", + "WaitTask", + "WaitUntil", ] 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..9f9a28869 100644 --- a/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py +++ b/packages/uipath-platform/src/uipath/platform/resume_triggers/_protocol.py @@ -24,7 +24,25 @@ from uipath.platform.action_center import Task from uipath.platform.action_center.tasks import TaskStatus from uipath.platform.common._config import UiPathConfig -from uipath.platform.common.interrupt_models import ( +from uipath.platform.connections import EventArguments +from uipath.platform.context_grounding import DeepRagStatus, IndexStatus +from uipath.platform.context_grounding.context_grounding_index import ( + ContextGroundingIndex, +) +from uipath.platform.errors import ( + BatchTransformFailedException, + BatchTransformNotCompleteException, + OperationNotCompleteException, +) +from uipath.platform.orchestrator.job import JobState +from uipath.platform.resume_triggers._enums import ( + ExternalTrigger, + ExternalTriggerType, + PropertyName, + TriggerMarker, +) + +from .interrupt_models import ( CreateBatchTransform, CreateDeepRag, CreateDeepRagRaw, @@ -52,23 +70,6 @@ WaitTask, WaitUntil, ) -from uipath.platform.connections import EventArguments -from uipath.platform.context_grounding import DeepRagStatus, IndexStatus -from uipath.platform.context_grounding.context_grounding_index import ( - ContextGroundingIndex, -) -from uipath.platform.errors import ( - BatchTransformFailedException, - BatchTransformNotCompleteException, - OperationNotCompleteException, -) -from uipath.platform.orchestrator.job import JobState -from uipath.platform.resume_triggers._enums import ( - ExternalTrigger, - ExternalTriggerType, - PropertyName, - TriggerMarker, -) def _try_convert_to_json_format(value: str | None) -> Any: diff --git a/packages/uipath-platform/src/uipath/platform/common/interrupt_models.py b/packages/uipath-platform/src/uipath/platform/resume_triggers/interrupt_models.py similarity index 100% rename from packages/uipath-platform/src/uipath/platform/common/interrupt_models.py rename to packages/uipath-platform/src/uipath/platform/resume_triggers/interrupt_models.py diff --git a/packages/uipath-platform/tests/services/test_hitl.py b/packages/uipath-platform/tests/services/test_hitl.py index 5151d7a7a..bd5583595 100644 --- a/packages/uipath-platform/tests/services/test_hitl.py +++ b/packages/uipath-platform/tests/services/test_hitl.py @@ -18,31 +18,6 @@ from uipath.platform.action_center import Task from uipath.platform.action_center.tasks import TaskStatus -from uipath.platform.common import ( - CreateBatchTransform, - CreateDeepRag, - CreateDeepRagRaw, - CreateEphemeralIndex, - CreateEphemeralIndexRaw, - CreateTask, - DocumentExtraction, - DocumentExtractionValidation, - InvokeProcess, - InvokeProcessRaw, - InvokeSystemAgent, - WaitBatchTransform, - WaitDeepRag, - WaitDeepRagRaw, - WaitDocumentExtractionValidation, - WaitEphemeralIndex, - WaitEphemeralIndexRaw, - WaitIntegrationEvent, - WaitJob, - WaitJobRaw, - WaitSystemAgent, - WaitTask, - WaitUntil, -) from uipath.platform.connections import Connection from uipath.platform.context_grounding import ( BatchTransformCreationResponse, @@ -70,10 +45,33 @@ from uipath.platform.orchestrator import Job, JobErrorInfo from uipath.platform.orchestrator.job import JobState from uipath.platform.resume_triggers import ( + CreateBatchTransform, + CreateDeepRag, + CreateDeepRagRaw, + CreateEphemeralIndex, + CreateEphemeralIndexRaw, + CreateTask, + DocumentExtraction, + DocumentExtractionValidation, + InvokeProcess, + InvokeProcessRaw, + InvokeSystemAgent, PropertyName, TriggerMarker, UiPathResumeTriggerCreator, UiPathResumeTriggerReader, + WaitBatchTransform, + WaitDeepRag, + WaitDeepRagRaw, + WaitDocumentExtractionValidation, + WaitEphemeralIndex, + WaitEphemeralIndexRaw, + WaitIntegrationEvent, + WaitJob, + WaitJobRaw, + WaitSystemAgent, + WaitTask, + WaitUntil, ) diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index 2ae2b7efd..365d94e99 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1215,7 +1215,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.91" +version = "0.1.92" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index 561499d12..76339dd56 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "uipath" -version = "2.12.7" +version = "2.12.8" 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.11.7, <0.12.0", - "uipath-platform>=0.1.91, <0.2.0", + "uipath-platform>=0.1.92, <0.2.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 d441e74f0..b837034c1 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2598,7 +2598,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.12.7" +version = "2.12.8" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2741,7 +2741,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.91" +version = "0.1.92" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" }, @@ -2765,6 +2765,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "bandit", specifier = ">=1.8.2" }, + { name = "import-linter", specifier = ">=2.0" }, { name = "mypy", specifier = ">=1.19.0" }, { name = "pre-commit", specifier = ">=4.1.0" }, { name = "pytest", specifier = ">=7.4.0" }, From 5573ed6d61f0f0c12321d007667723221dabd367 Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Fri, 3 Jul 2026 16:43:15 +0300 Subject: [PATCH 3/6] refactor: decouple guardrails from _uipath to avoid cyclic dependency --- packages/uipath-platform/pyproject.toml | 4 +- .../src/uipath/platform/_uipath.py | 34 ++---- .../src/uipath/platform/common/auth.py | 26 ++++- .../platform/guardrails/_default_evaluator.py | 33 ++++++ .../uipath/platform/guardrails/_evaluator.py | 19 ++++ .../guardrails/decorators/validators/_base.py | 16 +-- .../uipath-platform/tests/common/test_auth.py | 44 ++++++++ .../services/test_guardrails_decorators.py | 100 ++++++++++++++++-- 8 files changed, 235 insertions(+), 41 deletions(-) create mode 100644 packages/uipath-platform/src/uipath/platform/guardrails/_default_evaluator.py create mode 100644 packages/uipath-platform/src/uipath/platform/guardrails/_evaluator.py create mode 100644 packages/uipath-platform/tests/common/test_auth.py diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 604e3cc59..7a5bd4ea7 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -138,8 +138,8 @@ type = "layers" containers = ["uipath.platform"] exhaustive = true layers = [ - "_uipath : guardrails : resume_triggers", - "agenthub : automation_ops : automation_tracker : connections : context_grounding : entities : external_applications : governance : memory : pii_detection : portal : resource_catalog : semantic_proxy", + "_uipath : resume_triggers", + "agenthub : automation_ops : automation_tracker : connections : context_grounding : entities : external_applications : governance : guardrails : memory : pii_detection : portal : resource_catalog : semantic_proxy", "orchestrator", "action_center : attachments : chat : documents : identity", "common", diff --git a/packages/uipath-platform/src/uipath/platform/_uipath.py b/packages/uipath-platform/src/uipath/platform/_uipath.py index 98af7b8b6..f2359cd6f 100644 --- a/packages/uipath-platform/src/uipath/platform/_uipath.py +++ b/packages/uipath-platform/src/uipath/platform/_uipath.py @@ -1,8 +1,6 @@ from functools import cached_property from typing import Optional -from pydantic import ValidationError - from uipath.platform.automation_tracker import AutomationTrackerService from .action_center import TasksService @@ -12,15 +10,13 @@ from .chat import ConversationsService, UiPathLlmChatService, UiPathOpenAIService from .common import ( ApiClient, - UiPathApiConfig, UiPathExecutionContext, ) -from .common.auth import resolve_config_from_env +from .common.auth import build_api_config, resolve_config_from_env from .connections import ConnectionsService from .context_grounding import ContextGroundingService from .documents import DocumentsService from .entities import EntitiesService -from .errors import BaseUrlMissingError, SecretMissingError from .external_applications import ExternalApplicationService from .governance import GovernanceService from .guardrails import GuardrailsService @@ -60,25 +56,15 @@ def __init__( scope: Optional[str] = None, debug: bool = False, ) -> None: - try: - if _has_valid_client_credentials(client_id, client_secret): - assert client_id and client_secret - service = ExternalApplicationService(base_url) - token_data = service.get_token_data(client_id, client_secret, scope) - base_url, secret = service._base_url, token_data.access_token - else: - base_url, secret = resolve_config_from_env(base_url, secret) - - self._config = UiPathApiConfig( - base_url=base_url, - secret=secret, - ) - except ValidationError as e: - for error in e.errors(): - if error["loc"][0] == "base_url": - raise BaseUrlMissingError() from e - elif error["loc"][0] == "secret": - raise SecretMissingError() from e + if _has_valid_client_credentials(client_id, client_secret): + assert client_id and client_secret + service = ExternalApplicationService(base_url) + token_data = service.get_token_data(client_id, client_secret, scope) + base_url, secret = service._base_url, token_data.access_token + else: + base_url, secret = resolve_config_from_env(base_url, secret) + + self._config = build_api_config(base_url, secret) self._execution_context = UiPathExecutionContext() @property diff --git a/packages/uipath-platform/src/uipath/platform/common/auth.py b/packages/uipath-platform/src/uipath/platform/common/auth.py index a8f87d3e4..612d3ca4f 100644 --- a/packages/uipath-platform/src/uipath/platform/common/auth.py +++ b/packages/uipath-platform/src/uipath/platform/common/auth.py @@ -3,13 +3,16 @@ from os import environ as env from typing import Optional -from pydantic import BaseModel +from pydantic import BaseModel, ValidationError from uipath.platform.constants import ( ENV_BASE_URL, ENV_UIPATH_ACCESS_TOKEN, ENV_UNATTENDED_USER_ACCESS_TOKEN, ) +from uipath.platform.errors import BaseUrlMissingError, SecretMissingError + +from ._config import UiPathApiConfig class TokenData(BaseModel): @@ -35,3 +38,24 @@ def resolve_config_from_env( or env.get(ENV_UIPATH_ACCESS_TOKEN) ) return base_url_value, secret_value + + +def build_api_config( + base_url: Optional[str], + secret: Optional[str], +) -> UiPathApiConfig: + """Build a validated API config, translating missing fields into typed errors. + + Raises: + BaseUrlMissingError: If ``base_url`` is missing. + SecretMissingError: If ``secret`` is missing. + """ + try: + return UiPathApiConfig(base_url=base_url, secret=secret) # type: ignore[arg-type] + except ValidationError as e: + for error in e.errors(): + if error["loc"][0] == "base_url": + raise BaseUrlMissingError() from e + elif error["loc"][0] == "secret": + raise SecretMissingError() from e + raise diff --git a/packages/uipath-platform/src/uipath/platform/guardrails/_default_evaluator.py b/packages/uipath-platform/src/uipath/platform/guardrails/_default_evaluator.py new file mode 100644 index 000000000..44381ef17 --- /dev/null +++ b/packages/uipath-platform/src/uipath/platform/guardrails/_default_evaluator.py @@ -0,0 +1,33 @@ +"""Default env-driven guardrail evaluator.""" + +from typing import Any + +from uipath.core.guardrails import GuardrailValidationResult + +from ..common import UiPathExecutionContext +from ..common.auth import build_api_config, resolve_config_from_env +from ._guardrails_service import GuardrailsService +from .guardrails import BuiltInValidatorGuardrail + + +class DefaultGuardrailEvaluator: + """Evaluates guardrails via a ``GuardrailsService`` configured from env vars. + + Raises: + BaseUrlMissingError: If the base URL is not configured. + SecretMissingError: If the access token is not configured. + """ + + def __init__(self) -> None: + base_url, secret = resolve_config_from_env(None, None) + self._service = GuardrailsService( + build_api_config(base_url, secret), UiPathExecutionContext() + ) + + def evaluate_guardrail( + self, + input_data: str | dict[str, Any], + guardrail: BuiltInValidatorGuardrail, + ) -> GuardrailValidationResult: + """Validate input data using the provided guardrail.""" + return self._service.evaluate_guardrail(input_data, guardrail) diff --git a/packages/uipath-platform/src/uipath/platform/guardrails/_evaluator.py b/packages/uipath-platform/src/uipath/platform/guardrails/_evaluator.py new file mode 100644 index 000000000..bb4dc7892 --- /dev/null +++ b/packages/uipath-platform/src/uipath/platform/guardrails/_evaluator.py @@ -0,0 +1,19 @@ +"""Internal protocol for guardrail evaluation capabilities.""" + +from typing import Any, Protocol + +from uipath.core.guardrails import GuardrailValidationResult + +from .guardrails import BuiltInValidatorGuardrail + + +class GuardrailEvaluator(Protocol): + """Capability to evaluate a built-in guardrail against input data.""" + + def evaluate_guardrail( + self, + input_data: str | dict[str, Any], + guardrail: BuiltInValidatorGuardrail, + ) -> GuardrailValidationResult: + """Validate input data using the provided guardrail.""" + ... diff --git a/packages/uipath-platform/src/uipath/platform/guardrails/decorators/validators/_base.py b/packages/uipath-platform/src/uipath/platform/guardrails/decorators/validators/_base.py index a9eaf5afd..02aad480d 100644 --- a/packages/uipath-platform/src/uipath/platform/guardrails/decorators/validators/_base.py +++ b/packages/uipath-platform/src/uipath/platform/guardrails/decorators/validators/_base.py @@ -5,6 +5,8 @@ from uipath.core.guardrails import GuardrailValidationResult +from uipath.platform.guardrails._default_evaluator import DefaultGuardrailEvaluator +from uipath.platform.guardrails._evaluator import GuardrailEvaluator from uipath.platform.guardrails.guardrails import BuiltInValidatorGuardrail from .._enums import GuardrailExecutionStage @@ -78,6 +80,8 @@ def get_built_in_guardrail(self, name, description, enabled_for_evals): ) """ + _evaluator: GuardrailEvaluator | None = None + @abstractmethod def get_built_in_guardrail( self, @@ -109,15 +113,13 @@ def run( ) -> GuardrailValidationResult: """Evaluate via the UiPath Guardrails API. - Lazily initialises the ``UiPath`` client on the first call and reuses - it for all subsequent invocations. + Lazily initialises the guardrail evaluator on the first call and + reuses it for all subsequent invocations. """ built_in = self.get_built_in_guardrail(name, description, enabled_for_evals) - if not hasattr(self, "_uipath"): - from uipath.platform import UiPath - - self._uipath: Any = UiPath() - return self._uipath.guardrails.evaluate_guardrail(data, built_in) + if self._evaluator is None: + self._evaluator = DefaultGuardrailEvaluator() + return self._evaluator.evaluate_guardrail(data, built_in) class CustomGuardrailValidator(GuardrailValidatorBase, ABC): diff --git a/packages/uipath-platform/tests/common/test_auth.py b/packages/uipath-platform/tests/common/test_auth.py new file mode 100644 index 000000000..8b8fe67a2 --- /dev/null +++ b/packages/uipath-platform/tests/common/test_auth.py @@ -0,0 +1,44 @@ +"""Tests for config helpers in uipath.platform.common.auth.""" + +from unittest.mock import patch + +import pytest +from pydantic import BaseModel, ValidationError + +from uipath.platform.common import UiPathApiConfig +from uipath.platform.common.auth import build_api_config +from uipath.platform.errors import BaseUrlMissingError, SecretMissingError + + +class TestBuildApiConfig: + def test_returns_config_when_both_values_present(self): + config = build_api_config("https://test.uipath.com", "token") + assert isinstance(config, UiPathApiConfig) + assert config.base_url == "https://test.uipath.com" + assert config.secret == "token" + + def test_missing_base_url_raises_typed_error(self): + with pytest.raises(BaseUrlMissingError): + build_api_config(None, "token") + + def test_missing_secret_raises_typed_error(self): + with pytest.raises(SecretMissingError): + build_api_config("https://test.uipath.com", None) + + def test_both_missing_raises_base_url_error_first(self): + with pytest.raises(BaseUrlMissingError): + build_api_config(None, None) + + def test_unmatched_validation_error_is_reraised(self): + class _Other(BaseModel): + other: str + + with pytest.raises(ValidationError) as exc_info: + _Other.model_validate({}) + unmatched = exc_info.value + + with patch( + "uipath.platform.common.auth.UiPathApiConfig", side_effect=unmatched + ): + with pytest.raises(ValidationError): + build_api_config("https://test.uipath.com", "token") diff --git a/packages/uipath-platform/tests/services/test_guardrails_decorators.py b/packages/uipath-platform/tests/services/test_guardrails_decorators.py index e578cba84..6246bfeaf 100644 --- a/packages/uipath-platform/tests/services/test_guardrails_decorators.py +++ b/packages/uipath-platform/tests/services/test_guardrails_decorators.py @@ -989,8 +989,8 @@ def test_custom_validator_path_delegates_to_run(self): ) assert result == _PASSED - def test_built_in_validator_path_lazy_initializes_uipath(self): - """BuiltInGuardrailValidator.run() lazily creates UiPath() and calls API.""" + def test_built_in_validator_uses_injected_evaluator(self): + """BuiltInGuardrailValidator.run() delegates to an injected evaluator.""" from uipath.platform.guardrails.decorators.validators import ( BuiltInGuardrailValidator, ) @@ -1003,16 +1003,102 @@ def get_built_in_guardrail(self, name, description, enabled_for_evals): return mock_built_in validator = _TestBuiltIn() + fake_evaluator = MagicMock() + fake_evaluator.evaluate_guardrail.return_value = _PASSED + validator._evaluator = fake_evaluator evaluator = _make_evaluator(validator, "G", None, True) - mock_uipath = MagicMock() - mock_uipath.guardrails.evaluate_guardrail.return_value = _PASSED - with patch("uipath.platform.UiPath", return_value=mock_uipath): + result = evaluator({"text": "hello"}, GuardrailExecutionStage.PRE, None, None) + + fake_evaluator.evaluate_guardrail.assert_called_once_with( + {"text": "hello"}, mock_built_in + ) + assert result == _PASSED + + def test_built_in_validator_path_lazy_initializes_default_evaluator(self): + """BuiltInGuardrailValidator.run() creates the default evaluator once.""" + from uipath.platform.guardrails.decorators.validators import ( + BuiltInGuardrailValidator, + ) + from uipath.platform.guardrails.guardrails import BuiltInValidatorGuardrail + + mock_built_in = MagicMock(spec=BuiltInValidatorGuardrail) + + class _TestBuiltIn(BuiltInGuardrailValidator): + def get_built_in_guardrail(self, name, description, enabled_for_evals): + return mock_built_in + + validator = _TestBuiltIn() + evaluator = _make_evaluator(validator, "G", None, True) + + with patch( + "uipath.platform.guardrails.decorators.validators._base.DefaultGuardrailEvaluator" + ) as mock_default: + mock_default.return_value.evaluate_guardrail.return_value = _PASSED + evaluator({"text": "hello"}, GuardrailExecutionStage.PRE, None, None) + evaluator({"text": "hello"}, GuardrailExecutionStage.PRE, None, None) + + mock_default.assert_called_once_with() + assert mock_default.return_value.evaluate_guardrail.call_count == 2 + + def test_built_in_validator_without_env_config_raises_missing_errors( + self, monkeypatch + ): + """Missing env credentials surface as typed errors from the decorator path.""" + from uipath.platform.errors import BaseUrlMissingError, SecretMissingError + from uipath.platform.guardrails.decorators.validators import ( + BuiltInGuardrailValidator, + ) + from uipath.platform.guardrails.guardrails import BuiltInValidatorGuardrail + + class _TestBuiltIn(BuiltInGuardrailValidator): + def get_built_in_guardrail(self, name, description, enabled_for_evals): + return MagicMock(spec=BuiltInValidatorGuardrail) + + for var in ( + "UIPATH_URL", + "UNATTENDED_USER_ACCESS_TOKEN", + "UIPATH_ACCESS_TOKEN", + ): + monkeypatch.delenv(var, raising=False) + + evaluator = _make_evaluator(_TestBuiltIn(), "G", None, True) + with pytest.raises(BaseUrlMissingError): evaluator({"text": "hello"}, GuardrailExecutionStage.PRE, None, None) + + monkeypatch.setenv("UIPATH_URL", "https://test.uipath.com") + with pytest.raises(SecretMissingError): evaluator({"text": "hello"}, GuardrailExecutionStage.PRE, None, None) - # UiPath() should be created only once despite two calls - assert mock_uipath.guardrails.evaluate_guardrail.call_count == 2 + +# --------------------------------------------------------------------------- +# 15b. DefaultGuardrailEvaluator +# --------------------------------------------------------------------------- + + +class TestDefaultGuardrailEvaluator: + def test_constructs_service_from_env_and_delegates(self, monkeypatch): + """The default evaluator builds a GuardrailsService from env config.""" + from uipath.platform.guardrails._default_evaluator import ( + DefaultGuardrailEvaluator, + ) + from uipath.platform.guardrails._guardrails_service import GuardrailsService + from uipath.platform.guardrails.guardrails import BuiltInValidatorGuardrail + + monkeypatch.setenv("UIPATH_URL", "https://test.uipath.com") + monkeypatch.setenv("UIPATH_ACCESS_TOKEN", "token") + + evaluator = DefaultGuardrailEvaluator() + assert isinstance(evaluator._service, GuardrailsService) + + mock_built_in = MagicMock(spec=BuiltInValidatorGuardrail) + with patch.object( + evaluator._service, "evaluate_guardrail", return_value=_PASSED + ) as mock_evaluate: + result = evaluator.evaluate_guardrail("hello", mock_built_in) + + mock_evaluate.assert_called_once_with("hello", mock_built_in) + assert result == _PASSED # --------------------------------------------------------------------------- From f42c777aba2a5b86334e8a935e6a92564526573c Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Mon, 6 Jul 2026 13:47:14 +0300 Subject: [PATCH 4/6] feat: use | instead of : --- packages/uipath-platform/pyproject.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 7a5bd4ea7..20fae6d70 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -133,17 +133,17 @@ exclude_type_checking_imports = true [[tool.importlinter.contracts]] id = "platform-layers" -name = "uipath.platform layered architecture" +name = "uipath-platform layered architecture" type = "layers" containers = ["uipath.platform"] exhaustive = true layers = [ - "_uipath : resume_triggers", - "agenthub : automation_ops : automation_tracker : connections : context_grounding : entities : external_applications : governance : guardrails : memory : pii_detection : portal : resource_catalog : semantic_proxy", - "orchestrator", - "action_center : attachments : chat : documents : identity", - "common", - "constants : errors", + "_uipath : resume_triggers", + "agenthub | automation_ops | automation_tracker | connections | context_grounding | entities | external_applications | governance | guardrails | memory | pii_detection | portal | resource_catalog | semantic_proxy", + "orchestrator", + "action_center | attachments | chat | documents | identity", + "common", + "constants | errors", ] [tool.uv] From 9b7908b2791e662a1da086afca24ef20da32660f Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Mon, 6 Jul 2026 11:26:43 +0300 Subject: [PATCH 5/6] feat: bump to 0.2.0 --- packages/uipath-platform/pyproject.toml | 2 +- packages/uipath-platform/uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 20fae6d70..e0b0566b1 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.1.92" +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/uv.lock b/packages/uipath-platform/uv.lock index 365d94e99..0ce3e3b21 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1215,7 +1215,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.1.92" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "httpx" }, From 9984c7e064d33010daa7931763d3e5f122e33271 Mon Sep 17 00:00:00 2001 From: Andrei Ancuta Date: Mon, 6 Jul 2026 13:42:55 +0300 Subject: [PATCH 6/6] feat: bump uipath to 2.13.0 --- packages/uipath/pyproject.toml | 4 ++-- packages/uipath/uv.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index 76339dd56..7bd1f1219 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.11.7, <0.12.0", - "uipath-platform>=0.1.92, <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 b837034c1..8ae351e7b 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.92" +version = "0.2.0" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },