diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index c4da7e297..2e490ade3 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -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" diff --git a/packages/uipath/src/uipath/agent/models/agent.py b/packages/uipath/src/uipath/agent/models/agent.py index 5aa959e8f..385a37e7c 100644 --- a/packages/uipath/src/uipath/agent/models/agent.py +++ b/packages/uipath/src/uipath/agent/models/agent.py @@ -142,6 +142,13 @@ class AgentEscalationRecipientType(str, CaseInsensitiveEnum): ARGUMENT_GROUP_NAME = "ArgumentGroupName" +class AgentEscalationChannelType(str, CaseInsensitiveEnum): + """Agent escalation channel type enumeration.""" + + ACTION_CENTER = "actionCenter" + ACTION_CENTER_QUICK_FORM = "actionCenterQuickForm" + + class AgentContextRetrievalMode(str, CaseInsensitiveEnum): """Agent context retrieval mode enumeration.""" @@ -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" ) @@ -706,12 +709,31 @@ 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") @@ -719,16 +741,12 @@ class AgentEscalationChannel(BaseCfg): {}, 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 @@ -737,6 +755,34 @@ 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.""" @@ -744,7 +790,7 @@ class AgentEscalationResourceConfig(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[0] = Field(default=0, alias="escalationType") @@ -764,7 +810,7 @@ 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( @@ -772,23 +818,6 @@ class AgentIxpVsEscalationResourceConfig(BaseAgentResourceConfig): ) -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.""" @@ -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)], ], Discriminator(lambda v: v.get("escalation_type") or v.get("escalationType") or 0), ] diff --git a/packages/uipath/tests/agent/models/test_agent.py b/packages/uipath/tests/agent/models/test_agent.py index fcfefa946..c324d4e7e 100644 --- a/packages/uipath/tests/agent/models/test_agent.py +++ b/packages/uipath/tests/agent/models/test_agent.py @@ -36,6 +36,7 @@ AgentNumberOperator, AgentNumberRule, AgentProcessToolResourceConfig, + AgentQuickFormChannelProperties, AgentResourceType, AgentToolArgumentPropertiesVariant, AgentToolType, @@ -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 ( diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 87b11124e..8acee32f9 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2552,7 +2552,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.10.78" +version = "2.10.79" source = { editable = "." } dependencies = [ { name = "applicationinsights" },