Skip to content

Commit eeb3aa3

Browse files
authored
Merge pull request #747 from UiPath/fix/local_debug_state_details
fix: improve local debug details
2 parents 85056ab + 79a8ff8 commit eeb3aa3

File tree

8 files changed

+83
-37
lines changed

8 files changed

+83
-37
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath"
3-
version = "2.1.97"
3+
version = "2.1.98"
44
description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.10"

src/uipath/_cli/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import click
55

6+
from .._utils._logs import setup_logging
67
from ._utils._common import add_cwd_to_path, load_environment_variables
78
from .cli_auth import auth as auth
89
from .cli_debug import debug as debug # type: ignore
@@ -47,6 +48,7 @@ def _get_safe_version() -> str:
4748
def cli(lv: bool, v: bool) -> None:
4849
load_environment_variables()
4950
add_cwd_to_path()
51+
setup_logging()
5052
if lv:
5153
try:
5254
version = importlib.metadata.version("uipath-langchain")

src/uipath/_cli/_debug/_bridge.py

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
from abc import ABC, abstractmethod
66
from typing import Any, Dict, Optional
77

8+
from pydantic import BaseModel
89
from pysignalr.client import SignalRClient
910
from rich.console import Console
1011
from rich.syntax import Syntax
12+
from rich.tree import Tree
1113

1214
from 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

228277
class SignalRDebugBridge(UiPathDebugBridge):

src/uipath/_cli/_debug/_runtime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
5656
raise RuntimeError("Failed to create inner runtime")
5757

5858
await self.debug_bridge.emit_execution_started(
59-
execution_id=self.context.execution_id or "default"
59+
execution_id=self.context.job_id or "default"
6060
)
6161

6262
# Try to stream events from inner runtime
@@ -78,7 +78,7 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
7878
except Exception as e:
7979
# Emit execution error
8080
await self.debug_bridge.emit_execution_error(
81-
execution_id=self.context.execution_id or "default",
81+
execution_id=self.context.job_id or "default",
8282
error=str(e),
8383
)
8484
raise

src/uipath/_cli/cli_debug.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# type: ignore
22
import asyncio
33
import os
4-
import uuid
54
from os import environ as env
65
from typing import Optional
76

@@ -110,7 +109,6 @@ def debug(
110109
execution_output_file=output_file,
111110
debug=debug,
112111
)
113-
debug_context.execution_id = debug_context.job_id or str(uuid.uuid4())
114112

115113
runtime_factory = UiPathRuntimeFactory(
116114
UiPathScriptRuntime,

src/uipath/_uipath.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
UiPathLlmChatService,
2222
UiPathOpenAIService,
2323
)
24-
from ._utils import setup_logging
2524
from ._utils._auth import resolve_config
2625
from .models.errors import BaseUrlMissingError, SecretMissingError
2726

@@ -56,7 +55,6 @@ def __init__(
5655
self._attachments_service: Optional[AttachmentsService] = None
5756
self._connections_service: Optional[ConnectionsService] = None
5857

59-
setup_logging(debug)
6058
self._execution_context = ExecutionContext()
6159

6260
@property

src/uipath/_utils/_logs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ def setup_logging(should_debug: Optional[bool] = None) -> None:
1111
datefmt="%Y-%m-%d %H:%M:%S",
1212
)
1313
logger.setLevel(logging.DEBUG if should_debug else logging.INFO)
14-
logger.removeHandler(logging.StreamHandler(sys.stdout))
1514
logger.addHandler(logging.StreamHandler(sys.stderr))

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)