2424 UiPathConversationContentPartEndEvent ,
2525 UiPathConversationContentPartEvent ,
2626 UiPathConversationContentPartStartEvent ,
27+ UiPathConversationExecutingToolCallEvent ,
2728 UiPathConversationMessage ,
2829 UiPathConversationMessageData ,
2930 UiPathConversationMessageEndEvent ,
@@ -60,6 +61,7 @@ def __init__(self, runtime_id: str, storage: UiPathRuntimeStorageProtocol | None
6061 self .storage = storage
6162 self .current_message : AIMessageChunk | AIMessage
6263 self .tools_requiring_confirmation : dict [str , Any ] = {}
64+ self .client_side_tools : dict [str , Any ] = {} # {tool_name: output_schema}
6365 self .seen_message_ids : set [str ] = set ()
6466 self ._storage_lock = asyncio .Lock ()
6567 self ._citation_stream_processor = CitationStreamProcessor ()
@@ -436,15 +438,39 @@ async def map_current_message_to_start_tool_call_events(self):
436438 tool_name in self .tools_requiring_confirmation
437439 )
438440 input_schema = self .tools_requiring_confirmation .get (tool_name )
441+ is_client_side = tool_name in self .client_side_tools
442+ output_schema = (
443+ self .client_side_tools .get (tool_name )
444+ if is_client_side
445+ else None
446+ )
439447 events .append (
440448 self .map_tool_call_to_tool_call_start_event (
441449 self .current_message .id ,
442450 tool_call ,
443451 require_confirmation = require_confirmation or None ,
444452 input_schema = input_schema ,
453+ is_client_side_tool = is_client_side or None ,
454+ output_schema = output_schema ,
445455 )
446456 )
447457
458+ # Emit executingToolCall from MessageMapper since there's no durable interrupt
459+ # to trigger it from the runtime loop.
460+ if not require_confirmation and not is_client_side :
461+ events .append (
462+ UiPathConversationMessageEvent (
463+ message_id = self .current_message .id ,
464+ tool_call = UiPathConversationToolCallEvent (
465+ tool_call_id = tool_call ["id" ],
466+ executing = UiPathConversationExecutingToolCallEvent (
467+ tool_name = tool_call ["name" ],
468+ input = tool_call ["args" ],
469+ ),
470+ ),
471+ )
472+ )
473+
448474 if self .storage is not None :
449475 await self .storage .set_value (
450476 self .runtime_id ,
@@ -476,19 +502,24 @@ async def map_tool_message_to_events(
476502 # Keep as string if not valid JSON
477503 pass
478504
479- events = [
480- UiPathConversationMessageEvent (
481- message_id = message_id ,
482- tool_call = UiPathConversationToolCallEvent (
483- tool_call_id = message .tool_call_id ,
484- end = UiPathConversationToolCallEndEvent (
485- timestamp = self .get_timestamp (),
486- output = content_value ,
487- is_error = message .status == "error" ,
505+ # Suppress endToolCall for client-side tools — the client already has the result (it produced it).
506+ is_client_side = message .response_metadata .get ("uipath_client_tool" , False )
507+ events : list [UiPathConversationMessageEvent ] = []
508+
509+ if not is_client_side :
510+ events .append (
511+ UiPathConversationMessageEvent (
512+ message_id = message_id ,
513+ tool_call = UiPathConversationToolCallEvent (
514+ tool_call_id = message .tool_call_id ,
515+ end = UiPathConversationToolCallEndEvent (
516+ timestamp = self .get_timestamp (),
517+ output = content_value ,
518+ is_error = message .status == "error" ,
519+ ),
488520 ),
489- ),
521+ )
490522 )
491- ]
492523
493524 if is_last_tool_call :
494525 events .append (self .map_to_message_end_event (message_id ))
@@ -546,6 +577,8 @@ def map_tool_call_to_tool_call_start_event(
546577 * ,
547578 require_confirmation : bool | None = None ,
548579 input_schema : Any | None = None ,
580+ is_client_side_tool : bool | None = None ,
581+ output_schema : Any | None = None ,
549582 ) -> UiPathConversationMessageEvent :
550583 return UiPathConversationMessageEvent (
551584 message_id = message_id ,
@@ -557,6 +590,8 @@ def map_tool_call_to_tool_call_start_event(
557590 input = tool_call ["args" ],
558591 require_confirmation = require_confirmation ,
559592 input_schema = input_schema ,
593+ is_client_side_tool = is_client_side_tool ,
594+ output_schema = output_schema ,
560595 ),
561596 ),
562597 )
0 commit comments