Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.

Commit 65d909e

Browse files
author
Valentina Bojan
committed
fix(guardrails): allow guardrail enums to be deserialized with upper/lowercase first letter
1 parent de6ed2a commit 65d909e

4 files changed

Lines changed: 83 additions & 4 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-core"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
description = "UiPath Core abstractions"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

src/uipath/core/guardrails/guardrails.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
"""Guardrails models for UiPath Platform."""
22

33
from enum import Enum
4-
from typing import Annotated, Callable, Literal
4+
from typing import Annotated, Any, Callable, Literal
55

6-
from pydantic import BaseModel, ConfigDict, Field
6+
from pydantic import BaseModel, ConfigDict, Field, field_validator
7+
8+
9+
def _decapitalize_first_letter(s: str) -> str:
10+
"""Convert first letter to lowercase (e.g., 'SimpleText' -> 'simpleText')."""
11+
if not s or len(s) == 0:
12+
return s
13+
if len(s) == 1:
14+
return s.lower()
15+
return s[0].lower() + s[1:]
716

817

918
class GuardrailValidationResultType(str, Enum):
@@ -56,6 +65,12 @@ class FieldReference(BaseModel):
5665

5766
model_config = ConfigDict(populate_by_name=True, extra="allow")
5867

68+
@field_validator("source", mode="before")
69+
@classmethod
70+
def normalize_type(cls, v: Any) -> Any:
71+
"""Normalize type by decapitalizing first letter."""
72+
return _decapitalize_first_letter(v) if isinstance(v, str) else v
73+
5974

6075
class SelectorType(str, Enum):
6176
"""Selector type enumeration."""
@@ -72,6 +87,17 @@ class AllFieldsSelector(BaseModel):
7287

7388
model_config = ConfigDict(populate_by_name=True, extra="allow")
7489

90+
@field_validator("sources", mode="before")
91+
@classmethod
92+
def normalize_sources(cls, v: Any) -> Any:
93+
"""Normalize sources by decapitalizing first letter of each item."""
94+
if isinstance(v, list):
95+
return [
96+
_decapitalize_first_letter(item) if isinstance(item, str) else item
97+
for item in v
98+
]
99+
return v
100+
75101

76102
class SpecificFieldsSelector(BaseModel):
77103
"""Specific fields selector."""
@@ -128,6 +154,12 @@ class UniversalRule(BaseModel):
128154

129155
model_config = ConfigDict(populate_by_name=True, extra="allow")
130156

157+
@field_validator("apply_to", mode="before")
158+
@classmethod
159+
def normalize_type(cls, v: Any) -> Any:
160+
"""Normalize type by decapitalizing first letter."""
161+
return _decapitalize_first_letter(v) if isinstance(v, str) else v
162+
131163

132164
class NumberRule(BaseModel):
133165
"""Number rule model."""
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Tests for guardrails models normalization validators."""
2+
3+
from typing import TYPE_CHECKING
4+
5+
from uipath.core.guardrails import (
6+
AllFieldsSelector,
7+
FieldReference,
8+
FieldSource,
9+
)
10+
11+
if TYPE_CHECKING:
12+
pass
13+
14+
15+
class TestGuardrailsModelsNormalization:
16+
"""Test guardrails models field normalization."""
17+
18+
def test_field_reference_normalizes_capitalized_source(
19+
self,
20+
) -> None:
21+
"""Test that FieldReference normalizes capitalized source values to lowercase."""
22+
# Create FieldReference with capitalized "Input" - should normalize to FieldSource.INPUT
23+
field_ref = FieldReference(path="testField", source="Input")
24+
assert field_ref.source == FieldSource.INPUT
25+
26+
# Create FieldReference with capitalized "Output" - should normalize to FieldSource.OUTPUT
27+
field_ref = FieldReference(path="testField", source="Output")
28+
assert field_ref.source == FieldSource.OUTPUT
29+
30+
# Create FieldReference with lowercase "input" - should work as-is
31+
field_ref = FieldReference(path="testField", source="input")
32+
assert field_ref.source == FieldSource.INPUT
33+
34+
def test_all_fields_selector_normalizes_capitalized_sources(
35+
self,
36+
) -> None:
37+
"""Test that AllFieldsSelector normalizes capitalized source values in the list."""
38+
# Create AllFieldsSelector with capitalized "Input" and "Output" - should normalize
39+
selector = AllFieldsSelector(selector_type="all", sources=["Input", "Output"])
40+
assert FieldSource.INPUT in selector.sources
41+
assert FieldSource.OUTPUT in selector.sources
42+
assert len(selector.sources) == 2
43+
44+
# Create AllFieldsSelector with mixed case - should normalize all
45+
selector = AllFieldsSelector(selector_type="all", sources=["Input", "output"])
46+
assert FieldSource.INPUT in selector.sources
47+
assert FieldSource.OUTPUT in selector.sources

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)