Skip to content

Conversation

@apetraru-uipath
Copy link
Contributor

@apetraru-uipath apetraru-uipath commented Jan 20, 2026

Input Modification Support for Guardrail Actions

Summary

This PR implements the ability for guardrail actions to modify input/output data before execution continues, enabling sanitization and filtering capabilities beyond just logging or blocking.

Key Changes

1. Enhanced GuardrailAction Interface

  • Updated handle_validation_result() to return str | dict[str, Any] | None instead of None
  • Actions can now return modified data to replace the original input/output
  • Backward compatible: existing actions (LogAction, BlockAction) return None

2. Helper Functions for Data Modification

  • _create_modified_tool_request(): Creates new ToolCallRequest with modified tool arguments
  • _create_modified_tool_result(): Creates new ToolMessage/Command with modified tool output

3. Middleware Updates

All guardrail middleware classes now support input/output modification:

  • UiPathDeterministicGuardrail: Supports pre-execution (tool input) and post-execution (tool output) modifications
  • UiPathPIIDetection: Supports tool input modification and message content modification (Agent/LLM scopes)
  • UiPathPromptInjection: Supports message content modification (LLM scope)

4. Custom FilterAction Example

  • Added CustomFilterAction in samples/joke-agent/middleware.py demonstrating how developers can implement custom actions that filter/modify data
  • Example filters offensive words from tool input, replacing them with ***

Use Cases

  • Data Sanitization: Filter sensitive or offensive content before tool execution
  • Input Normalization: Transform input data to meet tool requirements
  • Output Filtering: Sanitize tool output before it's returned to the agent
  • Message Modification: Clean message content in Agent/LLM scopes

Example

from middleware import CustomFilterAction

guardrail = UiPathDeterministicGuardrail(
    tool_names=[analyze_joke_syntax],
    rules=[lambda input_data: "donkey" in input_data.get("joke", "").lower()],
    action=CustomFilterAction(
        word_to_filter="donkey",
        replacement="***",
    ),
    name="Joke Content Validator",
)

When the guardrail detects "donkey" in the tool input, it automatically filters it to "***" before the tool receives the input.

Testing

  • Verified tool input modification works correctly
  • Verified tool output modification works correctly
  • Verified message content modification works for Agent/LLM scopes
  • Backward compatibility confirmed with existing LogAction and BlockAction

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch 11 times, most recently from ff0e42a to 5ea422d Compare January 26, 2026 13:40
authors = [{ name = "Andrei Petraru", email = "andrei.petraru@uipath.com" }]
requires-python = ">=3.11"
dependencies = [
"uipath-langchain>=0.5.1, <0.6.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

After I merge this PR, I should modify to "uipath-langchain>=0.5.2"

message will be generated.
"""

severity_level: AgentGuardrailSeverityLevel = AgentGuardrailSeverityLevel.WARNING
Copy link
Contributor

Choose a reason for hiding this comment

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

do you think we can use a enum LogActionSeverity one of logging.ERROR, logging.WARNING or logging.INFO instead of using AgentGuardrailSeverityLevel which si coupled by agent.json ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Introduced a new enum that resides in langchain project

GuardrailExecutionStage.PRE,
GuardrailExecutionStage.PRE_AND_POST,
):
# At PRE stage, evaluate all rules with input_data
Copy link
Contributor

Choose a reason for hiding this comment

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

can we extract this in a function? (same for the if block bellow)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

# 1-parameter rules receive output_data (tool output)
# 2-parameter rules receive both input_data and output_data
if middleware_instance.rules:
result = middleware_instance._evaluate_rules(
Copy link
Contributor

Choose a reason for hiding this comment

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

i think here would be more readable if we pass PRE/POST + input_data && output_data instead of sending same param 2 times, it may be confusing with the current signature when you read for the first time

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch 9 times, most recently from 91dbe8d to 6208ed2 Compare January 27, 2026 15:40
Entity(PIIDetectionEntity.CREDIT_CARD_NUMBER, 0.5),
Entity(PIIDetectionEntity.PHONE_NUMBER, 0.5),
],
tool_names=[analyze_joke_syntax],
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we call this "tools" instead of "tool_names"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good proposal, I had same question. Let's change to "tools", just forget about low-coded agent namings

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch from 6208ed2 to 64c6f04 Compare January 27, 2026 17:00
@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch 6 times, most recently from 18a6234 to 02db432 Compare January 28, 2026 10:10
UiPathPromptInjectionMiddleware,
)

__all__ = [
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 file? why would we have backwards incompatibility when we haven't published this version at all until 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.

Right, it can be removed

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch 2 times, most recently from 9cd9ee1 to bd3aa37 Compare January 28, 2026 12:27
@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_middleware_poc branch from bd3aa37 to c7a5061 Compare January 28, 2026 12:28
@apetraru-uipath apetraru-uipath merged commit 4a54ac0 into main Jan 28, 2026
39 checks passed
@apetraru-uipath apetraru-uipath deleted the feat/guardrails_middleware_poc branch January 28, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants