Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.10.78"
version = "2.10.79"
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"
Expand Down
95 changes: 62 additions & 33 deletions packages/uipath/src/uipath/agent/models/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ class AgentEscalationRecipientType(str, CaseInsensitiveEnum):
ARGUMENT_GROUP_NAME = "ArgumentGroupName"


class AgentEscalationChannelType(str, CaseInsensitiveEnum):
"""Agent escalation channel type enumeration."""

ACTION_CENTER = "actionCenter"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe leave a comment that this refers to action center apps specifically

ACTION_CENTER_QUICK_FORM = "actionCenterQuickForm"


class AgentContextRetrievalMode(str, CaseInsensitiveEnum):
"""Agent context retrieval mode enumeration."""

Expand Down Expand Up @@ -691,13 +698,9 @@ def _resolve_task_title(v: Any) -> Any:
return v


class AgentEscalationChannelProperties(BaseResourceProperties):
"""Agent escalation channel properties model."""
class BaseEscalationChannelProperties(BaseResourceProperties):
"""Fields shared by every escalation channel's properties."""

app_name: str | None = Field(default=None, alias="appName")
app_version: int = Field(..., alias="appVersion")
folder_name: Optional[str] = Field(None, alias="folderName")
resource_key: str | None = Field(default=None, alias="resourceKey")
is_actionable_message_enabled: Optional[bool] = Field(
None, alias="isActionableMessageEnabled"
)
Expand All @@ -706,29 +709,44 @@ class AgentEscalationChannelProperties(BaseResourceProperties):
)


class AgentEscalationChannel(BaseCfg):
"""Agent escalation channel model."""
class AgentEscalationChannelProperties(BaseEscalationChannelProperties):
"""Action Center app-task channel properties (channel type ``actionCenter``)."""

app_name: str | None = Field(default=None, alias="appName")
app_version: int = Field(..., alias="appVersion")
folder_name: Optional[str] = Field(None, alias="folderName")
resource_key: str | None = Field(default=None, alias="resourceKey")


class AgentQuickFormChannelProperties(BaseEscalationChannelProperties):
"""Quick Form channel properties (channel type ``actionCenterQuickForm``)."""

form_schema: Dict[str, Any] = Field(..., alias="schema")

@property
def schema_id(self) -> str | None:
"""Return the schema id nested inside the form schema body."""
return self.form_schema.get("schemaId")


class BaseAgentEscalationChannel(BaseCfg):
"""Fields shared by every escalation channel variant."""

id: Optional[str] = Field(None, alias="id")
name: str = Field(..., alias="name")
type: str = Field(alias="type")
description: str = Field(..., alias="description")
input_schema: Dict[str, Any] = Field(..., alias="inputSchema")
output_schema: Dict[str, Any] = Field(EMPTY_SCHEMA, alias="outputSchema")
argument_properties: Dict[str, AgentToolArgumentProperties] = Field(
{}, alias="argumentProperties"
)
outcome_mapping: Optional[Dict[str, str]] = Field(None, alias="outcomeMapping")
properties: AgentEscalationChannelProperties = Field(..., alias="properties")
recipients: List[AgentEscalationRecipient] = Field(..., alias="recipients")
task_title: Optional[Union[str, TaskTitle]] = Field(
default="Escalation Task", alias="taskTitle"
)
priority: Optional[str] = None
labels: List[str] = Field(default_factory=list)
# schema_body avoids shadowing pydantic.BaseModel.schema(); JSON alias stays "schema".
schema_id: Optional[str] = Field(None, alias="schemaId")
schema_body: Optional[Dict[str, Any]] = Field(None, alias="schema")

@model_validator(mode="before")
@classmethod
Expand All @@ -737,14 +755,42 @@ def _apply_task_title_resolution(cls, v: Any) -> Any:
return _resolve_task_title(v)


class AgentEscalationChannel(BaseAgentEscalationChannel):
"""Action Center app-task escalation channel (channel type ``actionCenter``)."""

type: Literal[AgentEscalationChannelType.ACTION_CENTER] = Field(
default=AgentEscalationChannelType.ACTION_CENTER, alias="type"
)
properties: AgentEscalationChannelProperties = Field(..., alias="properties")


class AgentQuickFormEscalationChannel(BaseAgentEscalationChannel):
"""Quick Form escalation channel; FormLib schema lives in ``properties.form_schema``."""

type: Literal[AgentEscalationChannelType.ACTION_CENTER_QUICK_FORM] = Field(
default=AgentEscalationChannelType.ACTION_CENTER_QUICK_FORM, alias="type"
)
properties: AgentQuickFormChannelProperties = Field(..., alias="properties")


EscalationChannel = Annotated[
Union[
AgentEscalationChannel,
AgentQuickFormEscalationChannel,
],
Field(discriminator="type"),
_case_insensitive_enum_validator("type", AgentEscalationChannelType),
]


class AgentEscalationResourceConfig(BaseAgentResourceConfig):
"""Agent escalation resource configuration model."""

id: Optional[str] = Field(None, alias="id")
resource_type: Literal[AgentResourceType.ESCALATION] = Field(
alias="$resourceType", default=AgentResourceType.ESCALATION, frozen=True
)
channels: List[AgentEscalationChannel] = Field(alias="channels")
channels: List[EscalationChannel] = Field(alias="channels")
is_agent_memory_enabled: bool = Field(default=False, alias="isAgentMemoryEnabled")
escalation_type: Literal[0] = Field(default=0, alias="escalationType")

Expand All @@ -764,31 +810,14 @@ class AgentIxpVsEscalationResourceConfig(BaseAgentResourceConfig):
resource_type: Literal[AgentResourceType.ESCALATION] = Field(
alias="$resourceType", default=AgentResourceType.ESCALATION, frozen=True
)
channels: List[AgentEscalationChannel] = Field(alias="channels")
channels: List[EscalationChannel] = Field(alias="channels")
is_agent_memory_enabled: bool = Field(default=False, alias="isAgentMemoryEnabled")
escalation_type: Literal[1] = Field(default=1, alias="escalationType")
vs_escalation_properties: AgentIxpVsEscalationProperties = Field(
..., alias="vsEscalationProperties"
)


class AgentQuickFormEscalationResourceConfig(BaseAgentResourceConfig):
"""Quick Form Agent escalation resource configuration model (escalationType=2).

Quick Form escalations render a schema-first HITL task in Action Center via FormLib.
The schema (and its key) live on the channel (see AgentEscalationChannel.schema_id /
schema) and are sent inline to Orchestrator's GenericTasks/CreateTask endpoint.
"""

id: Optional[str] = Field(None, alias="id")
resource_type: Literal[AgentResourceType.ESCALATION] = Field(
alias="$resourceType", default=AgentResourceType.ESCALATION, frozen=True
)
channels: List[AgentEscalationChannel] = Field(alias="channels")
is_agent_memory_enabled: bool = Field(default=False, alias="isAgentMemoryEnabled")
escalation_type: Literal[2] = Field(default=2, alias="escalationType")


class BaseAgentToolResourceConfig(BaseAgentResourceConfig):
"""Base agent tool resource configuration model."""

Expand Down Expand Up @@ -994,11 +1023,11 @@ class AgentUnknownToolResourceConfig(BaseAgentToolResourceConfig):
Field(discriminator="type"),
]


EscalationResourceConfig = Annotated[
Union[
Annotated[AgentEscalationResourceConfig, Tag(0)],
Annotated[AgentIxpVsEscalationResourceConfig, Tag(1)],
Annotated[AgentQuickFormEscalationResourceConfig, Tag(2)],
],
Comment thread
cotovanu-cristian marked this conversation as resolved.
Discriminator(lambda v: v.get("escalation_type") or v.get("escalationType") or 0),
]
Expand Down
74 changes: 74 additions & 0 deletions packages/uipath/tests/agent/models/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
AgentNumberOperator,
AgentNumberRule,
AgentProcessToolResourceConfig,
AgentQuickFormChannelProperties,
AgentResourceType,
AgentToolArgumentPropertiesVariant,
AgentToolType,
Expand Down Expand Up @@ -2607,11 +2608,84 @@ def test_agent_with_ixp_vs_escalation(self):
assert len(channel.recipients) == 0

# Validate channel properties
assert isinstance(channel, AgentEscalationChannel)
assert channel.properties.app_name is None
assert channel.properties.app_version == 1
assert channel.properties.folder_name is None
assert channel.properties.resource_key is None

def test_quick_form_channel_properties_derive_schema_id_from_body(self):
"""schema_id reads the schemaId nested inside the schema body."""

props = AgentQuickFormChannelProperties.model_validate(
{
"schema": {
"schemaId": "e74ebb74-80ba-47b9-a370-532a1ba4c41e",
"fields": [],
"outcomes": [],
},
}
)
assert props.schema_id == "e74ebb74-80ba-47b9-a370-532a1ba4c41e"

def test_quick_form_channel_properties_schema_id_none_when_absent(self):
"""schema_id is None when the schema body carries no schemaId."""

props = AgentQuickFormChannelProperties.model_validate(
{"schema": {"fields": [], "outcomes": []}}
)
assert props.schema_id is None

def test_quick_form_channel_properties_require_schema(self):
with pytest.raises(ValidationError):
AgentQuickFormChannelProperties.model_validate(
{"isActionableMessageEnabled": False}
)

def test_quick_form_channel_requires_schema(self):
"""A quick-form channel without a schema fails to parse."""
with pytest.raises(ValidationError):
TypeAdapter(AgentEscalationResourceConfig).validate_python(
{
"$resourceType": "escalation",
"name": "Escalation",
"description": "",
"channels": [
{
"name": "c",
"description": "",
"inputSchema": {"type": "object", "properties": {}},
"type": "actionCenterQuickForm",
"recipients": [],
"properties": {"isActionableMessageEnabled": False},
}
],
"isAgentMemoryEnabled": False,
}
)

def test_unknown_escalation_channel_type_is_rejected(self):
"""An unrecognized channel type fails to parse; the runtime cannot handle it."""
with pytest.raises(ValidationError):
TypeAdapter(AgentEscalationResourceConfig).validate_python(
{
"$resourceType": "escalation",
"name": "Escalation",
"description": "",
"channels": [
{
"name": "c",
"description": "",
"inputSchema": {"type": "object", "properties": {}},
"type": "someFutureChannel",
"recipients": [],
"properties": {},
}
],
"isAgentMemoryEnabled": False,
}
)

def test_task_title_text_builder_type(self):
"""Test TextBuilderTaskTitle with tokens."""
from uipath.agent.models.agent import (
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading