From 33087d5dc6ca0726a680fb2530be92ca0b6672f9 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 7 Feb 2024 16:49:04 -0800 Subject: [PATCH 1/9] start contributor experience changes --- Makefile | 7 +++-- README.md | 12 ++++++-- pyproject.toml | 3 +- test/test_validator.py | 21 -------------- tests/__init__.py | 0 tests/test_validator.py | 27 ++++++++++++++++++ validator/__init__.py | 4 +-- validator/main.py | 58 ++++++++++++--------------------------- validator/post-install.py | 1 + 9 files changed, 63 insertions(+), 70 deletions(-) delete mode 100644 test/test_validator.py create mode 100644 tests/__init__.py create mode 100644 tests/test_validator.py diff --git a/Makefile b/Makefile index 5311f25..414e178 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ +dev: + pip install -e ".[dev]" + lint: ruff check . -tests: - pytest ./test +test: + pytest ./tests type: pyright validator diff --git a/README.md b/README.md index d2856b5..45322a3 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ # Guardrails Validator Template ## How to create a Guardrails Validator -- On the top right of the page, click "Use this template", select "create a new repository" and set a name for the package. +- On the top right of the page, click "Use this template", select "create a new repository" and set a name for the package. See [Naming Conventions](#naming-conventions) below. +- Clone down the new repository. - Modify the class in [validator/main.py](validator/main.py) with source code for the new validator - Make sure that the class still inherits from `Validator` and has the `register_validator` annotation. - Set the `name` in the `register_validator` to the name of the repo and set the appropriate data type. - Change [validator/__init__.py](validator/__init__.py) to your new Validator classname instead of RegexMatch - Locally test the validator with the test instructions below +- Update this README to follow the Validator Card format; you can find an example [here](https://github.com/guardrails-ai/lowercase/blob/main/README.md) -* Note: This package uses a pyproject.toml file, on first run, run `pip install .` to pull down and install all dependencies +* Note: This package uses a pyproject.toml file, on first run, run `make dev` to pull down and install all dependencies + +### Naming Conventions +1. Avoid using `is` and `bug` +2. Use snake_case: i.e. `_` to separate words. e.g. valid_address +3. For the description of the repo, write one sentence that says what the validator does; should be the same as the description in the pydoc string. +4. When annotating the class use the `{namespace}/{validator_name}` pattern: e.g. `@register_validator(name=“guardrails/valid_address”)` ### Testing and using your validator - Open [test/test-validator.py](test/test-validator.py) to test your new validator diff --git a/pyproject.toml b/pyproject.toml index 8a5be34..a3b933f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,6 @@ license = {file = "LICENSE"} readme = "README.md" requires-python = ">= 3.8" dependencies = [ - "rstr", "guardrails-ai>=0.3.2" ] @@ -24,7 +23,7 @@ dev = [ minversion = "6.0" addopts = "-rP" testpaths = [ - "test" + "tests" ] [tool.pyright] diff --git a/test/test_validator.py b/test/test_validator.py deleted file mode 100644 index 9125cc5..0000000 --- a/test/test_validator.py +++ /dev/null @@ -1,21 +0,0 @@ -# to run these, run -# pytest test/test-validator.py - -from guardrails import Guard -from validator import RegexMatch - -# We use 'refrain' as the validator's fail action, -# so we expect failures to always result in a guarded output of None -# Learn more about corrective actions here: -# https://www.guardrailsai.com/docs/concepts/output/#%EF%B8%8F-specifying-corrective-actions -guard = Guard.from_string(validators=[RegexMatch(regex="a.*", match_type="fullmatch", on_fail="refrain")]) - -def test_pass(): - test_output = "a test value" - raw_output, guarded_output, *rest = guard.parse(test_output) - assert(guarded_output is test_output) - -def test_fail(): - test_output = "b test value" - raw_output, guarded_output, *rest = guard.parse(test_output) - assert(guarded_output is None) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_validator.py b/tests/test_validator.py new file mode 100644 index 0000000..77afb8a --- /dev/null +++ b/tests/test_validator.py @@ -0,0 +1,27 @@ +# to run these, run +# make tests + +from guardrails import Guard +import pytest +from validator import ValidatorTemplate + +# We use 'exception' as the validator's fail action, +# so we expect failures to always raise an Exception +# Learn more about corrective actions here: +# https://www.guardrailsai.com/docs/concepts/output/#%EF%B8%8F-specifying-corrective-actions +guard = Guard.from_string(validators=[ValidatorTemplate(arg_1="arg_1", arg_2="arg_2", on_fail="exception")]) + +def test_pass(): + test_output = "pass" + result = guard.parse(test_output) + + assert result.validation_passed is True + assert result.validated_output == test_output + +def test_fail(): + with pytest.raises(Exception) as exc_info: + test_output = "fail" + guard.parse(test_output) + + # Assert the exception has your error_message + assert str(exc_info.value) == "Validation failed for field with errors: {A descriptive but concise error message about why validation failed}" diff --git a/validator/__init__.py b/validator/__init__.py index 36c3988..72a2623 100644 --- a/validator/__init__.py +++ b/validator/__init__.py @@ -1,3 +1,3 @@ -from .main import RegexMatch +from .main import ValidatorTemplate -__all__ = ["RegexMatch"] +__all__ = ["ValidatorTemplate"] diff --git a/validator/main.py b/validator/main.py index 9a0dc4f..e980de3 100644 --- a/validator/main.py +++ b/validator/main.py @@ -1,9 +1,5 @@ -import re -import string from typing import Any, Callable, Dict, Optional -import rstr - from guardrails.validator_base import ( FailResult, PassResult, @@ -13,59 +9,39 @@ ) -@register_validator(name="guardrails/regex_match", data_type="string") -class RegexMatch(Validator): - """Validates that a value matches a regular expression. +@register_validator(name="guardrails/validator_template", data_type="string") +class ValidatorTemplate(Validator): + """Validates that {fill in how you validator interacts with the passed value}. **Key Properties** | Property | Description | | ----------------------------- | --------------------------------- | - | Name for `format` attribute | `regex_match` | + | Name for `format` attribute | `guardrails/validator_template` | | Supported data types | `string` | - | Programmatic fix | Generate a string that matches the regular expression | + | Programmatic fix | {If you support programmatic fixes, explain it here. Otherwise `None`} | Args: - regex: Str regex pattern - match_type: Str in {"search", "fullmatch"} for a regex search or full-match option + arg_1 (string): {Description of the argument here} + arg_2 (string): {Description of the argument here} """ # noqa def __init__( self, - regex: str, - match_type: Optional[str] = None, + arg_1: str, + arg_2: str, on_fail: Optional[Callable] = None, ): - # todo -> something forces this to be passed as kwargs and therefore xml-ized. - # match_types = ["fullmatch", "search"] - - if match_type is None: - match_type = "fullmatch" - assert match_type in [ - "fullmatch", - "search", - ], 'match_type must be in ["fullmatch", "search"]' - - super().__init__(on_fail=on_fail, match_type=match_type, regex=regex) - self._regex = regex - self._match_type = match_type + super().__init__(on_fail=on_fail, arg_1=arg_1, arg_2=arg_2) + self._arg_1 = arg_1 + self._arg_2 = arg_2 def validate(self, value: Any, metadata: Dict) -> ValidationResult: - p = re.compile(self._regex) - """Validates that value matches the provided regular expression.""" - # Pad matching string on either side for fix - # example if we are performing a regex search - str_padding = ( - "" if self._match_type == "fullmatch" else rstr.rstr(string.ascii_lowercase) - ) - self._fix_str = str_padding + rstr.xeger(self._regex) + str_padding - - if not getattr(p, self._match_type)(value): + """Validates that {fill in how you validator interacts with the passed value}.""" + # Add your custom validator logic here and return a PassResult or FailResult accordingly. + if value != "pass": # FIXME return FailResult( - error_message=f"Result must match {self._regex}", - fix_value=self._fix_str, + error_message="{A descriptive but concise error message about why validation failed}", + fix_value="{The programmtic fix if applicable, otherwise remove this kwarg.}", ) return PassResult() - - def to_prompt(self, with_keywords: bool = True) -> str: - return "results should match " + self._regex diff --git a/validator/post-install.py b/validator/post-install.py index 2a9ac9a..536a250 100644 --- a/validator/post-install.py +++ b/validator/post-install.py @@ -1,3 +1,4 @@ print("post-install starting...") print("This is where you would do things like download nltk tokenizers or login to the HuggingFace hub...") print("post-install complete!") +# If you don't have anything to add here you should delete this file. \ No newline at end of file From 68a065b86de7b60c4c88a6d4ba287495adb4e49f Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 7 Feb 2024 17:02:24 -0800 Subject: [PATCH 2/9] update setup steps --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 45322a3..77c4774 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ - Clone down the new repository. - Modify the class in [validator/main.py](validator/main.py) with source code for the new validator - Make sure that the class still inherits from `Validator` and has the `register_validator` annotation. - - Set the `name` in the `register_validator` to the name of the repo and set the appropriate data type. -- Change [validator/__init__.py](validator/__init__.py) to your new Validator classname instead of RegexMatch -- Locally test the validator with the test instructions below + - Set the `name` in the `register_validator` to the name of the repo prefixed with your org as a namespace and set the appropriate data type. +- Change [validator/__init__.py](validator/__init__.py) to your new Validator classname instead of ValidatorTemplate +- Perform a self install with `make dev` or `pip install -e ".[dev]"` +- Locally test the validator with the [test instructions below](#testing-and-using-your-validator) - Update this README to follow the Validator Card format; you can find an example [here](https://github.com/guardrails-ai/lowercase/blob/main/README.md) * Note: This package uses a pyproject.toml file, on first run, run `make dev` to pull down and install all dependencies From da45c4daefba1c5ae219a64df127c96f4fc18cd7 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 7 Feb 2024 17:18:39 -0800 Subject: [PATCH 3/9] contributing and readme swap --- CONTRIBUTING.md | 38 ++++++++++++++++ README.md | 115 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1d1dd29 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +# Guardrails Validator Template + +## How to create a Guardrails Validator +- On the top right of the page, click "Use this template", select "create a new repository" and set a name for the package. See [Naming Conventions](#naming-conventions) below. +- Clone down the new repository. +- Modify the class in [validator/main.py](validator/main.py) with source code for the new validator + - Make sure that the class still inherits from `Validator` and has the `register_validator` annotation. + - Set the `name` in the `register_validator` to the name of the repo prefixed with your org as a namespace and set the appropriate data type. +- Change [validator/__init__.py](validator/__init__.py) to your new Validator classname instead of ValidatorTemplate +- Perform a self install with `make dev` or `pip install -e ".[dev]"` +- Locally test the validator with the [test instructions below](#testing-and-using-your-validator) +- Modify the README and follow the Validator Card format; you can find an example [here](https://github.com/guardrails-ai/lowercase/blob/main/README.md) + +* Note: This package uses a pyproject.toml file, on first run, run `make dev` to pull down and install all dependencies + +### Naming Conventions +1. Avoid using `is` and `bug` +2. Use snake_case: i.e. `_` to separate words. e.g. valid_address +3. For the description of the repo, write one sentence that says what the validator does; should be the same as the description in the pydoc string. +4. When annotating the class use the `{namespace}/{validator_name}` pattern: e.g. `@register_validator(name=“guardrails/valid_address”)` + +### Testing and using your validator +- Open [test/test-validator.py](test/test-validator.py) to test your new validator +- Import your new validator and modify `ValidatorTestObject` accordingly +- Modify the TEST_OUTPUT and TEST_FAIL_OUTPUT accordingly +- Run `python test/test-validator.py` via terminal, make sure the returned output reflects the input object +- Write advanced tests for failures, etc. + +## Upload your validator to the validator hub +- Update the [pyproject.toml](pyproject.toml) file and make necessary changes as follows: + - Update the `name` field to the name of your validator + - Update the `description` field to a short description of your validator + - Update the `authors` field to your name and email + - Add/update the `dependencies` field to include all dependencies your validator needs. +- If there are are any post-installation steps such as downloading tokenizers, logging into huggingface etc., update the [post-install.py](validator/post-install.py) file accordingly. +- You can add additional files to the [validator](validator) directory, but don't rename any existing files/directories. + - e.g. Add any environment variables (without the values, just the keys) to the [.env](.env) file. +- Ensure that there are no other dependencies or any additional steps required to run your validator. diff --git a/README.md b/README.md index 77c4774..491aa64 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,77 @@ -# Guardrails Validator Template - -## How to create a Guardrails Validator -- On the top right of the page, click "Use this template", select "create a new repository" and set a name for the package. See [Naming Conventions](#naming-conventions) below. -- Clone down the new repository. -- Modify the class in [validator/main.py](validator/main.py) with source code for the new validator - - Make sure that the class still inherits from `Validator` and has the `register_validator` annotation. - - Set the `name` in the `register_validator` to the name of the repo prefixed with your org as a namespace and set the appropriate data type. -- Change [validator/__init__.py](validator/__init__.py) to your new Validator classname instead of ValidatorTemplate -- Perform a self install with `make dev` or `pip install -e ".[dev]"` -- Locally test the validator with the [test instructions below](#testing-and-using-your-validator) -- Update this README to follow the Validator Card format; you can find an example [here](https://github.com/guardrails-ai/lowercase/blob/main/README.md) - -* Note: This package uses a pyproject.toml file, on first run, run `make dev` to pull down and install all dependencies - -### Naming Conventions -1. Avoid using `is` and `bug` -2. Use snake_case: i.e. `_` to separate words. e.g. valid_address -3. For the description of the repo, write one sentence that says what the validator does; should be the same as the description in the pydoc string. -4. When annotating the class use the `{namespace}/{validator_name}` pattern: e.g. `@register_validator(name=“guardrails/valid_address”)` - -### Testing and using your validator -- Open [test/test-validator.py](test/test-validator.py) to test your new validator -- Import your new validator and modify `ValidatorTestObject` accordingly -- Modify the TEST_OUTPUT and TEST_FAIL_OUTPUT accordingly -- Run `python test/test-validator.py` via terminal, make sure the returned output reflects the input object -- Write advanced tests for failures, etc. - -## Upload your validator to the validator hub -- Update the [pyproject.toml](pyproject.toml) file and make necessary changes as follows: - - Update the `name` field to the name of your validator - - Update the `description` field to a short description of your validator - - Update the `authors` field to your name and email - - Add/update the `dependencies` field to include all dependencies your validator needs. -- If there are are any post-installation steps such as downloading tokenizers, logging into huggingface etc., update the [post-install.py](validator/post-install.py) file accordingly. -- You can add additional files to the [validator](validator) directory, but don't rename any existing files/directories. - - e.g. Add any environment variables (without the values, just the keys) to the [.env](.env) file. -- Ensure that there are no other dependencies or any additional steps required to run your validator. +# Overview + +| Developed by | Guardrails AI | +| Date of development | Feb 15, 2024 | +| Validator type | Format | +| Blog | | +| License | Apache 2 | +| Input/Output | Output | + +# Description + +This validator ensures that a generated output is the literal "pass". + +# Installation + +```bash +$ guardrails hub install hub://guardrails/validator_template +``` + +# Usage Examples + +## Validating string output via Python + +In this example, we’ll test that a generated word is `pass`. + +```python +# Import Guard and Validator +from guardrails.hub import ValidatorTemplate +from guardrails import Guard + +# Initialize Validator +val = ValidatorTemplate() + +# Setup Guard +guard = Guard.from_string( + validators=[val, ...], +) + +guard.parse("pass") # Validator passes +guard.parse("fail") # Validator fails +``` + +## Validating JSON output via Python + +In this example, we verify that a processes’s status is specified as `pass`. + +```python +# Import Guard and Validator +from pydantic import BaseModel +from guardrails.hub import ValidatorTemplate +from guardrails import Guard + +val = ValidatorTemplate() + +# Create Pydantic BaseModel +class Process(BaseModel): + process_name: str + status: str = Field(validators=[val]) + +# Create a Guard to check for valid Pydantic output +guard = Guard.from_pydantic(output_class=Process) + +# Run LLM output generating JSON through guard +guard.parse(""" +{ + "process_name": "tempalting", + "status": "pass" +} +""") +``` + +# API Reference + +`__init__` +- `arg_1`: A placeholder argument to demonstrate how to use init arguments. +- `arg_2`: Another placeholder argument to demonstrate how to use init arguments. +- `on_fail`: The policy to enact when a validator fails. \ No newline at end of file From 44299964a935b491160511448d8f4781712299c1 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 7 Feb 2024 17:19:16 -0800 Subject: [PATCH 4/9] comment on __init__ --- validator/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/main.py b/validator/main.py index e980de3..441d20a 100644 --- a/validator/main.py +++ b/validator/main.py @@ -26,6 +26,7 @@ class ValidatorTemplate(Validator): arg_2 (string): {Description of the argument here} """ # noqa + # If you don't have any init args, you can omit the __init__ method. def __init__( self, arg_1: str, From 01f9bcc64ce8ac4b69cf3c5f4c8ebb7bc600810f Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Mon, 12 Feb 2024 09:53:34 -0600 Subject: [PATCH 5/9] typo --- README.md | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 491aa64..8fbe997 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ guard = Guard.from_pydantic(output_class=Process) # Run LLM output generating JSON through guard guard.parse(""" { - "process_name": "tempalting", + "process_name": "templating", "status": "pass" } """) diff --git a/pyproject.toml b/pyproject.toml index a3b933f..31d9023 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dev = [ "pyright", "pytest", "ruff" -] +] [tool.pytest.ini_options] minversion = "6.0" From 790ae304839861e031f38f7a34738da35888a585 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 14 Feb 2024 08:56:06 -0600 Subject: [PATCH 6/9] update readme to match latest validator card format --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8fbe997..d599837 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,16 @@ # Description -This validator ensures that a generated output is the literal "pass". +## Intended Use +This validator is a template for creating other validators, but for demonstrative purposes it ensures that a generated output is the literal `pass`. + +## Requirements + +* Dependencies: + - guardrails-ai>=0.4.0 + +* Foundation model access keys: + - OPENAI_API_KEY # Installation @@ -21,28 +30,25 @@ $ guardrails hub install hub://guardrails/validator_template ## Validating string output via Python -In this example, we’ll test that a generated word is `pass`. +In this example, we apply the validator to a string output generated by an LLM. ```python # Import Guard and Validator from guardrails.hub import ValidatorTemplate from guardrails import Guard -# Initialize Validator -val = ValidatorTemplate() - # Setup Guard -guard = Guard.from_string( - validators=[val, ...], +guard = Guard.use( + ValidatorTemplate ) -guard.parse("pass") # Validator passes -guard.parse("fail") # Validator fails +guard.validate("pass") # Validator passes +guard.validate("fail") # Validator fails ``` ## Validating JSON output via Python -In this example, we verify that a processes’s status is specified as `pass`. +In this example, we apply the validator to a string field of a JSON output generated by an LLM. ```python # Import Guard and Validator @@ -50,6 +56,7 @@ from pydantic import BaseModel from guardrails.hub import ValidatorTemplate from guardrails import Guard +# Initialize Validator val = ValidatorTemplate() # Create Pydantic BaseModel @@ -71,7 +78,32 @@ guard.parse(""" # API Reference -`__init__` -- `arg_1`: A placeholder argument to demonstrate how to use init arguments. -- `arg_2`: Another placeholder argument to demonstrate how to use init arguments. -- `on_fail`: The policy to enact when a validator fails. \ No newline at end of file +**`__init__(self, on_fail="noop")`** +
    +Initializes a new instance of the ValidatorTemplate class. + +**Parameters** +- **`arg_1`** *(str)*: A placeholder argument to demonstrate how to use init arguments. +- **`arg_2`** *(str)*: Another placeholder argument to demonstrate how to use init arguments. +- **`on_fail`** *(str, Callable)*: The policy to enact when a validator fails. If `str`, must be one of `reask`, `fix`, `filter`, `refrain`, `noop`, `exception` or `fix_reask`. Otherwise, must be a function that is called when the validator fails. +
+
+ +**`validate(self, value, metadata) → ValidationResult`** +
    +Validates the given `value` using the rules defined in this validator, relying on the `metadata` provided to customize the validation process. This method is automatically invoked by `guard.parse(...)`, ensuring the validation logic is applied to the input data. + +Note: + +1. This method should not be called directly by the user. Instead, invoke `guard.parse(...)` where this method will be called internally for each associated Validator. +2. When invoking `guard.parse(...)`, ensure to pass the appropriate `metadata` dictionary that includes keys and values required by this validator. If `guard` is associated with multiple validators, combine all necessary metadata into a single dictionary. + +**Parameters** +- **`value`** *(Any):* The input value to validate. +- **`metadata`** *(dict):* A dictionary containing metadata required for validation. Keys and values must match the expectations of this validator. + + + | Key | Type | Description | Default | + | --- | --- | --- | --- | + | key1 | String | Description of key1's role. | N/A | +
From 314edd297fa71c3e96da669b25e46991ae76201a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 14 Feb 2024 09:16:01 -0600 Subject: [PATCH 7/9] update project name --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 31d9023..71b0559 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "regexmatch" +name = "validator-template" version = "0.0.0" description = "Template repo for Guardrails Hub validators." authors = [ From 44226dde6a9234e8817018761612c4e5181ef0e6 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 14 Feb 2024 09:34:43 -0600 Subject: [PATCH 8/9] default metadata to empty dictionary --- validator/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/main.py b/validator/main.py index 441d20a..759d0b4 100644 --- a/validator/main.py +++ b/validator/main.py @@ -37,7 +37,7 @@ def __init__( self._arg_1 = arg_1 self._arg_2 = arg_2 - def validate(self, value: Any, metadata: Dict) -> ValidationResult: + def validate(self, value: Any, metadata: Dict = {}) -> ValidationResult: """Validates that {fill in how you validator interacts with the passed value}.""" # Add your custom validator logic here and return a PassResult or FailResult accordingly. if value != "pass": # FIXME From bb89133a06869cfb2481c1eef02ba1935e1c655e Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 14 Feb 2024 14:55:26 -0500 Subject: [PATCH 9/9] add pydanitc Field import --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d599837..4d6a228 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ In this example, we apply the validator to a string field of a JSON output gener ```python # Import Guard and Validator -from pydantic import BaseModel +from pydantic import BaseModel, Field from guardrails.hub import ValidatorTemplate from guardrails import Guard