Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/workflows/lint-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ wheels/

.cache/*
.cache
.import_linter_cache/
site


Expand Down
1 change: 1 addition & 0 deletions packages/uipath-platform/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
28 changes: 27 additions & 1 deletion packages/uipath-platform/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-platform"
version = "0.2.9"
version = "0.2.10"
description = "HTTP client library for programmatic access to UiPath Platform"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -126,6 +127,31 @@ 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
# TODO: Remove this exception in the next breaking-change release.
ignore_imports = [
"uipath.platform.common.interrupt_models -> uipath.platform.resume_triggers.interrupt_models",
]
unmatched_ignore_imports_alerting = "error"
layers = [
"guardrails",
"_uipath : resume_triggers",
"_guardrails_service | 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"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Internal UiPath Guardrails service layer."""

from ._guardrails_service import GuardrailsService
from .guardrails import (
BYO_VALIDATOR_TYPE,
BuiltInValidatorGuardrail,
EnumListParameterValue,
EnumParameterValue,
GuardrailType,
MapEnumParameterValue,
NumberParameterValue,
TextListParameterValue,
TextParameterValue,
ValidatorParameter,
)

__all__ = [
"BYO_VALIDATOR_TYPE",
"BuiltInValidatorGuardrail",
"EnumListParameterValue",
"EnumParameterValue",
"GuardrailType",
"GuardrailsService",
"MapEnumParameterValue",
"NumberParameterValue",
"TextListParameterValue",
"TextParameterValue",
"ValidatorParameter",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Guardrails models for UiPath Platform."""

from enum import Enum
from typing import Annotated, Literal

from pydantic import BaseModel, ConfigDict, Field
from uipath.core.guardrails import BaseGuardrail


class EnumListParameterValue(BaseModel):
"""Enum list parameter value."""

parameter_type: Literal["enum-list"] = Field(alias="$parameterType")

Check failure on line 13 in packages/uipath-platform/src/uipath/platform/_guardrails_service/guardrails.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "$parameterType" 6 times.

See more on https://sonarcloud.io/project/issues?id=UiPath_uipath-python&issues=AZ9lRHn6D0idQcaAoGRM&open=AZ9lRHn6D0idQcaAoGRM&pullRequest=1788
id: str
value: list[str]

model_config = ConfigDict(populate_by_name=True, extra="allow")


class MapEnumParameterValue(BaseModel):
"""Map enum parameter value."""

parameter_type: Literal["map-enum"] = Field(alias="$parameterType")
id: str
value: dict[str, float]

model_config = ConfigDict(populate_by_name=True, extra="allow")


class NumberParameterValue(BaseModel):
"""Number parameter value."""

parameter_type: Literal["number"] = Field(alias="$parameterType")
id: str
value: float

model_config = ConfigDict(populate_by_name=True, extra="allow")


class EnumParameterValue(BaseModel):
"""Single-select enum parameter value."""

parameter_type: Literal["enum"] = Field(alias="$parameterType")
id: str
value: str

model_config = ConfigDict(populate_by_name=True, extra="allow")


class TextParameterValue(BaseModel):
"""Free-text parameter value."""

parameter_type: Literal["text"] = Field(alias="$parameterType")
id: str
value: str

model_config = ConfigDict(populate_by_name=True, extra="allow")


class TextListParameterValue(BaseModel):
"""List-of-text parameter value."""

parameter_type: Literal["text-list"] = Field(alias="$parameterType")
id: str
value: list[str]

model_config = ConfigDict(populate_by_name=True, extra="allow")


ValidatorParameter = Annotated[
EnumListParameterValue
| MapEnumParameterValue
| NumberParameterValue
| EnumParameterValue
| TextParameterValue
| TextListParameterValue,
Field(discriminator="parameter_type"),
]


#: Sentinel ``validator_type`` for Bring Your Own Guardrail (BYOG) guardrails; the
#: connector-backed configuration is referenced by ``byo_validator_name`` instead.
BYO_VALIDATOR_TYPE = "byo"


class BuiltInValidatorGuardrail(BaseGuardrail):
"""Built-in validator guardrail model."""

guardrail_type: Literal["builtInValidator"] = Field(alias="$guardrailType")
validator_type: str = Field(alias="validatorType")
validator_parameters: list[ValidatorParameter] = Field(
default_factory=list, alias="validatorParameters"
)
byo_validator_name: str | None = Field(default=None, alias="byoValidatorName")

model_config = ConfigDict(populate_by_name=True, extra="allow")


class GuardrailType(str, Enum):
"""Guardrail type enumeration."""

BUILT_IN_VALIDATOR = "builtInValidator"
CUSTOM = "custom"
2 changes: 1 addition & 1 deletion packages/uipath-platform/src/uipath/platform/_uipath.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from uipath.platform.automation_tracker import AutomationTrackerService

from ._guardrails_service import GuardrailsService
from .action_center import TasksService
from .agenthub._agenthub_service import AgentHubService
from .agenthub._remote_a2a_service import RemoteA2aService
Expand All @@ -23,7 +24,6 @@
from .errors import BaseUrlMissingError, SecretMissingError
from .external_applications import ExternalApplicationService
from .governance import GovernanceService
from .guardrails import GuardrailsService
from .memory import MemoryService
from .orchestrator import (
AssetsService,
Expand Down
107 changes: 79 additions & 28 deletions packages/uipath-platform/src/uipath/platform/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,41 @@
This module contains common models used across multiple services.
"""

from typing import TYPE_CHECKING, Any

from uipath.core.triggers import UiPathResumeMetadata

# TODO: Remove the interrupt-model compatibility exports in the next breaking-change release.

Check warning on line 10 in packages/uipath-platform/src/uipath/platform/common/__init__.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=UiPath_uipath-python&issues=AZ9hR6ccesiRx7lWFvgN&open=AZ9hR6ccesiRx7lWFvgN&pullRequest=1788
if TYPE_CHECKING:
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 ._api_client import ApiClient
from ._base_service import BaseService, resolve_trace_id
from ._bindings import (
Expand Down Expand Up @@ -41,34 +74,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
from .timeout import (
UiPathTimeoutError,
Expand Down Expand Up @@ -151,3 +156,49 @@
]

from .validation import validate_pagination_params

_INTERRUPT_MODEL_NAMES = {
"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",
}


def __getattr__(name: str) -> Any:
"""Resolve moved interrupt models on demand."""
if name not in _INTERRUPT_MODEL_NAMES:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

from . import interrupt_models

value = interrupt_models._resolve(name)
globals()[name] = value
return value


def __dir__() -> list[str]:
"""List common exports, including compatibility aliases."""
return sorted(set(globals()) | set(__all__))
Loading
Loading