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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.1.84"
version = "2.1.85"
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.10"
Expand Down
11 changes: 11 additions & 0 deletions src/uipath/_cli/_runtime/_hitl.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,24 @@ async def read(cls, resume_trigger: UiPathResumeTrigger) -> Optional[str]:
default_escalation = Escalation()
match resume_trigger.trigger_type:
case UiPathResumeTriggerType.ACTION:
include_metadata = False
if resume_trigger.payload:
try:
payload_data = json.loads(resume_trigger.payload)
include_metadata = payload_data.get("include_metadata", False)
except (json.JSONDecodeError, AttributeError):
include_metadata = False

if resume_trigger.item_key:
action = await uipath.actions.retrieve_async(
resume_trigger.item_key,
app_folder_key=resume_trigger.folder_key,
app_folder_path=resume_trigger.folder_path,
)

if include_metadata:
return action

if default_escalation.enabled:
return default_escalation.extract_response_value(action.data)

Expand Down
2 changes: 2 additions & 0 deletions src/uipath/models/interrupt_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ class CreateAction(BaseModel):
app_folder_key: Optional[str] = None
app_key: Optional[str] = None
app_version: Optional[int] = 1
include_metadata: bool = False


class WaitAction(BaseModel):
action: Action
app_folder_path: Optional[str] = None
app_folder_key: Optional[str] = None
include_metadata: bool = False
55 changes: 55 additions & 0 deletions tests/cli/test_hitl.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import uuid
from unittest.mock import AsyncMock, patch

Expand Down Expand Up @@ -312,3 +313,57 @@ async def test_create_resume_trigger_api(
assert resume_trigger.api_resume is not None
assert isinstance(resume_trigger.api_resume.inbox_id, str)
assert resume_trigger.api_resume.request == api_input

@pytest.mark.anyio
async def test_interrupt_with_include_metadata_true_returns_full_action(
self,
setup_test_env: None,
) -> None:
"""Test that interrupt() with include_metadata=True returns the whole action object."""
action_key = "test-action-key"

# Create an action with include_metadata=True
create_action = CreateAction(
title="Test Action",
app_name="TestApp",
app_folder_path="/test/path",
data={"input": "test-input"},
include_metadata=True,
)

# Mock the action that would be retrieved after user interaction
mock_action = Action(
key=action_key,
action="Reject",
data={"input": "test-input"},
status=2,
title="Test Action",
id=12345,
)

with (
patch(
"uipath._services.actions_service.ActionsService.create_async"
) as mock_create,
patch(
"uipath._services.actions_service.ActionsService.retrieve_async"
) as mock_retrieve,
):
mock_create.return_value = Action(key=action_key)
mock_retrieve.return_value = mock_action

# Simulate interrupt()
processor = HitlProcessor(create_action)
resume_trigger = await processor.create_resume_trigger()
if isinstance(resume_trigger.payload, dict):
resume_trigger.payload = json.dumps(resume_trigger.payload)

action_data = await HitlReader.read(resume_trigger)

# verify we got the whole action object
assert isinstance(action_data, Action)
assert action_data.action == "Reject", "Should contain 'action' field"
assert action_data.data == {"input": "test-input"}, (
"Should contain correct 'data' field"
)
assert action_data != mock_action.data