From 5b263df641a52aa4cf2ef7f776e237b916bc8d93 Mon Sep 17 00:00:00 2001 From: iliescucristian Date: Mon, 12 Jan 2026 12:13:43 +0200 Subject: [PATCH 1/2] feat: improve hitl details --- .../guardrails/actions/escalate_action.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/uipath_langchain/agent/guardrails/actions/escalate_action.py b/src/uipath_langchain/agent/guardrails/actions/escalate_action.py index a3e79d20..e929ea63 100644 --- a/src/uipath_langchain/agent/guardrails/actions/escalate_action.py +++ b/src/uipath_langchain/agent/guardrails/actions/escalate_action.py @@ -7,7 +7,8 @@ from langchain_core.messages import AIMessage, AnyMessage, BaseMessage, ToolMessage from langgraph.types import Command, interrupt -from uipath.platform.common import CreateEscalation +from uipath._utils import UiPathUrl +from uipath.platform.common import CreateEscalation, UiPathConfig from uipath.platform.guardrails import ( BaseGuardrail, GuardrailScope, @@ -76,10 +77,13 @@ async def _node( # Validate message count based on execution stage _validate_message_count(state, execution_stage) + (redirect_url, tenant_name) = _get_agent_execution_viewer_url() # Build base data dictionary with common fields data: Dict[str, Any] = { "GuardrailName": guardrail.name, "GuardrailDescription": guardrail.description, + "TenantName": tenant_name, + "AgentTrace": redirect_url, "Component": scope.name.lower(), "ExecutionStage": _execution_stage_to_string(execution_stage), "GuardrailResult": state.guardrail_validation_result, @@ -624,3 +628,40 @@ def _execution_stage_to_string( if execution_stage == ExecutionStage.PRE_EXECUTION: return "PreExecution" return "PostExecution" + + +def _get_agent_execution_viewer_url() -> tuple[str, str]: + """Generate the agent execution viewer URL based on execution context. + + Args: + cloud_base_url: Optional cloud base URL. If not provided, will be extracted from environment. + + Returns: + The constructed viewer URL for the agent execution. + + Note: + Currently uses hardcoded values for agentId and packageVersion. + These should be made configurable in the future. + """ + cloud_base_url = UiPathConfig.base_url + uiPath_Url = UiPathUrl(cloud_base_url) + tenant_name = uiPath_Url.tenant_name + organization_id = UiPathConfig.organization_id + agent_id = UiPathConfig.project_id + + # Route to appropriate URL based on source + if UiPathConfig.is_studio_project: + return ( + f"{uiPath_Url.base_url}/{organization_id}/studio_/designer/{agent_id}", + tenant_name, + ) + else: + execution_folder_id = UiPathConfig.folder_key + process_uuid = UiPathConfig.process_uuid + trace_id = UiPathConfig.trace_id + package_version = UiPathConfig.process_version + + return ( + f"{uiPath_Url.base_url}/{organization_id}/agents_/deployed/{execution_folder_id}/{process_uuid}/{agent_id}/{package_version}/traces/{trace_id}", + tenant_name, + ) From 5aa7ade7f11bb7ec17abc683e9b8e0235d7f09e2 Mon Sep 17 00:00:00 2001 From: iliescucristian Date: Mon, 12 Jan 2026 12:53:38 +0200 Subject: [PATCH 2/2] feat: address comments --- pyproject.toml | 2 +- .../guardrails/actions/escalate_action.py | 48 +++++++++++-------- uv.lock | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 217c57e1..76739577 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-langchain" -version = "0.4.4" +version = "0.4.5" description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/src/uipath_langchain/agent/guardrails/actions/escalate_action.py b/src/uipath_langchain/agent/guardrails/actions/escalate_action.py index e929ea63..07307a63 100644 --- a/src/uipath_langchain/agent/guardrails/actions/escalate_action.py +++ b/src/uipath_langchain/agent/guardrails/actions/escalate_action.py @@ -77,18 +77,21 @@ async def _node( # Validate message count based on execution stage _validate_message_count(state, execution_stage) - (redirect_url, tenant_name) = _get_agent_execution_viewer_url() # Build base data dictionary with common fields data: Dict[str, Any] = { "GuardrailName": guardrail.name, "GuardrailDescription": guardrail.description, - "TenantName": tenant_name, - "AgentTrace": redirect_url, "Component": scope.name.lower(), "ExecutionStage": _execution_stage_to_string(execution_stage), "GuardrailResult": state.guardrail_validation_result, } + # Add tenant and trace URL if base_url is configured + cloud_base_url = UiPathConfig.base_url + if cloud_base_url is not None: + data["TenantName"] = _get_tenant_name(cloud_base_url) + data["AgentTrace"] = _get_agent_execution_viewer_url(cloud_base_url) + # Add stage-specific fields if execution_stage == ExecutionStage.PRE_EXECUTION: # PRE_EXECUTION: Only Inputs field from last message @@ -630,38 +633,43 @@ def _execution_stage_to_string( return "PostExecution" -def _get_agent_execution_viewer_url() -> tuple[str, str]: - """Generate the agent execution viewer URL based on execution context. +def _get_tenant_name(cloud_base_url: str) -> str: + """Extract the tenant name from the UiPath base URL. Args: - cloud_base_url: Optional cloud base URL. If not provided, will be extracted from environment. + cloud_base_url: The UiPath cloud base URL to extract tenant name from. Returns: - The constructed viewer URL for the agent execution. + str: The tenant name extracted from the base URL. + """ + uiPath_Url = UiPathUrl(cloud_base_url) + return uiPath_Url.tenant_name + + +def _get_agent_execution_viewer_url(cloud_base_url: str) -> str: + """Generate the agent execution viewer URL based on execution context. - Note: - Currently uses hardcoded values for agentId and packageVersion. - These should be made configurable in the future. + Constructs the appropriate URL for viewing agent execution traces. The URL format + depends on whether the agent is running in a studio project (development) or + deployed (production) context. + + Args: + cloud_base_url: The UiPath cloud base URL to use for constructing the viewer URL. + + Returns: + str: The constructed agent execution viewer URL. """ - cloud_base_url = UiPathConfig.base_url uiPath_Url = UiPathUrl(cloud_base_url) - tenant_name = uiPath_Url.tenant_name organization_id = UiPathConfig.organization_id agent_id = UiPathConfig.project_id # Route to appropriate URL based on source if UiPathConfig.is_studio_project: - return ( - f"{uiPath_Url.base_url}/{organization_id}/studio_/designer/{agent_id}", - tenant_name, - ) + return f"{uiPath_Url.base_url}/{organization_id}/studio_/designer/{agent_id}" else: execution_folder_id = UiPathConfig.folder_key process_uuid = UiPathConfig.process_uuid trace_id = UiPathConfig.trace_id package_version = UiPathConfig.process_version - return ( - f"{uiPath_Url.base_url}/{organization_id}/agents_/deployed/{execution_folder_id}/{process_uuid}/{agent_id}/{package_version}/traces/{trace_id}", - tenant_name, - ) + return f"{uiPath_Url.base_url}/{organization_id}/agents_/deployed/{execution_folder_id}/{process_uuid}/{agent_id}/{package_version}/traces/{trace_id}" diff --git a/uv.lock b/uv.lock index 0b5ca9e4..8d54c2bb 100644 --- a/uv.lock +++ b/uv.lock @@ -3297,7 +3297,7 @@ wheels = [ [[package]] name = "uipath-langchain" -version = "0.4.4" +version = "0.4.5" source = { editable = "." } dependencies = [ { name = "aiosqlite" },