generated from guardrails-ai/validator-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
191 lines (153 loc) · 6.94 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- coding: UTF-8 -*-
# SPDX-License-Identifier: MIT
# COPYRIGHT: (c) 2024-now jenisys
# CreatedBy: jenisys
# Created: 2024-06-22
# SEE: https://spdx.org/licenses/MIT.html
"""
This module provides a ``CucumberExpressionMatch`` class,
a simple validator of [cucumber-expressions] for [guardrails].
[cucumber-expressions]: https://github.com/cucumber/cucumber-expressions
[guardrails]: https://github.com/guardrails-ai/guardrails
"""
# BASED ON: https://github.com/guardrails-ai/validator-template
from typing import Any, Callable, Dict, List, Optional
from cucumber_expressions.expression import CucumberExpression
from cucumber_expressions.parameter_type import ParameterType
from cucumber_expressions.parameter_type_registry import ParameterTypeRegistry
from guardrails import OnFailAction
from guardrails.validator_base import (
FailResult,
PassResult,
ValidationResult,
Validator,
register_validator,
)
import rstr
import pytest
# -----------------------------------------------------------------------------
# PACKAGE METADATA
# -----------------------------------------------------------------------------
__version__ = "0.2.1"
__license__ = "MIT"
# -----------------------------------------------------------------------------
# GUARDRAILS VALIDATOR
# -----------------------------------------------------------------------------
@register_validator(name="guardrails/cucumber_expression_match", data_type="string")
class CucumberExpressionMatch(Validator):
r"""
# Overview
| Developed by | jenisys |
|---------------------|----------------|
| Date of development | 2024-06-22 |
| Validator type | rule-following |
| License | MIT |
| Input/Output | Output |
# Description
This [guardrails] validator provides support for [cucumber-expressions],
a simpler, more readable "regular expression" dialect with the following features:
* Supports built-in `parameter_type`(s) for common types, like: `{int}`, `{float}`, ...
* Supports user-defined `parameter_type`(s) with own `regular expressions`,
type conversion and transformation
* Supports alternative text, like: `apple/orange` (matches: `apple` or `orange`)
* Support optional text, like: `apple(s)` (matches: `apple`, `apples`)
This validator is similar to [regex_match],
but often easier to use because:
* [cucumber-expressions] are often more readable
(especially for people that are less familiar with `regular expressions`)
* less error-prone, because a readable placeholder(s) are used for the complex regular expression.
SEE ALSO:
* [cucumber-expressions]
* [guardrails]
[cucumber-expressions]: https://github.com/cucumber/cucumber-expressions
[guardrails]: https://github.com/guardrails-ai/guardrails
[regex_match]: https://github.com/guardrails-ai/regex_match
## Intended Use
Check if text follows a specified schema (described by this cucumber-expression).
## Requirements
* Dependencies:
- guardrails-ai>=0.5.0
- cucumber-expressions>=17.1.0
- rstr>=3.2.2
* Dev Dependencies:
- pytest
- pyright
- ruff
- codespell
# Installation
```bash
$ guardrails hub install hub://guardrails/cucumber_expression_match
```
# Usage Examples
## Validating string output via Python
In this example, we apply the validator to a string output generated by an LLM.
```python
# -- FILE: use_guardrails_cucumber_expression_match.py
from guardrails import Guard, OnFailAction
from guardrails.hub import CucumberExpressionMatch
from cucumber_expressions.parameter_type import ParameterType
# -- SETUP GUARD:
positive_number = ParameterType("positive_number", regexp=r"\d+", type=int)
guard = Guard().use(CucumberExpressionMatch,
expression="I buy {positive_number} apple(s)/banana(s)/orange(s)",
parameter_types=[positive_number],
on_fail=OnFailAction.EXCEPTION
)
# -- VALIDATOR PASSES: Good cases
guard.validate("I buy 0 apples") # Guardrail passes
guard.validate("I buy 1 apple") # Guardrail passes
guard.validate("I buy 1 banana") # Guardrail passes
guard.validate("I buy 2 bananas") # Guardrail passes
guard.validate("I buy 1 orange") # Guardrail passes
guard.validate("I buy 3 oranges") # Guardrail passes
# -- VALIDATOR FAILS: Bad cases
try:
guard.validate("I buy 2 melons") # Guardrail fails: Unexpected fruit
guard.validate("I buy -10 apples") # Guardrail fails: Negative number
except Exception as e:
print(e)
```
""" # noqa
def __init__(
self,
expression: str,
parameter_types: Optional[List[ParameterType]] = None,
on_fail: Optional[Callable] = None,
):
"""Initializes a new instance of the CucumberExpressionMatch class.
**Key Properties**
| Property | Description |
| ----------------------------- | ------------------------------------------------------- |
| Name for `format` attribute | `jenisys/guardrails.cucumber_expression_match` |
| Supported data types | `string` |
| Programmatic fix | Generate a string that matches this cucumber-expression |
Args:
expression (str): Cucumber expression to use (as string).
parameter_types (Optional[List[ParameterType]]): Parameter types to use (optional).
on_fail`** *(str, Callable)*: The policy to enact when a validator fails.
"""
parameter_types = parameter_types or []
super().__init__(on_fail=on_fail)
self._expression = expression
self._parameter_type_registry = ParameterTypeRegistry()
for parameter_type in parameter_types:
self._parameter_type_registry.define_parameter_type(parameter_type)
def validate(self, value: Any, metadata: Optional[Dict] = {}) -> ValidationResult:
"""Validates that {fill in how you validator interacts with the passed value}.
Args:
value (Any): The value to validate.
metadata (Dict): The metadata to validate against.
Returns:
ValidationResult: The validation result (PassResult or FailResult).
"""
this_expression = CucumberExpression(self._expression,
self._parameter_type_registry)
matched = this_expression.match(value)
if matched is None:
fix_string = rstr.xeger(this_expression.regexp)
return FailResult(
error_message=f"Result must match: {self._expression}",
fix_value=fix_string,
)
# -- VALIDATION-PASSED: "value" matches this cucumber-expression.
return PassResult()