Skip to content

Commit 3ca39ff

Browse files
committed
refactor: send executing tool call event for tools without confirmation to go with other event handling
1 parent 1549ecc commit 3ca39ff

4 files changed

Lines changed: 11 additions & 21 deletions

File tree

src/uipath_langchain/agent/tools/client_side_tool.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,9 @@ def create_client_side_tool(
6161
) -> StructuredTool:
6262
"""Create a client-side tool that pauses the graph and waits for the client to execute it.
6363
64-
The tool uses @durable_interrupt to suspend the graph. The client SDK receives
65-
an executingToolCall event, runs its registered handler, and sends endToolCall
66-
back through CAS. The bridge routes that endToolCall to wait_for_resume(),
67-
which unblocks the graph with the client's result.
64+
The tool uses @durable_interrupt to suspend the graph. The client receives
65+
an executingToolCall event, executes its registered handler, and sends
66+
endToolCall back through CAS.
6867
"""
6968
tool_name = sanitize_tool_name(resource.name)
7069
input_model = create_model_from_schema(resource.input_schema)
@@ -96,7 +95,6 @@ async def wait_for_client_execution() -> dict[str, Any]:
9695
"tool_call_id": tool_call_id,
9796
"tool_name": tool_name,
9897
"input": kwargs,
99-
"is_execution_phase": True,
10098
}
10199

102100
result = await wait_for_client_execution()

src/uipath_langchain/chat/hitl.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,12 @@ def request_approval(
127127
"""
128128
tool_call_id: str = tool_args.pop("tool_call_id")
129129

130-
# For server-side tools, is_execution_phase=True so the bridge emits
131-
# executingToolCall at the confirmation interrupt.
132-
# For client-side tools, is_execution_phase=False here because the
133-
# execution interrupt in client_side_tool.py handles it separately.
134-
is_execution_trigger = not (tool.metadata or {}).get(
135-
IS_CONVERSATIONAL_CLIENT_SIDE_TOOL, False
136-
)
137-
138130
@durable_interrupt
139131
def ask_confirmation():
140132
return {
141133
"tool_call_id": tool_call_id,
142134
"tool_name": tool.name,
143135
"input": tool_args,
144-
"is_execution_phase": is_execution_trigger,
145136
}
146137

147138
response = ask_confirmation()

src/uipath_langchain/runtime/messages.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,10 @@ async def map_current_message_to_start_tool_call_events(self):
465465
)
466466
)
467467

468-
# Emit executingToolCall from MessageMapper for tools without
469-
# a durable interrupt. Tools with interrupts (client-side, HITL)
470-
# get executingToolCall from the bridge instead.
471-
if not require_confirmation and not is_client_side:
468+
# Emit executingToolCall for tools without confirmation.
469+
# Confirmed tools get executingToolCall from the runtime
470+
# loop after the confirmation resumes.
471+
if not require_confirmation:
472472
events.append(
473473
UiPathConversationMessageEvent(
474474
message_id=self.current_message.id,

tests/runtime/test_chat_message_mapper.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,8 +2259,8 @@ async def test_no_executing_for_confirmation_tool(self):
22592259
assert len(executing_events) == 0
22602260

22612261
@pytest.mark.asyncio
2262-
async def test_no_executing_for_client_side_tool(self):
2263-
"""Should NOT emit executingToolCall for a client-side tool (bridge handles it)."""
2262+
async def test_emits_executing_for_client_side_tool(self):
2263+
"""Should emit executingToolCall for a client-side tool without confirmation."""
22642264
storage = create_mock_storage()
22652265
storage.get_value.return_value = {}
22662266
mapper = UiPathChatMessagesMapper("test-runtime", storage)
@@ -2287,7 +2287,8 @@ async def test_no_executing_for_client_side_tool(self):
22872287
for e in result
22882288
if e.tool_call is not None and e.tool_call.executing is not None
22892289
]
2290-
assert len(executing_events) == 0
2290+
assert len(executing_events) == 1
2291+
assert executing_events[0].tool_call.executing.input == {"title": "Avatar"}
22912292

22922293

22932294
class TestClientSideToolEndSuppression:

0 commit comments

Comments
 (0)