55from abc import ABC , abstractmethod
66from typing import Any , Dict , Optional
77
8+ from pydantic import BaseModel
89from pysignalr .client import SignalRClient
910from rich .console import Console
1011from rich .syntax import Syntax
12+ from rich .tree import Tree
1113
1214from uipath ._cli ._runtime ._contracts import (
1315 UiPathBreakpointResult ,
@@ -115,7 +117,7 @@ async def emit_state_update(self, state_event: UiPathAgentStateEvent) -> None:
115117
116118 self .console .print (f"[yellow]●[/yellow] [bold]{ state_event .node_name } [/bold]" )
117119 if state_event .payload :
118- self ._print_json (state_event .payload , label = "State " )
120+ self ._print_json (state_event .payload , label = "state " )
119121
120122 async def emit_breakpoint_hit (
121123 self , breakpoint_result : UiPathBreakpointResult
@@ -135,7 +137,7 @@ async def emit_breakpoint_hit(
135137
136138 # Display current state
137139 if breakpoint_result .current_state :
138- self ._print_json (breakpoint_result .current_state , label = "State " )
140+ self ._print_json (breakpoint_result .current_state , label = "state " )
139141
140142 async def emit_execution_completed (
141143 self ,
@@ -157,7 +159,7 @@ async def emit_execution_completed(
157159
158160 self .console .print (f"[{ color } ]{ symbol } END[/{ color } ]" )
159161 if runtime_result .output :
160- self ._print_json (runtime_result .output , label = "Output " )
162+ self ._print_json (runtime_result .output , label = "output " )
161163
162164 async def emit_execution_error (
163165 self ,
@@ -193,36 +195,83 @@ async def wait_for_resume(self) -> Any:
193195 self .console .print ()
194196 return user_input
195197
196- def _print_json (self , data : Dict [str , Any ], label : str = "Data " ) -> None :
197- """Print JSON data in a readable format ."""
198+ def _print_json (self , data : Dict [str , Any ], label : str = "data " ) -> None :
199+ """Print JSON data with enhanced hierarchy ."""
198200 try :
199- json_str = json .dumps (data , indent = 2 , default = str )
200-
201- # Limit output if too large
202- is_truncated = False
203- if len (json_str ) > 2000 :
204- json_str = json_str [:2000 ] + "\n ..."
205- is_truncated = True
206-
207- syntax = Syntax (
208- json_str ,
209- "json" ,
210- theme = "monokai" ,
211- line_numbers = False ,
212- background_color = "default" ,
213- )
201+ # Create a tree for nested structure
202+ tree = Tree (f"[bold cyan]{ label } [/bold cyan]" )
203+
204+ def process_value (
205+ node : Tree , value : Any , key_label : str , depth : int
206+ ) -> None :
207+ """Process a single value and add it to the tree."""
208+ if isinstance (value , BaseModel ):
209+ branch = node .add (
210+ f"{ key_label } [dim]({ type (value ).__name__ } )[/dim]"
211+ )
212+ add_to_tree (branch , value , depth + 1 )
213+ elif isinstance (value , dict ):
214+ branch = node .add (f"{ key_label } [dim](dict)[/dim]" )
215+ add_to_tree (branch , value , depth + 1 )
216+ elif isinstance (value , list ):
217+ branch = node .add (
218+ f"{ key_label } [dim](list, { len (value )} items)[/dim]"
219+ )
220+ add_to_tree (branch , value , depth + 1 )
221+ else :
222+ val_str = str (value )
223+ if len (val_str ) > 250 :
224+ val_str = val_str [:250 ] + "..."
225+ node .add (f"{ key_label } : [green]{ val_str } [/green]" )
226+
227+ def add_to_tree (node : Tree , payload : Any , depth : int = 0 ):
228+ if depth > 10 :
229+ node .add ("[dim]...[/dim]" )
230+ return
231+
232+ if isinstance (payload , BaseModel ):
233+ try :
234+ payload = payload .model_dump () # Pydantic v2
235+ except AttributeError :
236+ payload = payload .dict () # Pydantic v1
237+ add_to_tree (node , payload , depth )
238+
239+ elif isinstance (payload , dict ):
240+ for key , value in payload .items ():
241+ process_value (node , value , f"[yellow]{ key } [/yellow]" , depth )
242+
243+ elif isinstance (payload , list ):
244+ for i , item in enumerate (payload ):
245+ process_value (node , item , f"[cyan]#{ i } [/cyan]" , depth )
246+
247+ else :
248+ val_str = str (payload )
249+ if len (val_str ) > 250 :
250+ val_str = val_str [:250 ] + "..."
251+ node .add (f"[green]{ val_str } [/green]" )
252+
253+ add_to_tree (tree , data )
214254
215255 self .console .print ()
216- truncated_text = " (truncated)" if is_truncated else ""
217- self .console .print (f"[dim]{ label } { truncated_text } :" )
218- self .console .print (syntax )
256+ self .console .print (tree )
219257 self .console .print ()
258+
220259 except Exception :
221- # Fallback to simple print
222- self .console .print ()
223- self .console .print (f"[dim]{ label } :" )
224- self .console .print (str (data ))
225- self .console .print ()
260+ try :
261+ json_str = json .dumps (data , indent = 2 , default = str )
262+ if len (json_str ) > 10000 :
263+ json_str = json_str [:10000 ] + "\n ..."
264+
265+ syntax = Syntax (json_str , "json" , theme = "monokai" , line_numbers = False )
266+ self .console .print (f"\n [dim]{ label } :" )
267+ self .console .print (syntax )
268+ self .console .print ()
269+ except Exception :
270+ # Fallback to simple print
271+ self .console .print ()
272+ self .console .print (f"[dim]{ label } :" )
273+ self .console .print (str (data ))
274+ self .console .print ()
226275
227276
228277class SignalRDebugBridge (UiPathDebugBridge ):
0 commit comments