Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions guardrails/decorators/experimental.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import functools
from guardrails.logger import logger


def experimental(func):
"""Decorator to mark a function as experimental."""

@functools.wraps(func)
def wrapper(*args, **kwargs):
logger.warn(
f"The function '{func.__name__}' is experimental and subject to change."
)
return func(*args, **kwargs)

return wrapper
9 changes: 8 additions & 1 deletion guardrails/guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,13 @@
ValidatorMap,
)

from guardrails.utils.tools_utils import (
from guardrails.utils.structured_data_utils import (
# Prevent duplicate declaration in the docs
json_function_calling_tool as json_function_calling_tool_util,
output_format_json_schema as output_format_json_schema,
)
from guardrails.decorators.experimental import experimental

from guardrails.settings import settings


Expand Down Expand Up @@ -1331,6 +1334,10 @@ def to_dict(self) -> Dict[str, Any]:

return i_guard.to_dict()

@experimental
def response_format_json_schema(self) -> Dict[str, Any]:
return output_format_json_schema(schema=self._base_model) # type: ignore

def json_function_calling_tool(
self,
tools: Optional[list] = None,
Expand Down
75 changes: 75 additions & 0 deletions guardrails/utils/structured_data_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from typing import List, Optional
from guardrails.logger import logger
from guardrails.classes.schema.processed_schema import ProcessedSchema
from guardrails.types.pydantic import ModelOrListOfModels


# takes processed schema and converts it to a openai tool object
def schema_to_tool(schema) -> dict:
tool = {
"type": "function",
"function": {
"name": "gd_response_tool",
"description": "A tool for generating responses to guardrails."
" It must be called last in every response.",
"parameters": schema,
"required": schema["required"] or [],
},
}
return tool


def set_additional_properties_false_iteratively(schema):
stack = [schema]
while stack:
current = stack.pop()
if isinstance(current, dict):
if "properties" in current:
current["required"] = list(
current["properties"].keys()
) # this has to be set
if "maximum" in current:
logger.warn("Property maximum is not supported." " Dropping")
current.pop("maximum") # the api does not like these set
if "minimum" in current:
logger.warn("Property maximum is not supported." " Dropping")
current.pop("minimum") # the api does not like these set
if "default" in current:
logger.warn("Property default is not supported. Marking field Required")
current.pop("default") # the api does not like these set
for prop in current.values():
stack.append(prop)
elif isinstance(current, list):
for prop in current:
stack.append(prop)
if (
isinstance(current, dict)
and "additionalProperties" not in current
and "type" in current
and current["type"] == "object"
):
current["additionalProperties"] = False # the api needs these set


def json_function_calling_tool(
schema: ProcessedSchema,
tools: Optional[List] = None,
) -> List:
tools = tools or []
tools.append(schema_to_tool(schema)) # type: ignore
return tools


def output_format_json_schema(schema: ModelOrListOfModels) -> dict:
parsed_schema = schema.model_json_schema() # type: ignore

set_additional_properties_false_iteratively(parsed_schema)

return {
"type": "json_schema",
"json_schema": {
"name": parsed_schema["title"],
"schema": parsed_schema,
"strict": True,
}, # type: ignore
}
27 changes: 0 additions & 27 deletions guardrails/utils/tools_utils.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

from guardrails.schema.pydantic_schema import pydantic_model_to_schema

from guardrails.utils.tools_utils import json_function_calling_tool, schema_to_tool
from guardrails.utils.structured_data_utils import (
json_function_calling_tool,
schema_to_tool,
output_format_json_schema,
)


class Delivery(BaseModel):
Expand Down Expand Up @@ -141,3 +145,89 @@ def test_json_function_calling_tool():
},
}
]


def test_output_format_json_schema():
schema = output_format_json_schema(Schedule)
assert schema == {
"type": "json_schema",
"json_schema": {
"name": "Schedule",
"schema": {
"additionalProperties": False,
"$defs": {
"Delivery": {
"additionalProperties": False,
"properties": {
"customer": {
"description": "customer name",
"title": "Customer",
"type": "string",
},
"pickup_time": {
"description": "date and time of pickup",
"title": "Pickup Time",
"type": "string",
},
"pickup_location": {
"description": "address of pickup",
"title": "Pickup Location",
"type": "string",
},
"dropoff_time": {
"description": "date and time of dropoff",
"title": "Dropoff Time",
"type": "string",
},
"dropoff_location": {
"description": "address of dropoff",
"title": "Dropoff Location",
"type": "string",
},
"price": {
"description": "price of delivery with"
" currency symbol included",
"title": "Price",
"type": "string",
},
"items": {
"description": "items for pickup/delivery typically"
" something a single person can carry on a bike",
"title": "Items",
"type": "string",
},
"number_items": {
"description": "number of items",
"title": "Number Items",
"type": "integer",
},
},
"required": [
"customer",
"pickup_time",
"pickup_location",
"dropoff_time",
"dropoff_location",
"price",
"items",
"number_items",
],
"title": "Delivery",
"type": "object",
}
},
"properties": {
"deliveries": {
"description": "deliveries for messenger",
"items": {"$ref": "#/$defs/Delivery"},
"title": "Deliveries",
"type": "array",
}
},
"required": ["deliveries"],
"title": "Schedule",
"type": "object",
},
"strict": True,
},
}