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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-langchain"
version = "0.4.0"
version = "0.4.1"
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"
Expand Down
22 changes: 20 additions & 2 deletions src/uipath_langchain/agent/guardrails/guardrail_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,28 @@ def _create_validation_command(
Returns:
Command to update state and route to appropriate node.
"""
if not result.validation_passed:
from uipath.core.guardrails import GuardrailValidationResultType

# Handle new format: result has a 'result' attribute with GuardrailValidationResultType
if hasattr(result, "result"):
if result.result != GuardrailValidationResultType.PASSED:
return Command(
goto=failure_node, update={"guardrail_validation_result": result.reason}
)
# Handle old format: backwards compatibility for tests using validation_passed
elif hasattr(result, "validation_passed"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this check if we don't mind backwards compatibility for now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove it in the next PR. Now I want to fix build issues and test failures as fast as possible. Upgrading uipath-core to 0.1.6 is not that easy.
Let's unblock everyone for now

if not result.validation_passed:
return Command(
goto=failure_node, update={"guardrail_validation_result": result.reason}
)
else:
# Fallback: assume failure if format is unknown
return Command(
goto=failure_node, update={"guardrail_validation_result": result.reason}
goto=failure_node,
update={"guardrail_validation_result": "Unknown validation result format"},
)

# Update guardail trace with skip/reason
return Command(goto=success_node, update={"guardrail_validation_result": None})


Expand Down
10 changes: 8 additions & 2 deletions tests/cli/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from unittest.mock import patch

import pytest
from uipath.core.guardrails import GuardrailValidationResult
from uipath.core.guardrails import (
GuardrailValidationResult,
GuardrailValidationResultType,
)


@pytest.fixture
Expand All @@ -19,7 +22,10 @@ def mock_guardrails_service():

def mock_evaluate_guardrail(text, guardrail):
"""Mock guardrail evaluation - always passes validation."""
return GuardrailValidationResult(validation_passed=True, reason="")
return GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED,
reason="",
)

with patch(
"uipath.platform.guardrails.GuardrailsService.evaluate_guardrail",
Expand Down
38 changes: 27 additions & 11 deletions tests/cli/test_agent_with_guardrails.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@

import pytest
from langchain_core.messages import AIMessage
from uipath.core.guardrails import GuardrailValidationResult
from uipath.core.guardrails import (
GuardrailValidationResult,
GuardrailValidationResultType,
)
from uipath.runtime import (
UiPathExecuteOptions,
UiPathRuntimeContext,
Expand Down Expand Up @@ -288,12 +291,13 @@ def mock_evaluate_guardrail(text, guardrail):
and ".com" in text
):
return GuardrailValidationResult(
validation_passed=False, # PII detected - should trigger block!
result=GuardrailValidationResultType.VALIDATION_FAILED,
reason="PII detected in text",
)
else:
return GuardrailValidationResult(
validation_passed=True, reason=""
result=GuardrailValidationResultType.PASSED,
reason="",
)

with (
Expand Down Expand Up @@ -422,12 +426,15 @@ def mock_evaluate_guardrail(text, guardrail):
# Prompt injection guardrail should detect and block
if guardrail.name == "Prompt injection guardrail":
return GuardrailValidationResult(
validation_passed=False,
result=GuardrailValidationResultType.VALIDATION_FAILED,
reason="Prompt injection detected",
)

# All other guardrails pass
return GuardrailValidationResult(validation_passed=True, reason="")
return GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED,
reason="",
)

# Mock LLM - should NOT be called if guardrail blocks at LLM level
async def mock_llm_invoke(*args, **kwargs):
Expand Down Expand Up @@ -867,12 +874,15 @@ def mock_evaluate_guardrail(text, guardrail):
and ".com" in text
):
return GuardrailValidationResult(
validation_passed=False,
result=GuardrailValidationResultType.VALIDATION_FAILED,
reason="PII detected in tool input",
)

# All other guardrails pass
return GuardrailValidationResult(validation_passed=True, reason="")
return GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED,
reason="",
)

# Mock LLM responses - tool call contains an email address
async def mock_llm_invoke(*args, **kwargs):
Expand Down Expand Up @@ -1025,12 +1035,15 @@ def mock_evaluate_guardrail(text, guardrail):
and ".com" in text
):
return GuardrailValidationResult(
validation_passed=False,
result=GuardrailValidationResultType.VALIDATION_FAILED,
reason="PII detected in LLM output",
)

# All other guardrails pass
return GuardrailValidationResult(validation_passed=True, reason="")
return GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED,
reason="",
)

# Mock LLM responses - LLM output contains PII in tool call args
async def mock_llm_invoke(*args, **kwargs):
Expand Down Expand Up @@ -1228,12 +1241,15 @@ def mock_evaluate_guardrail(text, guardrail):
and ".com" in text
):
return GuardrailValidationResult(
validation_passed=False,
result=GuardrailValidationResultType.VALIDATION_FAILED,
reason="PII detected in LLM output",
)

# All other guardrails pass
return GuardrailValidationResult(validation_passed=True, reason="")
return GuardrailValidationResult(
result=GuardrailValidationResultType.PASSED,
reason="",
)

# Mock LLM responses - LLM output contains PII in tool call args
async def mock_llm_invoke(*args, **kwargs):
Expand Down
9 changes: 5 additions & 4 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.