11"""Escalation tool creation for Action Center integration."""
22
3+ import logging
34from enum import Enum
45from typing import Any , Literal
56
1415 AssetRecipient ,
1516 StandardRecipient ,
1617)
18+ from uipath .agent .utils .text_tokens import safe_get_nested
1719from uipath .eval .mocks import mockable
1820from uipath .platform import UiPath
1921from uipath .platform .action_center .tasks import TaskRecipient , TaskRecipientType
3941 sanitize_tool_name ,
4042)
4143
44+ try :
45+ from uipath .agent .models .agent import ( # type: ignore[attr-defined]
46+ ArgumentEmailRecipient ,
47+ ArgumentGroupNameRecipient ,
48+ )
49+ except ImportError :
50+ # Added in uipath>=2.10.43; define stubs for older installed versions
51+
52+ class ArgumentEmailRecipient : # type: ignore[no-redef]
53+ """Stub for ArgumentEmailRecipient (requires uipath>=2.10.43)."""
54+
55+ pass
56+
57+ class ArgumentGroupNameRecipient : # type: ignore[no-redef]
58+ """Stub for ArgumentGroupNameRecipient (requires uipath>=2.10.43)."""
59+
60+ pass
61+
62+
63+ logger = logging .getLogger (__name__ )
64+
4265
4366class EscalationAction (str , Enum ):
4467 """Actions that can be taken after an escalation completes."""
@@ -49,6 +72,7 @@ class EscalationAction(str, Enum):
4972
5073async def resolve_recipient_value (
5174 recipient : AgentEscalationRecipient ,
75+ input_args : dict [str , Any ] | None = None ,
5276) -> TaskRecipient | None :
5377 """Resolve recipient value based on recipient type."""
5478 if isinstance (recipient , AssetRecipient ):
@@ -60,6 +84,26 @@ async def resolve_recipient_value(
6084 type = TaskRecipientType .GROUP_NAME
6185 return TaskRecipient (value = value , type = type , displayName = value )
6286
87+ if isinstance (recipient , ArgumentEmailRecipient ):
88+ value = safe_get_nested (input_args or {}, recipient .argument_name )
89+ if not value :
90+ raise ValueError (
91+ f"Argument '{ recipient .argument_name } ' has no value in agent input."
92+ )
93+ return TaskRecipient (
94+ value = value , type = TaskRecipientType .EMAIL , displayName = value
95+ )
96+
97+ if isinstance (recipient , ArgumentGroupNameRecipient ):
98+ value = safe_get_nested (input_args or {}, recipient .argument_name )
99+ if not value :
100+ raise ValueError (
101+ f"Argument '{ recipient .argument_name } ' has no value in agent input."
102+ )
103+ return TaskRecipient (
104+ value = value , type = TaskRecipientType .GROUP_NAME , displayName = value
105+ )
106+
63107 if isinstance (recipient , StandardRecipient ):
64108 type = TaskRecipientType (recipient .type )
65109 if recipient .type == AgentEscalationRecipientType .USER_EMAIL :
@@ -159,8 +203,11 @@ class EscalationToolOutput(BaseModel):
159203 _bts_context : dict [str , Any ] = {}
160204
161205 async def escalation_tool_fn (** kwargs : Any ) -> dict [str , Any ]:
206+ agent_input : dict [str , Any ] = (
207+ tool .metadata .get ("agent_input" ) if tool .metadata else None
208+ ) or {}
162209 recipient : TaskRecipient | None = (
163- await resolve_recipient_value (channel .recipients [0 ])
210+ await resolve_recipient_value (channel .recipients [0 ], input_args = agent_input )
164211 if channel .recipients
165212 else None
166213 )
@@ -252,11 +299,13 @@ async def escalation_wrapper(
252299 if tool .metadata is None :
253300 raise RuntimeError ("Tool metadata is required for task_title resolution" )
254301
302+ state_dict = sanitize_dict_for_serialization (dict (state ))
255303 tool .metadata ["task_title" ] = resolve_task_title (
256304 channel .task_title ,
257- sanitize_dict_for_serialization ( dict ( state )) ,
305+ state_dict ,
258306 default_title = "Escalation Task" ,
259307 )
308+ tool .metadata ["agent_input" ] = state_dict
260309
261310 tool .metadata ["_call_id" ] = call .get ("id" )
262311 tool .metadata ["_call_args" ] = dict (call .get ("args" , {}))
0 commit comments