Skip to content

Commit

Permalink
Merge pull request #1002 from guardrails-ai/dtam/add_support_for_outp…
Browse files Browse the repository at this point in the history
…ut_format_json_schema

add experimental helper to get us a much cleaner interface to openai …
  • Loading branch information
zsimjee authored Aug 8, 2024
2 parents dace66f + 5c149d6 commit d6a1a29
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 29 deletions.
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,
},
}

0 comments on commit d6a1a29

Please sign in to comment.