From 738f5ddda25589f2809a48799ada9c0bcc864c89 Mon Sep 17 00:00:00 2001 From: Cristian Pufu Date: Fri, 5 Dec 2025 18:30:27 +0200 Subject: [PATCH] fix: use chat runtime on run and debug --- pyproject.toml | 4 +-- .../_cli/_chat/{_protocol.py => _bridge.py} | 20 +++++++------ src/uipath/_cli/cli_debug.py | 28 +++++++++++++------ src/uipath/_cli/cli_run.py | 22 +++++++++++++-- uv.lock | 10 +++---- 5 files changed, 59 insertions(+), 25 deletions(-) rename src/uipath/_cli/_chat/{_protocol.py => _bridge.py} (95%) diff --git a/pyproject.toml b/pyproject.toml index ceb30ad40..9c420dcd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [project] name = "uipath" -version = "2.2.23" +version = "2.2.24" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ - "uipath-runtime>=0.2.2, <0.3.0", + "uipath-runtime>=0.2.3, <0.3.0", "click>=8.3.1", "httpx>=0.28.1", "pyjwt>=2.10.1", diff --git a/src/uipath/_cli/_chat/_protocol.py b/src/uipath/_cli/_chat/_bridge.py similarity index 95% rename from src/uipath/_cli/_chat/_protocol.py rename to src/uipath/_cli/_chat/_bridge.py index 8ffed568b..e2f44399d 100644 --- a/src/uipath/_cli/_chat/_protocol.py +++ b/src/uipath/_cli/_chat/_bridge.py @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) -class WebSocketChatBridge: +class SocketIOChatBridge: """WebSocket-based chat bridge for streaming conversational events to CAS. Implements UiPathChatBridgeProtocol using python-socketio library. @@ -221,8 +221,6 @@ async def _cleanup_client(self) -> None: def get_chat_bridge( context: UiPathRuntimeContext, - conversation_id: str, - exchange_id: str, ) -> UiPathChatProtocol: """Factory to get WebSocket chat bridge for conversational agents. @@ -245,6 +243,9 @@ def get_chat_bridge( await bridge.disconnect(conversation_id, exchange_id) ``` """ + assert context.conversation_id is not None, "conversation_id must be set in context" + assert context.exchange_id is not None, "exchange_id must be set in context" + # Extract host from UIPATH_URL base_url = os.environ.get("UIPATH_URL") if not base_url: @@ -259,7 +260,7 @@ def get_chat_bridge( host = parsed.netloc # Construct WebSocket URL for CAS - websocket_url = f"wss://{host}/autopilotforeveryone_/websocket_/socket.io?conversationId={conversation_id}" + websocket_url = f"wss://{host}/autopilotforeveryone_/websocket_/socket.io?conversationId={context.conversation_id}" # Build headers from context headers = { @@ -268,12 +269,15 @@ def get_chat_bridge( or os.environ.get("UIPATH_TENANT_ID", ""), "X-UiPath-Internal-AccountId": context.org_id or os.environ.get("UIPATH_ORGANIZATION_ID", ""), - "X-UiPath-ConversationId": conversation_id, + "X-UiPath-ConversationId": context.conversation_id, } - return WebSocketChatBridge( + return SocketIOChatBridge( websocket_url=websocket_url, - conversation_id=conversation_id, - exchange_id=exchange_id, + conversation_id=context.conversation_id, + exchange_id=context.exchange_id, headers=headers, ) + + +__all__ = ["get_chat_bridge"] diff --git a/src/uipath/_cli/cli_debug.py b/src/uipath/_cli/cli_debug.py index 7aa8120e3..17d1839f4 100644 --- a/src/uipath/_cli/cli_debug.py +++ b/src/uipath/_cli/cli_debug.py @@ -9,15 +9,17 @@ UiPathRuntimeFactoryRegistry, UiPathRuntimeProtocol, ) +from uipath.runtime.chat import UiPathChatProtocol, UiPathChatRuntime from uipath.runtime.debug import UiPathDebugBridgeProtocol, UiPathDebugRuntime +from uipath._cli._chat._bridge import get_chat_bridge +from uipath._cli._debug._bridge import get_debug_bridge from uipath._cli._utils._debug import setup_debugging from uipath._cli._utils._studio_project import StudioClient from uipath._utils._bindings import ResourceOverwritesContext from uipath.platform.common import UiPathConfig from uipath.tracing import LlmOpsHttpExporter -from ._debug._bridge import get_debug_bridge from ._utils._console import ConsoleLogger from .middlewares import Middlewares @@ -108,28 +110,36 @@ async def execute_debug_runtime(): command="debug", ) as ctx: runtime: UiPathRuntimeProtocol | None = None + chat_runtime: UiPathRuntimeProtocol | None = None debug_runtime: UiPathRuntimeProtocol | None = None factory: UiPathRuntimeFactoryProtocol | None = None try: trigger_poll_interval: float = 5.0 + factory = UiPathRuntimeFactoryRegistry.get(context=ctx) + + runtime = await factory.new_runtime( + entrypoint, ctx.conversation_id or ctx.job_id or "default" + ) + if ctx.job_id: trace_manager.add_span_exporter(LlmOpsHttpExporter()) trigger_poll_interval = ( 0.0 # Polling disabled for production jobs ) - - factory = UiPathRuntimeFactoryRegistry.get(context=ctx) - - runtime = await factory.new_runtime( - entrypoint, ctx.job_id or "default" - ) + if ctx.conversation_id and ctx.exchange_id: + chat_bridge: UiPathChatProtocol = get_chat_bridge( + context=ctx + ) + chat_runtime = UiPathChatRuntime( + delegate=runtime, chat_bridge=chat_bridge + ) debug_bridge: UiPathDebugBridgeProtocol = get_debug_bridge(ctx) debug_runtime = UiPathDebugRuntime( - delegate=runtime, + delegate=chat_runtime or runtime, debug_bridge=debug_bridge, trigger_poll_interval=trigger_poll_interval, ) @@ -155,6 +165,8 @@ async def execute_debug_runtime(): finally: if debug_runtime: await debug_runtime.dispose() + if chat_runtime: + await chat_runtime.dispose() if runtime: await runtime.dispose() if factory: diff --git a/src/uipath/_cli/cli_run.py b/src/uipath/_cli/cli_run.py index 9c253a93e..38c3d1d13 100644 --- a/src/uipath/_cli/cli_run.py +++ b/src/uipath/_cli/cli_run.py @@ -10,11 +10,13 @@ UiPathRuntimeResult, UiPathStreamOptions, ) +from uipath.runtime.chat import UiPathChatProtocol, UiPathChatRuntime from uipath.runtime.context import UiPathRuntimeContext from uipath.runtime.debug import UiPathDebugBridgeProtocol from uipath.runtime.errors import UiPathRuntimeError from uipath.runtime.events import UiPathRuntimeStateEvent +from uipath._cli._chat._bridge import get_chat_bridge from uipath._cli._debug._bridge import ConsoleDebugBridge from uipath._cli._utils._common import read_resource_overwrites_from_file from uipath._cli._utils._debug import setup_debugging @@ -122,6 +124,7 @@ async def debug_runtime( ctx: UiPathRuntimeContext, runtime: UiPathRuntimeProtocol ) -> UiPathRuntimeResult | None: debug_bridge: UiPathDebugBridgeProtocol = ConsoleDebugBridge() + await debug_bridge.emit_execution_started() options = UiPathStreamOptions(resume=resume) async for event in runtime.stream(ctx.get_input(), options=options): @@ -156,19 +159,34 @@ async def execute() -> None: ): with ctx: runtime: UiPathRuntimeProtocol | None = None + chat_runtime: UiPathRuntimeProtocol | None = None factory: UiPathRuntimeFactoryProtocol | None = None try: factory = UiPathRuntimeFactoryRegistry.get(context=ctx) runtime = await factory.new_runtime( - entrypoint, ctx.job_id or "default" + entrypoint, + ctx.conversation_id or ctx.job_id or "default", ) if ctx.job_id: trace_manager.add_span_exporter(LlmOpsHttpExporter()) - ctx.result = await execute_runtime(ctx, runtime) + + if ctx.conversation_id and ctx.exchange_id: + chat_bridge: UiPathChatProtocol = get_chat_bridge( + context=ctx + ) + chat_runtime = UiPathChatRuntime( + delegate=runtime, chat_bridge=chat_bridge + ) + + ctx.result = await execute_runtime( + ctx, chat_runtime or runtime + ) else: ctx.result = await debug_runtime(ctx, runtime) finally: + if chat_runtime: + await chat_runtime.dispose() if runtime: await runtime.dispose() if factory: diff --git a/uv.lock b/uv.lock index f20a7463c..f11a0e9ca 100644 --- a/uv.lock +++ b/uv.lock @@ -2477,7 +2477,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.2.23" +version = "2.2.24" source = { editable = "." } dependencies = [ { name = "click" }, @@ -2540,7 +2540,7 @@ requires-dist = [ { name = "rich", specifier = ">=14.2.0" }, { name = "tenacity", specifier = ">=9.0.0" }, { name = "truststore", specifier = ">=0.10.1" }, - { name = "uipath-runtime", specifier = ">=0.2.2,<0.3.0" }, + { name = "uipath-runtime", specifier = ">=0.2.3,<0.3.0" }, ] [package.metadata.requires-dev] @@ -2586,14 +2586,14 @@ wheels = [ [[package]] name = "uipath-runtime" -version = "0.2.2" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/9b/ba331e5e56693af12f81b080b1a35e2cfdded98c19efb6d4d1f12bdeb830/uipath_runtime-0.2.2.tar.gz", hash = "sha256:a1cf3854a70908d5ca3649d98d0c2d4f1f54b2ff85ecbc7f3ca409ccda66ea42", size = 94918, upload-time = "2025-12-05T12:54:26.078Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/50/fe3da51da1426d49e73ae316b3f83ad9ceba942c2b45ecbd9919ef7fd719/uipath_runtime-0.2.3.tar.gz", hash = "sha256:d2c993586ad6bfc35de5a224f90589276f45105086b5fd955fe9be4a284ba5fd", size = 95481, upload-time = "2025-12-06T08:13:22.968Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/64/64526009871cedb376c80f9bd74cc38774a2d9505322e9d873c6b34ec251/uipath_runtime-0.2.2-py3-none-any.whl", hash = "sha256:4d8f517337ac409fd4b3b0c92bbb8828364651f21f371869aa81d6f8d87e082b", size = 36359, upload-time = "2025-12-05T12:54:24.666Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a0/b1898a03c6a13edc98f7497a43edae342616e4084a7f49d51adfe0194be1/uipath_runtime-0.2.3-py3-none-any.whl", hash = "sha256:ae8f56de81f630ba719f832bd67ec548cea97540066820bb0a9bf77a592c5e63", size = 36468, upload-time = "2025-12-06T08:13:21.327Z" }, ] [[package]]