Skip to content

Commit 2bf6f0f

Browse files
authored
Merge pull request #21 from UiPath/fix/add_chat_panel
feat: add chat panel
2 parents 7393b53 + 328f79e commit 2bf6f0f

File tree

12 files changed

+568
-92
lines changed

12 files changed

+568
-92
lines changed

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ description = "UiPath Developer Console"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
77
dependencies = [
8-
"uipath-runtime>=0.1.0, <0.2.0",
8+
"uipath-core>=0.0.6, <0.1.0",
9+
"uipath-runtime>=0.1.2, <0.2.0",
910
"textual>=6.6.0, <7.0.0",
1011
"pyperclip>=1.11.0, <2.0.0",
1112
]

src/uipath/dev/__init__.py

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import json
55
from datetime import datetime
66
from pathlib import Path
7-
from typing import Any
7+
from typing import Any, cast
88

99
import pyperclip # type: ignore[import-untyped]
1010
from textual import on
@@ -18,7 +18,14 @@
1818
from uipath.dev.infrastructure import (
1919
patch_textual_stderr,
2020
)
21-
from uipath.dev.models import ExecutionRun, LogMessage, TraceMessage
21+
from uipath.dev.models import (
22+
ChatMessage,
23+
ExecutionMode,
24+
ExecutionRun,
25+
LogMessage,
26+
TraceMessage,
27+
)
28+
from uipath.dev.models.chat import get_user_message, get_user_message_event
2229
from uipath.dev.services import RunService
2330
from uipath.dev.ui.panels import NewRunPanel, RunDetailsPanel, RunHistoryPanel
2431

@@ -64,6 +71,7 @@ def __init__(
6471
on_run_updated=self._on_run_updated,
6572
on_log=self._on_log_for_ui,
6673
on_trace=self._on_trace_for_ui,
74+
on_chat=self._on_chat_for_ui,
6775
)
6876

6977
# Just defaults for convenience
@@ -96,9 +104,11 @@ async def on_button_pressed(self, event: Button.Pressed) -> None:
96104
if event.button.id == "new-run-btn":
97105
await self.action_new_run()
98106
elif event.button.id == "execute-btn":
99-
await self.action_execute_run()
107+
await self.action_execute_run(mode=ExecutionMode.RUN)
100108
elif event.button.id == "debug-btn":
101-
await self.action_debug_run()
109+
await self.action_execute_run(mode=ExecutionMode.DEBUG)
110+
elif event.button.id == "chat-btn":
111+
await self.action_execute_run(mode=ExecutionMode.CHAT)
102112
elif event.button.id == "cancel-btn":
103113
await self.action_cancel()
104114
elif event.button.id == "debug-step-btn":
@@ -135,9 +145,23 @@ async def handle_chat_input(self, event: Input.Submitted) -> None:
135145
return
136146

137147
if details_panel.current_run.status == "suspended":
138-
details_panel.current_run.resume_data = {"message": user_text}
148+
details_panel.current_run.resume_data = {"value": user_text}
149+
else:
150+
msg = get_user_message(user_text)
151+
msg_ev = get_user_message_event(
152+
user_text, conversation_id=details_panel.current_run.id
153+
)
154+
self._on_chat_for_ui(
155+
ChatMessage(
156+
event=msg_ev,
157+
message=msg,
158+
run_id=details_panel.current_run.id,
159+
)
160+
)
161+
details_panel.current_run.input_data = {"messages": [msg]}
139162

140163
asyncio.create_task(self._execute_runtime(details_panel.current_run))
164+
141165
event.input.clear()
142166

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

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

160184
if not entrypoint:
161185
return
@@ -165,7 +189,7 @@ async def action_execute_run(self) -> None:
165189
except json.JSONDecodeError:
166190
return
167191

168-
run = ExecutionRun(entrypoint, input_payload, conversational)
192+
run = ExecutionRun(entrypoint, input_payload, mode=mode)
169193

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

175199
self._show_run_details(run)
176200

177-
if not run.conversational:
178-
asyncio.create_task(self._execute_runtime(run))
179-
else:
201+
if mode == ExecutionMode.CHAT:
180202
self._focus_chat_input()
181-
182-
async def action_debug_run(self) -> None:
183-
"""Execute a new run in debug mode (step-by-step)."""
184-
new_run_panel = self.query_one("#new-run-panel", NewRunPanel)
185-
entrypoint, input_data, conversational = new_run_panel.get_input_values()
186-
187-
if not entrypoint:
188-
return
189-
190-
try:
191-
input_payload: dict[str, Any] = json.loads(input_data)
192-
except json.JSONDecodeError:
193-
return
194-
195-
# Create run with debug=True
196-
run = ExecutionRun(entrypoint, input_payload, conversational, debug=True)
197-
198-
history_panel = self.query_one("#history-panel", RunHistoryPanel)
199-
history_panel.add_run(run)
200-
201-
self.run_service.register_run(run)
202-
203-
self._show_run_details(run)
204-
205-
# start execution in debug mode (it will pause immediately)
206-
asyncio.create_task(self._execute_runtime(run))
203+
else:
204+
asyncio.create_task(self._execute_runtime(run))
207205

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

268+
def _on_chat_for_ui(
269+
self,
270+
chat_msg: ChatMessage,
271+
) -> None:
272+
"""Append/refresh chat messages in the UI."""
273+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
274+
details_panel.add_chat_message(chat_msg)
275+
270276
def _show_run_details(self, run: ExecutionRun) -> None:
271277
"""Show details panel for a specific run."""
272278
new_panel = self.query_one("#new-run-panel")
@@ -289,7 +295,9 @@ def _add_subprocess_log(self, level: str, message: str) -> None:
289295

290296
def add_log() -> None:
291297
details_panel = self.query_one("#details-panel", RunDetailsPanel)
292-
run = getattr(details_panel, "current_run", None)
298+
run: ExecutionRun = cast(
299+
ExecutionRun, getattr(details_panel, "current_run", None)
300+
)
293301
if run:
294302
log_msg = LogMessage(run.id, level, message, datetime.now())
295303
# Route through RunService so state + UI stay in sync

src/uipath/dev/models/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""UiPath Dev Console models module."""
22

3-
from uipath.dev.models.execution import ExecutionRun
4-
from uipath.dev.models.messages import LogMessage, TraceMessage
3+
from uipath.dev.models.execution import ExecutionMode, ExecutionRun
4+
from uipath.dev.models.messages import ChatMessage, LogMessage, TraceMessage
55

66
__all__ = [
77
"ExecutionRun",
8+
"ExecutionMode",
9+
"ChatMessage",
810
"LogMessage",
911
"TraceMessage",
1012
]

0 commit comments

Comments
 (0)