Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ description = "UiPath Developer Console"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
dependencies = [
"uipath-runtime>=0.1.0, <0.2.0",
"uipath-core>=0.0.6, <0.1.0",
"uipath-runtime>=0.1.2, <0.2.0",
"textual>=6.6.0, <7.0.0",
"pyperclip>=1.11.0, <2.0.0",
]
Expand Down
84 changes: 46 additions & 38 deletions src/uipath/dev/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import json
from datetime import datetime
from pathlib import Path
from typing import Any
from typing import Any, cast

import pyperclip # type: ignore[import-untyped]
from textual import on
Expand All @@ -18,7 +18,14 @@
from uipath.dev.infrastructure import (
patch_textual_stderr,
)
from uipath.dev.models import ExecutionRun, LogMessage, TraceMessage
from uipath.dev.models import (
ChatMessage,
ExecutionMode,
ExecutionRun,
LogMessage,
TraceMessage,
)
from uipath.dev.models.chat import get_user_message, get_user_message_event
from uipath.dev.services import RunService
from uipath.dev.ui.panels import NewRunPanel, RunDetailsPanel, RunHistoryPanel

Expand Down Expand Up @@ -64,6 +71,7 @@ def __init__(
on_run_updated=self._on_run_updated,
on_log=self._on_log_for_ui,
on_trace=self._on_trace_for_ui,
on_chat=self._on_chat_for_ui,
)

# Just defaults for convenience
Expand Down Expand Up @@ -96,9 +104,11 @@ async def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "new-run-btn":
await self.action_new_run()
elif event.button.id == "execute-btn":
await self.action_execute_run()
await self.action_execute_run(mode=ExecutionMode.RUN)
elif event.button.id == "debug-btn":
await self.action_debug_run()
await self.action_execute_run(mode=ExecutionMode.DEBUG)
elif event.button.id == "chat-btn":
await self.action_execute_run(mode=ExecutionMode.CHAT)
elif event.button.id == "cancel-btn":
await self.action_cancel()
elif event.button.id == "debug-step-btn":
Expand Down Expand Up @@ -135,9 +145,23 @@ async def handle_chat_input(self, event: Input.Submitted) -> None:
return

if details_panel.current_run.status == "suspended":
details_panel.current_run.resume_data = {"message": user_text}
details_panel.current_run.resume_data = {"value": user_text}
else:
msg = get_user_message(user_text)
msg_ev = get_user_message_event(
user_text, conversation_id=details_panel.current_run.id
)
self._on_chat_for_ui(
ChatMessage(
event=msg_ev,
message=msg,
run_id=details_panel.current_run.id,
)
)
details_panel.current_run.input_data = {"messages": [msg]}

asyncio.create_task(self._execute_runtime(details_panel.current_run))

event.input.clear()

async def action_new_run(self) -> None:
Expand All @@ -152,10 +176,10 @@ async def action_cancel(self) -> None:
"""Cancel and return to new run view."""
await self.action_new_run()

async def action_execute_run(self) -> None:
async def action_execute_run(self, mode: ExecutionMode) -> None:
"""Execute a new run based on NewRunPanel inputs."""
new_run_panel = self.query_one("#new-run-panel", NewRunPanel)
entrypoint, input_data, conversational = new_run_panel.get_input_values()
entrypoint, input_data = new_run_panel.get_input_values()

if not entrypoint:
return
Expand All @@ -165,7 +189,7 @@ async def action_execute_run(self) -> None:
except json.JSONDecodeError:
return

run = ExecutionRun(entrypoint, input_payload, conversational)
run = ExecutionRun(entrypoint, input_payload, mode=mode)

history_panel = self.query_one("#history-panel", RunHistoryPanel)
history_panel.add_run(run)
Expand All @@ -174,36 +198,10 @@ async def action_execute_run(self) -> None:

self._show_run_details(run)

if not run.conversational:
asyncio.create_task(self._execute_runtime(run))
else:
if mode == ExecutionMode.CHAT:
self._focus_chat_input()

async def action_debug_run(self) -> None:
"""Execute a new run in debug mode (step-by-step)."""
new_run_panel = self.query_one("#new-run-panel", NewRunPanel)
entrypoint, input_data, conversational = new_run_panel.get_input_values()

if not entrypoint:
return

try:
input_payload: dict[str, Any] = json.loads(input_data)
except json.JSONDecodeError:
return

# Create run with debug=True
run = ExecutionRun(entrypoint, input_payload, conversational, debug=True)

history_panel = self.query_one("#history-panel", RunHistoryPanel)
history_panel.add_run(run)

self.run_service.register_run(run)

self._show_run_details(run)

# start execution in debug mode (it will pause immediately)
asyncio.create_task(self._execute_runtime(run))
else:
asyncio.create_task(self._execute_runtime(run))

async def action_debug_step(self) -> None:
"""Step to next breakpoint in debug mode."""
Expand Down Expand Up @@ -267,6 +265,14 @@ def _on_trace_for_ui(self, trace_msg: TraceMessage) -> None:
details_panel = self.query_one("#details-panel", RunDetailsPanel)
details_panel.add_trace(trace_msg)

def _on_chat_for_ui(
self,
chat_msg: ChatMessage,
) -> None:
"""Append/refresh chat messages in the UI."""
details_panel = self.query_one("#details-panel", RunDetailsPanel)
details_panel.add_chat_message(chat_msg)

def _show_run_details(self, run: ExecutionRun) -> None:
"""Show details panel for a specific run."""
new_panel = self.query_one("#new-run-panel")
Expand All @@ -289,7 +295,9 @@ def _add_subprocess_log(self, level: str, message: str) -> None:

def add_log() -> None:
details_panel = self.query_one("#details-panel", RunDetailsPanel)
run = getattr(details_panel, "current_run", None)
run: ExecutionRun = cast(
ExecutionRun, getattr(details_panel, "current_run", None)
)
if run:
log_msg = LogMessage(run.id, level, message, datetime.now())
# Route through RunService so state + UI stay in sync
Expand Down
6 changes: 4 additions & 2 deletions src/uipath/dev/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""UiPath Dev Console models module."""

from uipath.dev.models.execution import ExecutionRun
from uipath.dev.models.messages import LogMessage, TraceMessage
from uipath.dev.models.execution import ExecutionMode, ExecutionRun
from uipath.dev.models.messages import ChatMessage, LogMessage, TraceMessage

__all__ = [
"ExecutionRun",
"ExecutionMode",
"ChatMessage",
"LogMessage",
"TraceMessage",
]
Loading