| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- # Function calling example using pydantic models.
- import json
- from enum import Enum
- from typing import Union, Optional
- import requests
- from pydantic import BaseModel, Field
- import importlib
- 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
- # A example structured output based on pydantic models. The LLM will create an entry for a Book database out of an unstructured text.
- class Category(Enum):
- """
- The category of the book.
- """
- Fiction = "Fiction"
- NonFiction = "Non-Fiction"
- class Book(BaseModel):
- """
- Represents an entry about a book.
- """
- title: str = Field(..., description="Title of the book.")
- author: str = Field(..., description="Author of the book.")
- published_year: Optional[int] = Field(..., description="Publishing year of the book.")
- keywords: list[str] = Field(..., description="A list of keywords.")
- category: Category = Field(..., description="Category of the book.")
- summary: str = Field(..., description="Summary of the book.")
- # We need no additional parameters other than our list of pydantic models.
- gbnf_grammar, documentation = generate_gbnf_grammar_and_documentation([Book])
- system_message = "You are an advanced AI, tasked to create a dataset entry in JSON for a Book. The following is the expected output model:\n\n" + documentation
- text = """The Feynman Lectures on Physics is a physics textbook based on some lectures by Richard Feynman, a Nobel laureate who has sometimes been called "The Great Explainer". The lectures were presented before undergraduate students at the California Institute of Technology (Caltech), during 1961–1963. The book's co-authors are Feynman, Robert B. Leighton, and Matthew Sands."""
- prompt = f"<|im_start|>system\n{system_message}<|im_end|>\n<|im_start|>user\n{text}<|im_end|>\n<|im_start|>assistant"
- text = create_completion(prompt=prompt, grammar=gbnf_grammar)
- json_data = json.loads(text)
- print(Book(**json_data))
|