Skip to content

Commit 280bb81

Browse files
Merge pull request #1140 from guardrails-ai/feat/validator-versioning
Validator Versioning
2 parents cd37159 + a82a412 commit 280bb81

File tree

14 files changed

+472
-550
lines changed

14 files changed

+472
-550
lines changed

docs/concepts/remote_validation_inference.ipynb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
"\n",
2020
":::note\n",
2121
"Remote validation inferencing is only available in Guardrails versions 0.5.0 and above.\n",
22+
":::\n",
23+
"\n",
24+
"::note\n",
25+
"To enable/disable, you can run the cli command `guardrails configure` or modify your `~/.guardrailsrc`. For example, to disable remote inference use: `use_remote_inferencing=false`.\n",
2226
":::"
2327
]
2428
},
@@ -147,7 +151,7 @@
147151
],
148152
"metadata": {
149153
"kernelspec": {
150-
"display_name": ".venv",
154+
"display_name": "Python 3 (ipykernel)",
151155
"language": "python",
152156
"name": "python3"
153157
},
@@ -161,9 +165,9 @@
161165
"name": "python",
162166
"nbconvert_exporter": "python",
163167
"pygments_lexer": "ipython3",
164-
"version": "3.11.7"
168+
"version": "3.11.9"
165169
}
166170
},
167171
"nbformat": 4,
168-
"nbformat_minor": 2
172+
"nbformat_minor": 4
169173
}

docs/how_to_guides/continuous_integration_continuous_deployment.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ terraform apply -var="aws_region=us-east-1" -var="backend_memory=2048" -var="bac
255255
Once the deployment has succeeded you should see some output values (which will be required if you wish to set up CI).
256256

257257

258-
## Step4: Deploying Guardrails API
258+
## Step 4: Deploying Guardrails API
259259
### Manual
260260

261261
Firstly, create or use your existing guardrails token and export it to your current shell `export GUARDRAILS_TOKEN="..."`
@@ -444,7 +444,67 @@ By setting the above environment variable `GUARDRAILS_BASE_URL` the SDK will be
444444

445445
## Quick Start Repository Template
446446

447-
We've conveniently packaged all the artifacts from this document in a github repository that can be used as a template for your own verification and deployment [here](https://github.com/guardrails-ai/continuous_integration_and_deployment_aws_template).
447+
We've conveniently packaged all the artifacts from this document in a github repository that can be used as a template for your own verification and deployment [here](https://github.com/guardrails-ai/continuous_integration_and_deployment_aws_template).
448+
449+
## Diagram
450+
451+
```mermaid
452+
graph TD
453+
%% Internet and IGW
454+
Internet((Internet)) --> IGW[Internet Gateway]
455+
IGW --> RouteTable[Public Route Table]
456+
457+
%% VPC Container
458+
VPC[VPC<br/>10.0.0.0/16] --> IGW
459+
460+
%% IAM Permissions Group
461+
subgraph ECSIAMPermissions["ECS IAM Permissions"]
462+
ExecutionRole[ECS Execution Role]
463+
TaskRole[ECS Task Role]
464+
end
465+
466+
%% Public Subnet Group
467+
subgraph PublicSubnets["Public Subnets x3"]
468+
NLB[Network Load Balancer]
469+
TG[Target Group<br/>TCP:80]
470+
SG[Security Group<br/>Ingress: 8000<br/>Egress: All]
471+
ECSCluster[ECS Cluster]
472+
473+
%% ECS Components
474+
ECSService[ECS Service]
475+
TaskDef[Task Definition<br/>CPU: 1024<br/>Memory: 2048]
476+
Container[Container<br/>Port: 8000]
477+
478+
%% Internal Connections
479+
NLB --> |Port 80| TG
480+
TG --> ECSService
481+
ECSCluster --> ECSService
482+
SG --> ECSService
483+
ECSService --> TaskDef
484+
TaskDef --> Container
485+
end
486+
487+
%% IAM Connections
488+
ECSIAMPermissions --> TaskDef
489+
490+
%% Route Table Connection
491+
RouteTable --> PublicSubnets
492+
493+
%% External Service Connections
494+
CloudWatchLogs[CloudWatch Log Group] --> Container
495+
ECR[ECR Repository] --> |Image| Container
496+
497+
%% Internet Access
498+
Internet --> NLB
499+
500+
%% Styling
501+
classDef aws fill:#FF9900,stroke:#232F3E,stroke-width:2px,color:black
502+
classDef subnet fill:#FFD700,stroke:#232F3E,stroke-width:2px,color:black
503+
classDef iam fill:#FF6B6B,stroke:#232F3E,stroke-width:2px,color:black
504+
class VPC,IGW,NATGateway,RouteTable,NLB,TG,ECSCluster,ECSService,TaskDef,Container,SG,CloudWatchLogs,ECR aws
505+
class PublicSubnets subnet
506+
class ECSIAMPermissions,ExecutionRole,TaskRole iam
507+
```
448508

449509
## Terraform
450510

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Migrating to 0.6.0-alpha
2+
3+
## Summary
4+
This guide will help you migrate your codebase from 0.5.X to 0.6.0-alpha.
5+
6+
## Installation
7+
```bash
8+
pip install --pre guardrails-ai
9+
```
10+
11+
## List of backwards incompatible changes
12+
13+
1. All validators will require authentication with a guardrails token. This will also apply to all versions >=0.4.x of the guardrails-ai package.
14+
1. prompt, msg_history, instructions, reask_messages, reask_instructions, and will be removed from the `Guard.__call__` function and will instead be supported by a single messages argument, and a reask_messages argument for reasks.
15+
1. Custom callables now need to expect `messages`, as opposed to `prompt` to come in as a keyword argument.
16+
1. The default on_fail action for validators will change from noop to exception.
17+
1. In cases where we try and generate structured data, Guardrails will no longer automatically attempt to try and coerce the LLM into giving correctly formatted information.
18+
1. Guardrails will no longer automatically set a tool selection in the OpenAI callable when initialized using pydantic for initial prompts or reasks
19+
1. Guard.from_string is being removed in favor of Guard()
20+
1. Guard.from_pydantic is renamed to Guard.for_pydantic
21+
1. Guard.from_rail is renamed to Guard.for_rail
22+
1. Guard.from_rail_string is renamed to guard.for_rail_string
23+
1. The guardrails server will change from using Flask to FastAPI. We recommend serving uvicorn runners via a gunicorn WSGI.
24+
1. OpenAI, Cohere and Anthropic **callables are being removed in favor of support through passing no callable and setting the appropriate api key and model argument.
25+
26+
27+
## How to migrate
28+
29+
### Messages support for reask and RAILS
30+
`Guard.__call` and rails now fully support `reask_messages` as an argument.
31+
32+
### For `Guard.__call`
33+
```py
34+
response = guard(
35+
messages=[{
36+
"role":"user",
37+
"content":"tell me a joke"
38+
}],
39+
reask_messages=[{
40+
"role":"system"
41+
"content":"your previous joke failed validation can you tell me another joke?"
42+
}]
43+
)
44+
```
45+
46+
#### For Rail
47+
```
48+
<rail version="0.1">
49+
<messages>
50+
<message role="system">
51+
Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'.
52+
${document}
53+
${gr.xml_prefix_prompt}
54+
</message>
55+
<message role="user">
56+
${question}
57+
</message>
58+
</messages>
59+
<reask_messages>
60+
<message="system">
61+
You were asked ${question} and it was not correct can you try again?
62+
</message>
63+
</reask_messages>
64+
</rail>
65+
```
66+
67+
## Improvements
68+
69+
### Per message validation and fix support
70+
Message validation is now more granular and executed on a per message content basis.
71+
This allows for on_fix behavior to be fully supported.
72+
73+
## Backwards-incompatible changes
74+
### Validator onFail default behavior is now exception
75+
Previously the default behavior for validation failure was noop. This meant developers were required to set it on_fail to exception or check validation_failed
76+
to know if validation failed. This was unintuitive for new users and led to confusion around if validators were working or not. This new behavior will require
77+
exception handling be added or configurations manually to be set to noop if desired.
78+
79+
### Simplified schema injection behavior
80+
Previously prompt and instruction suffixes and formatting hints were sometimes automatically injected
81+
into prompt and instructions if guardrails detected xml or a structured schema being used for output.
82+
This caused confusion and unexpected behavior when arguments to llms were being mutated without a developer asking for it.
83+
Developers will now need to intentionally include Guardrails template variables such as `${gr.complete_xml_suffix_v2}`
84+
85+
### Guardrails Server migration from Flask to FastAPI
86+
In 0.5.X guard.__call and guard.validate async streaming received support for on_fail="fix" merge and parallelized async validation.
87+
We have updated the server to use FastAPI which is build on ASGI to be able to fully take advantage of these improvements.
88+
config.py now fully supports the definition of AsyncGuards and streaming=true as a request argument.
89+
We recommend the combination of `gunicorn` and `uvicorn.workers.UvicornWorker`s
90+
91+
### Streamlined prompt, instructions and msg_history arguments into messages
92+
`Guard.__call` prompt, instruction, reask_prompt and reask_instruction arguments have been streamlined into messages and reask_messages
93+
Instructions should be specified with the role system and prompts with the role user. Any of the out of the box supported llms that require only one text prompt will automatically have the messages converted to one unless a custom callable is being used.
94+
```
95+
# version < 0.6.0
96+
guard(
97+
instructions="you are a funny assistant",
98+
prompt="tell me a joke"
99+
)
100+
# version >= 0.6.0
101+
guard(
102+
messages=[
103+
{"role":"system", "content":"you are a funny assistant"},
104+
{"role":"user", "content":"tell me a joke"}
105+
]
106+
)
107+
```
108+
109+
### Removal of guardrails OpenAI, Cohere, Anthropic Callables
110+
These callables are being removed in favor of support through passing no callable and setting the appropriate api key and model argument.
111+
112+
### Prompt no longer a required positional argument on custom callables
113+
Custom callables will no longer throw an error if the prompt arg is missing in their declaration and guardrails will no longer pass prompt as the first argument. They need to be updated to the messages kwarg to get text input. If a custom callables underlying llm only accepts a single string a helper exists that can compose messages into one otherwise some code to adapt them will be required.
114+
115+
```py
116+
from guardrails import messages_to_prompt_string
117+
118+
class CustomCallableCallable(PromptCallableBase):
119+
def llm_api(
120+
self,
121+
*args,
122+
**kwargs,
123+
) -> str:
124+
messages = kwargs.pop("messages", [])
125+
prompt = messages_to_prompt_string(messages)
126+
127+
llm_string_output = some_llm_call_requiring_prompt(
128+
prompt,
129+
*args,
130+
**kwargs,
131+
)
132+
return llm_string_output
133+
```

docusaurus/docusaurus.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const config = {
2929
markdown: {
3030
mermaid: true
3131
},
32-
// themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-classic'],
32+
themes: ['@docusaurus/theme-mermaid'],
3333
// Even if you don't use internalization, you can use this field to set useful
3434
// metadata like html lang. For example, if your site is Chinese, you may want
3535
// to replace "en" with "zh-Hans".

guardrails/cli/hub/list.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import re
33

44
from guardrails.cli.hub.hub import hub_command
5-
from guardrails.cli.hub.utils import get_site_packages_location
65
from guardrails.hub_telemetry.hub_tracing import trace
76
from .console import console
87

@@ -11,7 +10,9 @@
1110
@trace(name="guardrails-cli/hub/list")
1211
def list():
1312
"""List all installed validators."""
14-
site_packages = get_site_packages_location()
13+
from guardrails.hub.validator_package_service import ValidatorPackageService
14+
15+
site_packages = ValidatorPackageService.get_site_packages_location()
1516
hub_init_file = os.path.join(site_packages, "guardrails", "hub", "__init__.py")
1617

1718
installed_validators = []

guardrails/cli/hub/uninstall.py

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os
2-
import shutil
32
import sys
43
from typing import List, Literal
54

@@ -10,9 +9,7 @@
109
from guardrails.cli.server.hub_client import get_validator_manifest
1110
from guardrails_hub_types import Manifest
1211

13-
from guardrails.cli.hub.utils import get_site_packages_location
14-
from guardrails.cli.hub.utils import get_org_and_package_dirs
15-
from guardrails.cli.hub.utils import get_hub_directory
12+
from guardrails.cli.hub.utils import pip_process
1613
from guardrails.hub_telemetry.hub_tracing import trace
1714

1815
from .console import console
@@ -32,46 +29,28 @@ def remove_line(file_path: str, line_content: str):
3229

3330

3431
def remove_from_hub_inits(manifest: Manifest, site_packages: str):
35-
org_package = get_org_and_package_dirs(manifest)
32+
from guardrails.hub.validator_package_service import ValidatorPackageService
33+
3634
exports: List[str] = manifest.exports or []
3735
sorted_exports = sorted(exports, reverse=True)
38-
module_name = manifest.module_name
39-
relative_path = ".".join([*org_package, module_name])
40-
import_line = (
41-
f"from guardrails.hub.{relative_path} import {', '.join(sorted_exports)}"
36+
37+
validator_id = manifest.id
38+
import_path = ValidatorPackageService.get_import_path_from_validator_id(
39+
validator_id
4240
)
41+
import_line = f"from {import_path} import {', '.join(sorted_exports)}"
4342

4443
# Remove import line from main __init__.py
4544
hub_init_location = os.path.join(site_packages, "guardrails", "hub", "__init__.py")
4645
remove_line(hub_init_location, import_line)
4746

48-
# Remove import line from namespace __init__.py
49-
namespace = org_package[0]
50-
namespace_init_location = os.path.join(
51-
site_packages, "guardrails", "hub", namespace, "__init__.py"
52-
)
53-
lines = remove_line(namespace_init_location, import_line)
54-
55-
# remove namespace pkg if namespace __init__.py is empty
56-
if (len(lines) == 0) and (namespace != "hub"):
57-
logger.info(f"Removing namespace package {namespace} as it is now empty")
58-
try:
59-
shutil.rmtree(os.path.join(site_packages, "guardrails", "hub", namespace))
60-
except Exception as e:
61-
logger.error(f"Error removing namespace package {namespace}")
62-
logger.error(e)
63-
sys.exit(1)
64-
65-
66-
def uninstall_hub_module(manifest: Manifest, site_packages: str):
67-
uninstall_directory = get_hub_directory(manifest, site_packages)
68-
logger.info(f"Removing directory {uninstall_directory}")
69-
try:
70-
shutil.rmtree(uninstall_directory)
71-
except Exception as e:
72-
logger.error("Error removing directory")
73-
logger.error(e)
74-
sys.exit(1)
47+
48+
def uninstall_hub_module(manifest: Manifest):
49+
from guardrails.hub.validator_package_service import ValidatorPackageService
50+
51+
validator_id = manifest.id
52+
package_name = ValidatorPackageService.get_normalized_package_name(validator_id)
53+
pip_process("uninstall", package_name, flags=["-y"], quiet=True)
7554

7655

7756
@hub_command.command()
@@ -82,6 +61,8 @@ def uninstall(
8261
),
8362
):
8463
"""Uninstall a validator from the Hub."""
64+
from guardrails.hub.validator_package_service import ValidatorPackageService
65+
8566
if not package_uri.startswith("hub://"):
8667
logger.error("Invalid URI!")
8768
sys.exit(1)
@@ -98,11 +79,11 @@ def uninstall(
9879
# Prep
9980
with console.status("Fetching manifest", spinner="bouncingBar"):
10081
module_manifest = get_validator_manifest(module_name)
101-
site_packages = get_site_packages_location()
82+
site_packages = ValidatorPackageService.get_site_packages_location()
10283

10384
# Uninstall
10485
with console.status("Removing module", spinner="bouncingBar"):
105-
uninstall_hub_module(module_manifest, site_packages)
86+
uninstall_hub_module(module_manifest)
10687

10788
# Cleanup
10889
with console.status("Cleaning up", spinner="bouncingBar"):

0 commit comments

Comments
 (0)