# ruff: noqa: E501 import os from datetime import date from string import Template import typer from pydash import pascal_case, snake_case from guardrails.cli.hub.hub import hub_command from guardrails.cli.logger import LEVELS, logger from guardrails.hub_telemetry.hub_tracing import trace validator_template = Template( """ \""" This template is intended for creating simple validators. If your validator is complex or requires additional post-installation steps, consider using the template repository instead. The template repository can be found here: https://github.com/guardrails-ai/validator-template \""" from typing import Any, Callable, Dict, Optional from guardrails.validator_base import ( FailResult, PassResult, ValidationResult, Validator, register_validator, ) @register_validator(name="guardrails/${package_name}", data_type="string") class ${class_name}(Validator): # FIXME: Update the class docstring to reflect the purpose and usage of your validator. \"""# Overview | Developed by | {FIXME: Your organization name} | | Date of development | ${dev_date} | | Validator type | Format | | License | Apache 2 | | Input/Output | Output | # Description {FIXME: A brief description of what your validator does.} ## (Optional) Intended Use {FIXME: Optionally, include a brief description of the intended use of your validator, including any limitations or constraints.} ## Requirements * Dependencies: - guardrails-ai>=0.4.0 - {FIXME: Include any other dependencies you need here} * Dev Dependencies: - pytest - pyright - ruff - {FIXME: Include any other dev dependencies you need here} * Foundation model access keys: - {FIXME: Include any access environment variables you need here like OPENAI_API_KEY} # Installation ```bash $ guardrails hub install hub://guardrails/${package_name} ``` # Usage Examples ## Validating string output via Python In this example, we apply the validator to a string output generated by an LLM. ```python # Import Guard and Validator from guardrails.hub import ${class_name} from guardrails import Guard # Setup Guard guard = Guard.use( ${class_name}({FIXME: list any args here}) ) guard.validate({FIXME: Add an input that should pass the validator}) # Validator passes guard.validate({FIXME: Add an input that should fail the validator}) # Validator fails ``` \""" # noqa # If you don't have any init args, you can omit the __init__ method. def __init__( self, arg_1: str, # FIXME: Replace with your custom init args. on_fail: Optional[Callable] = None, ): \"""Initializes a new instance of the ${class_name} class. Args: arg_1 (str): FIXME: Describe the purpose of this argument. 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. \""" super().__init__(on_fail=on_fail, arg_1=arg_1) self._arg_1 = arg_1 def validate(self, value: Any, metadata: 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. FIXME: Add any additional args you need here in metadata. | Key | Description | | --- | --- | | a | b | \""" # Add your custom validator logic here and return a PassResult or FailResult accordingly. if value != "pass": # FIXME return FailResult( error_message="{FIXME: A descriptive but concise error message about why validation failed}", fix_value="{FIXME: The programmtic fix if applicable, otherwise remove this kwarg.}", ) return PassResult() # Run tests via `pytest -rP ${filepath}` class Test${class_name}: def test_success_case(self): # FIXME: Replace with your custom test logic for the success case. validator = ${class_name}("s") result = validator.validate("pass", {}) assert isinstance(result, PassResult) is True def test_failure_case(self): # FIXME: Replace with your custom test logic for the failure case. validator = ${class_name}("s") result = validator.validate("fail", {}) assert isinstance(result, FailResult) is True assert result.error_message == "{A descriptive but concise error message about why validation failed}" assert result.fix_value == "fails" """ ) @hub_command.command(name="create-validator") @trace(name="guardrails-cli/hub/create-validator") def create_validator( name: str = typer.Argument(help="The name for your validator."), filepath: str = typer.Argument( help="The location to write your validator template to", default="./{validator_name}.py", ), ): """Lightweight method for creating simple validators. For more complex submissions see here: https://github.com/guardrails-ai/validator-template?tab=readme-ov-file#how-to-create-a-guardrails-validator """ disclaimer = """ This utility is intended for creating simple validators. If your validator is complex or requires additional post-installation steps,\ consider using the template repository instead. The template repository can be found here:\ https://github.com/guardrails-ai/validator-template """ logger.log(level=LEVELS.get("NOTICE") or 0, msg=disclaimer) package_name = snake_case(name) class_name = pascal_case(name) if not filepath or filepath == "./{validator_name}.py": filepath = f"./{package_name}.py" template = validator_template.safe_substitute( { "package_name": package_name, "class_name": class_name, "filepath": filepath, "dev_date": date.today().strftime("%b %d, %Y"), } ) target = os.path.abspath(filepath) with open(target, "w") as validator_file: validator_file.write(template) validator_file.close() success_message = Template( """ Successfully created validator template at ${filepath}! Make any necessary changes then submit for review with the following command: guardrails hub submit ${package_name} ${filepath} """ ).safe_substitute({"filepath": filepath, "package_name": package_name}) logger.log(level=LEVELS.get("SUCCESS"), msg=success_message) # type: ignore