Skip to content

Commit 4f0d96b

Browse files
fix: handle escalation task deletion scenario (#654)
1 parent a5319af commit 4f0d96b

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

src/uipath_langchain/agent/tools/escalation_tool.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ async def create_escalation_task():
210210
if isinstance(result, dict):
211211
result = TypeAdapter(EscalationToolOutput).validate_python(result)
212212

213+
if result.is_deleted:
214+
return {
215+
"action": EscalationAction.END,
216+
"output": None,
217+
"outcome": "The escalation task was deleted",
218+
}
219+
213220
outcome = result.action
214221
escalation_output = _parse_task_data(
215222
result.data.model_dump()

tests/agent/tools/test_escalation_tool.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ async def test_escalation_tool_metadata_has_recipient(
297297
mock_result = MagicMock()
298298
mock_result.action = None
299299
mock_result.data = {}
300+
mock_result.is_deleted = False
300301
mock_interrupt.return_value = mock_result
301302

302303
tool = create_escalation_tool(escalation_resource)
@@ -325,6 +326,7 @@ async def test_escalation_tool_metadata_recipient_none_when_no_recipients(
325326
mock_result = MagicMock()
326327
mock_result.action = None
327328
mock_result.data = {}
329+
mock_result.is_deleted = False
328330
mock_interrupt.return_value = mock_result
329331

330332
tool = create_escalation_tool(escalation_resource_no_recipient)
@@ -349,6 +351,7 @@ async def test_escalation_tool_with_string_task_title(
349351
mock_result = MagicMock()
350352
mock_result.action = None
351353
mock_result.data = {}
354+
mock_result.is_deleted = False
352355
mock_interrupt.return_value = mock_result
353356

354357
# Create resource with string task title
@@ -398,6 +401,7 @@ async def test_escalation_tool_with_text_builder_task_title(
398401
mock_result = MagicMock()
399402
mock_result.action = None
400403
mock_result.data = {}
404+
mock_result.is_deleted = False
401405
mock_interrupt.return_value = mock_result
402406

403407
# Create resource with TEXT_BUILDER task title containing variable token
@@ -455,6 +459,7 @@ async def test_escalation_tool_with_empty_task_title_defaults_to_escalation_task
455459
mock_result = MagicMock()
456460
mock_result.action = None
457461
mock_result.data = {}
462+
mock_result.is_deleted = False
458463
mock_interrupt.return_value = mock_result
459464

460465
# Create resource with empty string task title
@@ -555,6 +560,7 @@ async def test_escalation_tool_result_validation(
555560
mock_result.assigned_to_user = None
556561
mock_result.action = "approve"
557562
mock_result.data = {}
563+
mock_result.is_deleted = False
558564
mock_interrupt.return_value = mock_result
559565

560566
tool = create_escalation_tool(escalation_resource)
@@ -579,6 +585,7 @@ async def test_escalation_tool_extracts_action_from_result(
579585
mock_result = MagicMock()
580586
mock_result.action = "approve"
581587
mock_result.data = {"approved": True}
588+
mock_result.is_deleted = False
582589
mock_interrupt.return_value = mock_result
583590

584591
tool = create_escalation_tool(escalation_resource)
@@ -588,6 +595,31 @@ async def test_escalation_tool_extracts_action_from_result(
588595

589596
assert mock_interrupt.called
590597

598+
@pytest.mark.asyncio
599+
@patch("uipath_langchain.agent.tools.escalation_tool.UiPath")
600+
@patch("uipath_langchain.agent.tools.durable_interrupt.decorator.interrupt")
601+
async def test_escalation_tool_raises_when_task_is_deleted(
602+
self, mock_interrupt, mock_uipath_class, escalation_resource
603+
):
604+
"""Test that escalation tool raises AgentRuntimeError when task is deleted."""
605+
from uipath_langchain.agent.exceptions import AgentRuntimeError
606+
607+
mock_client = MagicMock()
608+
mock_client.tasks.create_async = AsyncMock(return_value=_make_mock_task())
609+
mock_uipath_class.return_value = mock_client
610+
611+
mock_result = MagicMock()
612+
mock_result.action = "approve"
613+
mock_result.data = {}
614+
mock_result.is_deleted = True
615+
mock_interrupt.return_value = mock_result
616+
617+
tool = create_escalation_tool(escalation_resource)
618+
call = ToolCall(args={}, id="test-call", name=tool.name)
619+
620+
with pytest.raises(AgentRuntimeError):
621+
await tool.awrapper(tool, call, {}) # type: ignore[attr-defined]
622+
591623
@pytest.mark.asyncio
592624
@patch("uipath_langchain.agent.tools.escalation_tool.UiPath")
593625
@patch("uipath_langchain.agent.tools.durable_interrupt.decorator.interrupt")
@@ -787,6 +819,7 @@ async def test_creates_task_then_interrupts_with_wait_escalation(
787819
mock_result.action = "approve"
788820
mock_result.data = {}
789821
mock_result.assigned_to_user = None
822+
mock_result.is_deleted = False
790823
mock_interrupt.return_value = mock_result
791824

792825
tool = create_escalation_tool(escalation_resource)

0 commit comments

Comments
 (0)