Refactored Grammar Generator

Added example and usage instruction.
This commit is contained in:
Maximilian Winter 2024-01-12 17:13:26 +01:00
parent ebca51be4f
commit 2b42f5254c
2 changed files with 223 additions and 115 deletions

View file

@ -0,0 +1,100 @@
# Function calling example using pydantic models.
import json
from enum import Enum
from typing import Union
import requests
from pydantic import BaseModel, Field
from pydantic_models_to_grammar import generate_gbnf_grammar_and_documentation
# Function to get completion on the llama.cpp server with grammar.
def create_completion(prompt, grammar):
headers = {"Content-Type": "application/json"}
data = {"prompt": prompt, "grammar": grammar}
response = requests.post("http://127.0.0.1:8080/completion", headers=headers, json=data)
data = response.json()
print(data["content"])
return data["content"]
# A function for the agent to send a message to the user.
class SendMessageToUser(BaseModel):
"""
Send a message to the User.
"""
chain_of_thought: str = Field(..., description="Your chain of thought while sending the message.")
message: str = Field(..., description="Message you want to send to the user.")
def run(self):
print(self.message)
# Enum for the calculator function.
class MathOperation(Enum):
ADD = "add"
SUBTRACT = "subtract"
MULTIPLY = "multiply"
DIVIDE = "divide"
# Very simple calculator tool for the agent.
class Calculator(BaseModel):
"""
Perform a math operation on two numbers.
"""
number_one: Union[int, float] = Field(..., description="First number.")
operation: MathOperation = Field(..., description="Math operation to perform.")
number_two: Union[int, float] = Field(..., description="Second number.")
def run(self):
if self.operation == MathOperation.ADD:
return self.number_one + self.number_two
elif self.operation == MathOperation.SUBTRACT:
return self.number_one - self.number_two
elif self.operation == MathOperation.MULTIPLY:
return self.number_one * self.number_two
elif self.operation == MathOperation.DIVIDE:
return self.number_one / self.number_two
else:
raise ValueError("Unknown operation.")
# Here the grammar gets generated by passing the available function models to generate_gbnf_grammar_and_documentation function. This also generates a documentation usable by the LLM.
# pydantic_model_list is the list of pydanitc models
# outer_object_name is an optional name for an outer object around the actual model object. Like a "function" object with "function_parameters" which contains the actual model object. If None, no outer object will be generated
# outer_object_content is the name of outer object content.
# model_prefix is the optional prefix for models in the documentation. (Default="Output Model")
# fields_prefix is the prefix for the model fields in the documentation. (Default="Output Fields")
gbnf_grammar, documentation = generate_gbnf_grammar_and_documentation(
pydantic_model_list=[SendMessageToUser, Calculator], outer_object_name="function",
outer_object_content="function_parameters", model_prefix="Function", fields_prefix="Parameters")
print(gbnf_grammar)
print(documentation)
system_message = "You are an advanced AI, tasked to assist the user by calling functions in JSON format. The following are the available functions and their parameters and types:\n\n" + documentation
user_message = "What is 42 * 42?"
prompt = f"<|im_start|>system\n{system_message}<|im_end|>\n<|im_start|>user\n{user_message}<|im_end|>\n<|im_start|>assistant"
text = create_completion(prompt=prompt, grammar=gbnf_grammar)
# This should output something like this:
# {
# "function": "calculator",
# "function_parameters": {
# "number_one": 42,
# "operation": "multiply",
# "number_two": 42
# }
# }
function_dictionary = json.loads(text)
if function_dictionary["function"] == "calculator":
function_parameters = {**function_dictionary["function_parameters"]}
print(Calculator(**function_parameters).run())
# This should output: 1764

View file

@ -26,6 +26,8 @@ class PydanticDataType(Enum):
CUSTOM_CLASS (str): Represents a custom class data type. CUSTOM_CLASS (str): Represents a custom class data type.
""" """
STRING = "string" STRING = "string"
TRIPLE_QUOTED_STRING = "triple_quoted_string"
MARKDOWN_STRING = "markdown_string"
BOOLEAN = "boolean" BOOLEAN = "boolean"
INTEGER = "integer" INTEGER = "integer"
FLOAT = "float" FLOAT = "float"
@ -156,12 +158,12 @@ def generate_gbnf_integer_rules(max_digit=None, min_digit=None):
Generates GBNF (Generalized Backus-Naur Form) rules for integers based on the given maximum and minimum digits. Generates GBNF (Generalized Backus-Naur Form) rules for integers based on the given maximum and minimum digits.
Parameters: Parameters:
- max_digit (int): The maximum number of digits for the integer. Default is None. max_digit (int): The maximum number of digits for the integer. Default is None.
- min_digit (int): The minimum number of digits for the integer. Default is None. min_digit (int): The minimum number of digits for the integer. Default is None.
Returns: Returns:
- integer_rule (str): The identifier for the integer rule generated. integer_rule (str): The identifier for the integer rule generated.
- additional_rules (list): A list of additional rules generated based on the given maximum and minimum digits. additional_rules (list): A list of additional rules generated based on the given maximum and minimum digits.
""" """
additional_rules = [] additional_rules = []
@ -362,7 +364,15 @@ def generate_gbnf_rule_for_type(model_name, look_for_markdown_code_block, look_f
else: else:
gbnf_type = f"{model_name}-{field_name}-union" gbnf_type = f"{model_name}-{field_name}-union"
elif isclass(field_type) and issubclass(field_type, str): elif isclass(field_type) and issubclass(field_type, str):
if field_info and hasattr(field_info, 'pattern'): if field_info and hasattr(field_info, 'json_schema_extra') and field_info.json_schema_extra is not None:
triple_quoted_string = field_info.json_schema_extra.get('triple_quoted_string', False)
markdown_string = field_info.json_schema_extra.get('markdown_string', False)
gbnf_type = PydanticDataType.TRIPLE_QUOTED_STRING.value if triple_quoted_string else PydanticDataType.STRING.value
gbnf_type = PydanticDataType.MARKDOWN_STRING.value if markdown_string else gbnf_type
elif field_info and hasattr(field_info, 'pattern'):
# Convert regex pattern to grammar rule # Convert regex pattern to grammar rule
regex_pattern = field_info.regex.pattern regex_pattern = field_info.regex.pattern
gbnf_type = f"pattern-{field_name} ::= {regex_to_gbnf(regex_pattern)}" gbnf_type = f"pattern-{field_name} ::= {regex_to_gbnf(regex_pattern)}"
@ -396,7 +406,6 @@ def generate_gbnf_rule_for_type(model_name, look_for_markdown_code_block, look_f
# Generate GBNF rule for integer with given attributes # Generate GBNF rule for integer with given attributes
gbnf_type, rules = generate_gbnf_integer_rules(max_digit=max_digits, min_digit=min_digits) gbnf_type, rules = generate_gbnf_integer_rules(max_digit=max_digits, min_digit=min_digits)
else: else:
gbnf_type, rules = gbnf_type, [] gbnf_type, rules = gbnf_type, []
@ -407,8 +416,7 @@ def generate_gbnf_rule_for_type(model_name, look_for_markdown_code_block, look_f
return gbnf_type, rules return gbnf_type, rules
def generate_gbnf_grammar(model: Type[BaseModel], look_for_markdown_code_block, look_for_triple_quoted_string, def generate_gbnf_grammar(model: Type[BaseModel], processed_models: set, created_rules: dict) -> (list, bool, bool):
processed_models: set, created_rules: dict) -> list:
""" """
Generate GBnF Grammar Generate GBnF Grammar
@ -420,8 +428,7 @@ def generate_gbnf_grammar(model: Type[BaseModel], look_for_markdown_code_block,
:param look_for_triple_quoted_string :param look_for_triple_quoted_string
:param processed_models: A set of already processed models to prevent infinite recursion. :param processed_models: A set of already processed models to prevent infinite recursion.
:param created_rules: A dict containing already created rules to prevent duplicates. :param created_rules: A dict containing already created rules to prevent duplicates.
:return: A list of GBnF grammar rules in string format. :return: A list of GBnF grammar rules in string format. And two booleans indicating if an extra markdown or triple quoted string is in the grammar.
Example Usage: Example Usage:
``` ```
model = MyModel model = MyModel
@ -454,6 +461,8 @@ def generate_gbnf_grammar(model: Type[BaseModel], look_for_markdown_code_block,
nested_rules = [] nested_rules = []
has_markdown_code_block = False has_markdown_code_block = False
has_triple_quoted_string = False has_triple_quoted_string = False
look_for_markdown_code_block = False
look_for_triple_quoted_string = False
for field_name, field_info in model_fields.items(): for field_name, field_info in model_fields.items():
if not issubclass(model, BaseModel): if not issubclass(model, BaseModel):
field_type, default_value = field_info field_type, default_value = field_info
@ -468,17 +477,16 @@ def generate_gbnf_grammar(model: Type[BaseModel], look_for_markdown_code_block,
format_model_and_field_name(field_name), format_model_and_field_name(field_name),
field_type, is_optional, field_type, is_optional,
processed_models, created_rules, field_info) processed_models, created_rules, field_info)
if ((look_for_markdown_code_block and field_name != "markdown_code_block") or ( look_for_markdown_code_block = True if rule_name == "markdown_string" else False
not look_for_markdown_code_block)) and ( look_for_triple_quoted_string = True if rule_name == "triple_quoted_string" else False
(look_for_triple_quoted_string and field_name != "triple_quoted_string") or ( if not look_for_markdown_code_block and not look_for_triple_quoted_string:
not look_for_triple_quoted_string)):
if rule_name not in created_rules: if rule_name not in created_rules:
created_rules[rule_name] = additional_rules created_rules[rule_name] = additional_rules
model_rule_parts.append(f' ws \"\\\"{field_name}\\\"\" ": " {rule_name}') # Adding escaped quotes model_rule_parts.append(f' ws \"\\\"{field_name}\\\"\" ": " {rule_name}') # Adding escaped quotes
nested_rules.extend(additional_rules) nested_rules.extend(additional_rules)
else: else:
has_triple_quoted_string = look_for_triple_quoted_string and field_name == "triple_quoted_string" and look_for_triple_quoted_string has_triple_quoted_string = look_for_markdown_code_block
has_markdown_code_block = look_for_markdown_code_block and field_name == "markdown_code_block" and look_for_markdown_code_block has_markdown_code_block = look_for_triple_quoted_string
fields_joined = r' "," "\n" '.join(model_rule_parts) fields_joined = r' "," "\n" '.join(model_rule_parts)
model_rule = fr'{model_name} ::= "{{" "\n" {fields_joined} "\n" ws "}}"' model_rule = fr'{model_name} ::= "{{" "\n" {fields_joined} "\n" ws "}}"'
@ -492,12 +500,11 @@ def generate_gbnf_grammar(model: Type[BaseModel], look_for_markdown_code_block,
model_rule += '"\\n" markdown-code-block' model_rule += '"\\n" markdown-code-block'
all_rules = [model_rule] + nested_rules all_rules = [model_rule] + nested_rules
return all_rules return all_rules, has_markdown_code_block, has_triple_quoted_string
def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], look_for_markdown_code_block=False, def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], outer_object_name: str = None,
look_for_triple_quoted_string=False, root_rule_class: str = None, outer_object_content: str = None, list_of_outputs: bool = False) -> str:
root_rule_content: str = None, list_of_outputs: bool = False) -> str:
""" """
Generate GBNF Grammar from Pydantic Models. Generate GBNF Grammar from Pydantic Models.
@ -505,14 +512,12 @@ def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], lo
* grammar. * grammar.
Parameters: Parameters:
- models (List[Type[BaseModel]]): A list of Pydantic models to generate the grammar from. models (List[Type[BaseModel]]): A list of Pydantic models to generate the grammar from.
- look_for_markdown_code_block (bool, optional): Whether to look for Markdown code blocks in the models. If set to True, the generated grammar will have a rule for Markdown code blocks. outer_object_name (str): Outer object name for the GBNF grammar. If None, no outer object will be generated. Eg. "function" for function calling.
- look_for_triple_quoted_string outer_object_content (str): Content for the outer rule in the GBNF grammar. Eg. "function_parameters" or "params" for function calling.
- root_rule_class (str, optional): The name of the root model class. If provided, the generated grammar will have a root rule that matches the specified class. Default is None. list_of_outputs (str, optional): Allows a list of output objects
- root_rule_content (str, optional): The content of the root model rule. This can be used to specify additional constraints or transformations for the root model. Default is None.
Returns: Returns:
- str: The generated GBNF grammar string. str: The generated GBNF grammar string.
Examples: Examples:
models = [UserModel, PostModel] models = [UserModel, PostModel]
@ -525,11 +530,11 @@ def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], lo
processed_models = set() processed_models = set()
all_rules = [] all_rules = []
created_rules = {} created_rules = {}
if root_rule_class is None: if outer_object_name is None:
for model in models: for model in models:
model_rules = generate_gbnf_grammar(model, look_for_markdown_code_block, look_for_triple_quoted_string, model_rules, _, _ = generate_gbnf_grammar(model,
processed_models, created_rules) processed_models, created_rules)
all_rules.extend(model_rules) all_rules.extend(model_rules)
if list_of_outputs: if list_of_outputs:
@ -540,12 +545,14 @@ def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], lo
[format_model_and_field_name(model.__name__) for model in models]) [format_model_and_field_name(model.__name__) for model in models])
all_rules.insert(0, root_rule) all_rules.insert(0, root_rule)
return "\n".join(all_rules) return "\n".join(all_rules)
elif root_rule_class is not None: elif outer_object_name is not None:
root_rule = f"root ::= {format_model_and_field_name(root_rule_class)}\n" if list_of_outputs:
root_rule = fr'root ::= ws "[" {format_model_and_field_name(outer_object_name)} ("," {format_model_and_field_name(outer_object_name)})* "]"' + "\n"
else:
root_rule = f"root ::= {format_model_and_field_name(outer_object_name)}\n"
model_rule = fr'{format_model_and_field_name(outer_object_name)} ::= ws "{{" ws "\"{outer_object_name}\"" ": " grammar-models'
model_rule = fr'{format_model_and_field_name(root_rule_class)} ::= ws "{{" ws "\"{root_rule_class}\"" ": " grammar-models'
if not look_for_markdown_code_block and not look_for_triple_quoted_string:
model_rule += ' ws "}"'
fields_joined = " | ".join( fields_joined = " | ".join(
[fr'{format_model_and_field_name(model.__name__)}-grammar-model' for model in models]) [fr'{format_model_and_field_name(model.__name__)}-grammar-model' for model in models])
@ -553,13 +560,23 @@ def generate_gbnf_grammar_from_pydantic_models(models: List[Type[BaseModel]], lo
mod_rules = [] mod_rules = []
for model in models: for model in models:
mod_rule = fr'{format_model_and_field_name(model.__name__)}-grammar-model ::= ws' mod_rule = fr'{format_model_and_field_name(model.__name__)}-grammar-model ::= ws'
mod_rule += fr'"\"{format_model_and_field_name(model.__name__)}\"" "," ws "\"{root_rule_content}\"" ws ":" ws {format_model_and_field_name(model.__name__)}' + '\n' mod_rule += fr'"\"{format_model_and_field_name(model.__name__)}\"" "," ws "\"{outer_object_content}\"" ws ":" ws {format_model_and_field_name(model.__name__)}' + '\n'
mod_rules.append(mod_rule) mod_rules.append(mod_rule)
grammar_model_rules += "\n" + "\n".join(mod_rules) grammar_model_rules += "\n" + "\n".join(mod_rules)
look_for_markdown_code_block = False
look_for_triple_quoted_string = False
for model in models: for model in models:
model_rules = generate_gbnf_grammar(model, look_for_markdown_code_block, look_for_triple_quoted_string, model_rules, markdown_block, triple_quoted_string = generate_gbnf_grammar(model,
processed_models, created_rules) processed_models, created_rules)
all_rules.extend(model_rules) all_rules.extend(model_rules)
if markdown_block:
look_for_markdown_code_block = True
if triple_quoted_string:
look_for_triple_quoted_string = True
if not look_for_markdown_code_block and not look_for_triple_quoted_string:
model_rule += ' ws "}"'
all_rules.insert(0, root_rule + model_rule + grammar_model_rules) all_rules.insert(0, root_rule + model_rule + grammar_model_rules)
return "\n".join(all_rules) return "\n".join(all_rules)
@ -569,10 +586,10 @@ def get_primitive_grammar(grammar):
Returns the needed GBNF primitive grammar for a given GBNF grammar string. Returns the needed GBNF primitive grammar for a given GBNF grammar string.
Args: Args:
- grammar (str): The string containing the GBNF grammar. grammar (str): The string containing the GBNF grammar.
Returns: Returns:
- str: GBNF primitive grammar string. str: GBNF primitive grammar string.
""" """
type_list = [] type_list = []
if "string-list" in grammar: if "string-list" in grammar:
@ -676,11 +693,11 @@ def format_json_example(example: dict, depth: int) -> str:
Format a JSON example into a readable string with indentation. Format a JSON example into a readable string with indentation.
Args: Args:
- example (dict): JSON example to be formatted. example (dict): JSON example to be formatted.
- depth (int): Indentation depth. depth (int): Indentation depth.
Returns: Returns:
- str: Formatted JSON example string. str: Formatted JSON example string.
""" """
indent = ' ' * depth indent = ' ' * depth
formatted_example = '{\n' formatted_example = '{\n'
@ -697,13 +714,13 @@ def generate_text_documentation(pydantic_models: List[Type[BaseModel]], model_pr
Generate text documentation for a list of Pydantic models. Generate text documentation for a list of Pydantic models.
Args: Args:
- pydantic_models (List[Type[BaseModel]]): List of Pydantic model classes. pydantic_models (List[Type[BaseModel]]): List of Pydantic model classes.
- model_prefix (str): Prefix for the model section. model_prefix (str): Prefix for the model section.
- fields_prefix (str): Prefix for the fields section. fields_prefix (str): Prefix for the fields section.
- documentation_with_field_description (bool): Include field descriptions in the documentation. documentation_with_field_description (bool): Include field descriptions in the documentation.
Returns: Returns:
- str: Generated text documentation. str: Generated text documentation.
""" """
documentation = "" documentation = ""
pyd_models = [(model, True) for model in pydantic_models] pyd_models = [(model, True) for model in pydantic_models]
@ -759,14 +776,14 @@ def generate_field_text(field_name: str, field_type: Type[Any], model: Type[Base
Generate text documentation for a Pydantic model field. Generate text documentation for a Pydantic model field.
Args: Args:
- field_name (str): Name of the field. field_name (str): Name of the field.
- field_type (Type[Any]): Type of the field. field_type (Type[Any]): Type of the field.
- model (Type[BaseModel]): Pydantic model class. model (Type[BaseModel]): Pydantic model class.
- depth (int): Indentation depth in the documentation. depth (int): Indentation depth in the documentation.
- documentation_with_field_description (bool): Include field descriptions in the documentation. documentation_with_field_description (bool): Include field descriptions in the documentation.
Returns: Returns:
- str: Generated text documentation for the field. str: Generated text documentation for the field.
""" """
indent = ' ' * depth indent = ' ' * depth
@ -824,11 +841,11 @@ def format_multiline_description(description: str, indent_level: int) -> str:
Format a multiline description with proper indentation. Format a multiline description with proper indentation.
Args: Args:
- description (str): Multiline description. description (str): Multiline description.
- indent_level (int): Indentation level. indent_level (int): Indentation level.
Returns: Returns:
- str: Formatted multiline description. str: Formatted multiline description.
""" """
indent = ' ' * indent_level indent = ' ' * indent_level
return indent + description.replace('\n', '\n' + indent) return indent + description.replace('\n', '\n' + indent)
@ -840,13 +857,13 @@ def save_gbnf_grammar_and_documentation(grammar, documentation, grammar_file_pat
Save GBNF grammar and documentation to specified files. Save GBNF grammar and documentation to specified files.
Args: Args:
- grammar (str): GBNF grammar string. grammar (str): GBNF grammar string.
- documentation (str): Documentation string. documentation (str): Documentation string.
- grammar_file_path (str): File path to save the GBNF grammar. grammar_file_path (str): File path to save the GBNF grammar.
- documentation_file_path (str): File path to save the documentation. documentation_file_path (str): File path to save the documentation.
Returns: Returns:
- None None
""" """
try: try:
with open(grammar_file_path, 'w') as file: with open(grammar_file_path, 'w') as file:
@ -868,10 +885,10 @@ def remove_empty_lines(string):
Remove empty lines from a string. Remove empty lines from a string.
Args: Args:
- string (str): Input string. string (str): Input string.
Returns: Returns:
- str: String with empty lines removed. str: String with empty lines removed.
""" """
lines = string.splitlines() lines = string.splitlines()
non_empty_lines = [line for line in lines if line.strip() != ""] non_empty_lines = [line for line in lines if line.strip() != ""]
@ -879,11 +896,11 @@ def remove_empty_lines(string):
return string_no_empty_lines return string_no_empty_lines
def generate_and_save_gbnf_grammar_and_documentation(pydantic_model_list, look_for_markdown_code_block=False, def generate_and_save_gbnf_grammar_and_documentation(pydantic_model_list,
look_for_triple_quoted_string=False,
grammar_file_path="./generated_grammar.gbnf", grammar_file_path="./generated_grammar.gbnf",
documentation_file_path="./generated_grammar_documentation.md", documentation_file_path="./generated_grammar_documentation.md",
root_rule_class: str = None, root_rule_content: str = None, outer_object_name: str = None,
outer_object_content: str = None,
model_prefix: str = "Output Model", model_prefix: str = "Output Model",
fields_prefix: str = "Output Fields", fields_prefix: str = "Output Fields",
list_of_outputs: bool = False, list_of_outputs: bool = False,
@ -892,33 +909,29 @@ def generate_and_save_gbnf_grammar_and_documentation(pydantic_model_list, look_f
Generate GBNF grammar and documentation, and save them to specified files. Generate GBNF grammar and documentation, and save them to specified files.
Args: Args:
- pydantic_model_list: List of Pydantic model classes. pydantic_model_list: List of Pydantic model classes.
- look_for_markdown_code_block (bool): Whether to look for markdown code blocks in field descriptions. grammar_file_path (str): File path to save the generated GBNF grammar.
- look_for_triple_quoted_string (bool): Whether to look for triple-quoted strings in field descriptions. documentation_file_path (str): File path to save the generated documentation.
- grammar_file_path (str): File path to save the generated GBNF grammar. outer_object_name (str): Outer object name for the GBNF grammar. If None, no outer object will be generated. Eg. "function" for function calling.
- documentation_file_path (str): File path to save the generated documentation. outer_object_content (str): Content for the outer rule in the GBNF grammar. Eg. "function_parameters" or "params" for function calling.
- root_rule_class (str): Root rule class for the GBNF grammar. model_prefix (str): Prefix for the model section in the documentation.
- root_rule_content (str): Content for the root rule in the GBNF grammar. fields_prefix (str): Prefix for the fields section in the documentation.
- model_prefix (str): Prefix for the model section in the documentation. list_of_outputs (bool): Whether the output is a list of items.
- fields_prefix (str): Prefix for the fields section in the documentation. documentation_with_field_description (bool): Include field descriptions in the documentation.
- list_of_outputs (bool): Whether the output is a list of items.
- documentation_with_field_description (bool): Include field descriptions in the documentation.
Returns: Returns:
- None None
""" """
documentation = generate_text_documentation(pydantic_model_list, model_prefix, fields_prefix, documentation = generate_text_documentation(pydantic_model_list, model_prefix, fields_prefix,
documentation_with_field_description=documentation_with_field_description) documentation_with_field_description=documentation_with_field_description)
grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, look_for_markdown_code_block, grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, outer_object_name,
look_for_triple_quoted_string, root_rule_class, outer_object_content, list_of_outputs)
root_rule_content, list_of_outputs)
grammar = remove_empty_lines(grammar) grammar = remove_empty_lines(grammar)
save_gbnf_grammar_and_documentation(grammar, documentation, grammar_file_path, documentation_file_path) save_gbnf_grammar_and_documentation(grammar, documentation, grammar_file_path, documentation_file_path)
def generate_gbnf_grammar_and_documentation(pydantic_model_list, look_for_markdown_code_block=False, def generate_gbnf_grammar_and_documentation(pydantic_model_list, outer_object_name: str = None,
look_for_triple_quoted_string=False, root_rule_class: str = None, outer_object_content: str = None,
root_rule_content: str = None,
model_prefix: str = "Output Model", model_prefix: str = "Output Model",
fields_prefix: str = "Output Fields", list_of_outputs: bool = False, fields_prefix: str = "Output Fields", list_of_outputs: bool = False,
documentation_with_field_description=True): documentation_with_field_description=True):
@ -926,33 +939,28 @@ def generate_gbnf_grammar_and_documentation(pydantic_model_list, look_for_markdo
Generate GBNF grammar and documentation for a list of Pydantic models. Generate GBNF grammar and documentation for a list of Pydantic models.
Args: Args:
- pydantic_model_list: List of Pydantic model classes. pydantic_model_list: List of Pydantic model classes.
- look_for_markdown_code_block (bool): Whether to look for markdown code blocks in field descriptions. outer_object_name (str): Outer object name for the GBNF grammar. If None, no outer object will be generated. Eg. "function" for function calling.
- look_for_triple_quoted_string (bool): Whether to look for triple-quoted strings in field descriptions. outer_object_content (str): Content for the outer rule in the GBNF grammar. Eg. "function_parameters" or "params" for function calling.
- root_rule_class (str): Root rule class for the GBNF grammar. model_prefix (str): Prefix for the model section in the documentation.
- root_rule_content (str): Content for the root rule in the GBNF grammar. fields_prefix (str): Prefix for the fields section in the documentation.
- model_prefix (str): Prefix for the model section in the documentation. list_of_outputs (bool): Whether the output is a list of items.
- fields_prefix (str): Prefix for the fields section in the documentation. documentation_with_field_description (bool): Include field descriptions in the documentation.
- list_of_outputs (bool): Whether the output is a list of items.
- documentation_with_field_description (bool): Include field descriptions in the documentation.
Returns: Returns:
- tuple: GBNF grammar string, documentation string. tuple: GBNF grammar string, documentation string.
""" """
documentation = generate_text_documentation(copy(pydantic_model_list), model_prefix, fields_prefix, documentation = generate_text_documentation(copy(pydantic_model_list), model_prefix, fields_prefix,
documentation_with_field_description=documentation_with_field_description) documentation_with_field_description=documentation_with_field_description)
grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, look_for_markdown_code_block, grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, outer_object_name,
look_for_triple_quoted_string, root_rule_class, outer_object_content, list_of_outputs)
root_rule_content, list_of_outputs)
grammar = remove_empty_lines(grammar + get_primitive_grammar(grammar)) grammar = remove_empty_lines(grammar + get_primitive_grammar(grammar))
return grammar, documentation return grammar, documentation
def generate_gbnf_grammar_and_documentation_from_dictionaries(dictionaries: List[dict], def generate_gbnf_grammar_and_documentation_from_dictionaries(dictionaries: List[dict],
look_for_markdown_code_block=False, outer_object_name: str = None,
look_for_triple_quoted_string=False, outer_object_content: str = None,
root_rule_class: str = None,
root_rule_content: str = None,
model_prefix: str = "Output Model", model_prefix: str = "Output Model",
fields_prefix: str = "Output Fields", fields_prefix: str = "Output Fields",
list_of_outputs: bool = False, list_of_outputs: bool = False,
@ -961,25 +969,22 @@ def generate_gbnf_grammar_and_documentation_from_dictionaries(dictionaries: List
Generate GBNF grammar and documentation from a list of dictionaries. Generate GBNF grammar and documentation from a list of dictionaries.
Args: Args:
- dictionaries (List[dict]): List of dictionaries representing Pydantic models. dictionaries (List[dict]): List of dictionaries representing Pydantic models.
- look_for_markdown_code_block (bool): Whether to look for markdown code blocks in field descriptions. outer_object_name (str): Outer object name for the GBNF grammar. If None, no outer object will be generated. Eg. "function" for function calling.
- look_for_triple_quoted_string (bool): Whether to look for triple-quoted strings in field descriptions. outer_object_content (str): Content for the outer rule in the GBNF grammar. Eg. "function_parameters" or "params" for function calling.
- root_rule_class (str): Root rule class for the GBNF grammar. model_prefix (str): Prefix for the model section in the documentation.
- root_rule_content (str): Content for the root rule in the GBNF grammar. fields_prefix (str): Prefix for the fields section in the documentation.
- model_prefix (str): Prefix for the model section in the documentation. list_of_outputs (bool): Whether the output is a list of items.
- fields_prefix (str): Prefix for the fields section in the documentation. documentation_with_field_description (bool): Include field descriptions in the documentation.
- list_of_outputs (bool): Whether the output is a list of items.
- documentation_with_field_description (bool): Include field descriptions in the documentation.
Returns: Returns:
- tuple: GBNF grammar string, documentation string. tuple: GBNF grammar string, documentation string.
""" """
pydantic_model_list = create_dynamic_models_from_dictionaries(dictionaries) pydantic_model_list = create_dynamic_models_from_dictionaries(dictionaries)
documentation = generate_text_documentation(copy(pydantic_model_list), model_prefix, fields_prefix, documentation = generate_text_documentation(copy(pydantic_model_list), model_prefix, fields_prefix,
documentation_with_field_description=documentation_with_field_description) documentation_with_field_description=documentation_with_field_description)
grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, look_for_markdown_code_block, grammar = generate_gbnf_grammar_from_pydantic_models(pydantic_model_list, outer_object_name,
look_for_triple_quoted_string, root_rule_class, outer_object_content, list_of_outputs)
root_rule_content, list_of_outputs)
grammar = remove_empty_lines(grammar + get_primitive_grammar(grammar)) grammar = remove_empty_lines(grammar + get_primitive_grammar(grammar))
return grammar, documentation return grammar, documentation
@ -1145,3 +1150,6 @@ def convert_dictionary_to_to_pydantic_model(dictionary: dict, model_name: str =
custom_model = create_model(model_name, **fields) custom_model = create_model(model_name, **fields)
return custom_model return custom_model