Refactored Grammar Generator
Added example and usage instruction.
This commit is contained in:
parent
ebca51be4f
commit
2b42f5254c
2 changed files with 223 additions and 115 deletions
100
examples/pydantic-models-to-grammar-examples.py
Normal file
100
examples/pydantic-models-to-grammar-examples.py
Normal 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
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue