Skip to content

Commit 28cce39

Browse files
feat: resolve ArgumentEmail and ArgumentGroupName recipients at runtime
Updates resolve_recipient_value() in escalation_tool to handle the two new argument-driven recipient types. Each type carries an argument_name that is looked up in the tool's runtime kwargs to produce a concrete email or group-name TaskRecipient. Follows the same pattern as AssetRecipient resolution. Backward compatible -- input_args defaults to None. Depends on uipath-python adding ArgumentEmailRecipient and ArgumentGroupNameRecipient to uipath.agent.models.agent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6e11d22 commit 28cce39

File tree

1 file changed

+51
-2
lines changed

1 file changed

+51
-2
lines changed

src/uipath_langchain/agent/tools/escalation_tool.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Escalation tool creation for Action Center integration."""
22

3+
import logging
34
from enum import Enum
45
from typing import Any, Literal
56

@@ -14,6 +15,7 @@
1415
AssetRecipient,
1516
StandardRecipient,
1617
)
18+
from uipath.agent.utils.text_tokens import safe_get_nested
1719
from uipath.eval.mocks import mockable
1820
from uipath.platform import UiPath
1921
from uipath.platform.action_center.tasks import TaskRecipient, TaskRecipientType
@@ -39,6 +41,27 @@
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

4366
class EscalationAction(str, Enum):
4467
"""Actions that can be taken after an escalation completes."""
@@ -49,6 +72,7 @@ class EscalationAction(str, Enum):
4972

5073
async 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

Comments
 (0)