Versatile LLM Tool Use (Function Calling) package for Laravel, compatible with all LLMs, enabling LLM to execute actual code functions (unlike LLMs' built-in capabilities).
Unlock Powerful Large Language Model (LLM) Interactions in Your Laravel Application.
This Laravel package enables versatile LLM Tool Use (Function Calling), allowing LLMs to decide and execute function calls based on the prompt. Unlike built-in capabilities that may only list functions, this package makes the actual calls, ensuring dynamic execution. Compatible with all LLMs, it enhances automation and interactivity in your applications.
ℹ️ OpenRouter is used as the default LLM provider in the package. The package is structured flexibly, allowing you to use your choice of LLM provider such as OpenAI, Claude, Gemini etc. (OpenRouter is a unified interface for LLMs.)
This is the contents of the published config file:
return ['env_variables'=> ['api_key'=>env('OPENROUTER_API_KEY'),'api_endpoint'=>env('OPENROUTER_API_ENDPOINT','https://openrouter.ai/api/v1/'),'default_model'=>'Your selection of the default model', ],'functions_yml_path'=>'Path to the functions yml file','schemas'=> ['function_payload_schema_path'=>'Path to the function payload schema yml file','function_results_schema_path'=>'Path to the function results schema yml file', ],'instructions'=> ['prompt_function_instructions'=>'Instructions for the LLM about how to make use of provided functions, prompt and function_payload_schema in order to get the desired response','function_results_instructions'=>'Instructions for the LLM about how to make use of provided function_results, prompt and function_results_schema in order to get the desired response','generate_prompt_function_instructions'=>'Instructions for generating specific/customised prompt_function_instructions', ],'function_signature_mapping'=>'Function signature mapping data (FunctionSignatureMappingData)',];
⚙️ Configuration
After publishing the package configuration file, steps you should be following:
➡️ env_variables Add following environment variables to your .env file in case you will be using OpenRouter as LLM provider (If your choice of LLM provider is different, then this is not required):
OPENROUTER_API_ENDPOINT: The endpoint URL for the OpenRouter API (default: https://openrouter.ai/api/v1/).
OPENROUTER_API_KEY: Your API key for accessing the OpenRouter API. You can obtain this key from the OpenRouter Dashboard. (e.g. sk-or-v1... )
OPENROUTER_DEFAULT_MODEL: Default model that will be used in the package - necessary for generateInstructions functionality (In codebase, you can still specify any model that you want for OpenRouter requests). You can check model list from OpenRouter Models. (e.g. 'mistralai/mistral-7b-instruct:free')
➡️ functions_yml_path Add path of the functions yml file which is the callable function list of your project. The Generate Function List section provides a deep dive into functions yml file; how to define functions, format needs to be used etc. (e.g. __DIR__ . '/../resources/functions.yml').
➡️ function_signature_mapping: Add mapping for function signature namings. The Define Function Signature Mapping section provides a deep dive into function signature mapping. (e.g.new FunctionSignatureMappingData(['parameters' => new MappingData(['path' => 'input_schema.properties', 'type' => 'array']), ...]) )
➡️ schemas Add path of the schemas needed for function payload and function results payload.
function_payload_schema_path: Path of the function payload schema. The Function Payload Schema section provides a deep dive into function payload schema. (e.g. __DIR__ . '/../resources/schemas/function_payload_schema.yml')
function_results_schema_path: Path of the function results schema. The Function Results Schema section provides a deep dive into function results schema. (e.g. __DIR__ . '/../resources/schemas/function_results_schema.yml')
➡️ instructions: Add instructions for prompt_function_instructions, function_results_instructions and generate_prompt_function_instructions.
prompt_function_instructions: Instructions for the LLM used for prompt function payload. The Generate Prompt Function Instructions section provides a deep dive into prompt function instructions. (e.g. You are an AI assistant that strictly follows instructions and provides response ...)
function_results_instructions: Instructions for the LLM used for function results payload. The Prepare Function Results Payload section provides a deep dive into function results instructions. (e.g. You will strictly follow the instructions as ...)
generate_prompt_function_instructions: Instructions for generating prompt_function_instructions by using generateInstructions function. The Generate Prompt Function Instructions section provides a deep dive into generating prompt function instructions. (e.g. Your role is to analyze the provided "functions" and ...)
⚡ Quick Usage Guide
This package is designed to be flexible, but for an easy quick start, follow the steps below:
Create a file named functions.yml under a directory of your choice. Modify config file accordingly for functions_yml_path. (Check Generate Function List for further details).
'functions_yml_path'=>__DIR__.'/../resources/functions.yml',// functions.yml is located under resources folder in this example.
Generate function list as in Use generateFunctionList method. It automatically adds functions to functions.yml file with all possible function signature details/descriptions considering also function's docblock.
(Add all classes and functions that LLM should take into account for its decisions, as much info as possible in FunctionData for better performance):
$class =Example::class;$functionDataA =newFunctionData(['function_name'=>'getFinancialData','parameters'=> [newParameterData(['name'=>'userId','type'=>'int','required'=>true,'description'=>'The unique identifier for the user.','example'=>12345 ]),newParameterData(['name'=>'startDate','type'=>'string','required'=>true,'description'=>'The starting date for the timeframe (inclusive).','example'=>'2023-01-01' ]),newParameterData(['name'=>'endDate','type'=>'string','required'=>true,'description'=>'The ending date for the timeframe (inclusive).','example'=>'2023-01-31' ]), ],'visibility'=>VisibilityType::PUBLIC,'description'=>'Retrieves financial data for a specific user and timeframe.','return'=>newReturnData(['type'=>'object','description'=>'An object containing details like totalAmount, transactions (array), and other relevant financial data.' ]),]);$functionDataB =newFunctionData(['function_name'=>'categorizeTransactions','parameters'=> [newParameterData(['name'=>'transactions','type'=>'array','required'=>true,'description'=>'An array of transactions with details like amount, date, and description.','example'=> [ ['amount'=>100,'date'=>'2023-01-01','description'=>'Groceries'], ['amount'=>50,'date'=>'2023-01-02','description'=>'Entertainment'] ] ]), ],'visibility'=>VisibilityType::PUBLIC,'description'=>'Categorizes a list of transactions based on predefined rules or machine learning models.','return'=>newReturnData(['type'=>'array','description'=>'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.','example'=> [ ['amount'=>100,'date'=>'2023-01-01','description'=>'Groceries','category'=>'Food','confidenceScore'=>0.95], ['amount'=>50,'date'=>'2023-01-02','description'=>'Entertainment','category'=>'Leisure','confidenceScore'=>0.8] ] ]),]);$functions = [$functionDataA, $functionDataB];$fileName =__DIR__.'/../resources/functions.yml'; // Path and the name of the file that function list will be generated// Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed).LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
Create yml file for function payload schema and modify function_payload_schema_path in config accordingly. (Check Function Payload Schema for further details).
Config file path for function_payload_schema_path:
'function_payload_schema_path'=>__DIR__.'/../resources/schemas/function_payload_schema.yml',// function_payload_schema.yml is located under resources/schemas folder in this example.
Add OPENROUTER_API_KEY, OPENROUTER_DEFAULT_MODEL and OPENROUTER_API_ENDPOINT in your env file - as defined in env_variables in config (OPENROUTER_API_ENDPOINT already has default value which can be skipped, OPENROUTER_API_KEY and OPENROUTER_DEFAULT_MODEL should be set as described in Configuration section.)
Leave prompt_function_instructions in config untouched or check out Generate Prompt Function Instructions in order to generate customized new instructions.
Make Laravel OpenRouter request for Tool Use (Function Calling) so that LLM will make decision which functions to call for the given prompt. (Note:Laravel OpenRouter already comes with this package - as default LLM provider - so no need for a separate installation)
$prompt ='Can tell me Mr. Boolean Bob credit score?';$model =config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models$content =LaravelPromptAlchemist::preparePromptFunctionPayload($prompt); // Prepare payload for the request.$messageData =newMessageData(['content'=>json_encode($content),'role'=>RoleType::USER,]);$chatData =newChatData(['messages'=> [ $messageData, ],'model'=> $model,'temperature'=>0.1,// Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.]);// Send OpenRouter request$response =LaravelOpenRouter::chatRequest($chatData);
// $response = LaravelOpenRouter::chatRequest($chatData);$responseContentData =str_replace("\n","", (Arr::get($response->choices[0],'message.content'))); // Get content from the response.$llmReturnedFunctions =json_decode($responseContentData,true); // Functions returned from LLM.// Foreach $llmReturnedFunctions and get each function to validate:$llmReturnedFunction = [ // Sample LLM returned function"function_name"=>"getFinancialData","parameters"=> [ [ "name"=>"userId","type"=>"int"], [ "name"=>"startDate","type"=>"string"], [ "name"=>"endDate","type"=>"string"], ],'class_name'=>'MoeMizrak\LaravelPromptAlchemist\Tests\Example'];// Formed LLM returned function data (FunctionData).$llmReturnedFunctionData =LaravelPromptAlchemist::formLlmReturnedFunctionData($llmReturnedFunction);
Invoke validateFunctionSignature for function signature validation of $llmReturnedFunctionData:
And finally, call functions returned from the LLM which are necessary for answering the prompt (Since function signature is validated, it is now safe to call LLM returned functions). (Check Call Function (Actual Function Call) for further details).
(Note: You need to set parameter values to be able to call the function. Required parameters have to be set, otherwise ErrorData is returned. It is a better practice to set the parameter values as checking validateFunctionSignature since type of provided values also gets validated if set, but it can also be done in this step.)
Set parameter values before calling function as below.
// Create parameter values, you just need parameter name and its value in correct type (int, string, array, object ...)$parameters = [newParameterData(['name'=>'userId','value'=>99,// int userId ]),newParameterData(['name'=>'startDate','value'=>'2023-06-01',// string startDate ]),newParameterData(['name'=>'endDate','value'=>'2023-07-01',// string endDate ]),];if (true=== $isValid) {// Set parameter values. $llmReturnedFunctionData->setParameterValues($parameters);}
Sample function result ($functionResultData) DTO object:
output:FunctionResultData(['function_name'=>'getFinancialData','result'=> (object) ['totalAmount'=>1000.0,'transactions'=> [ ['amount'=>100,'date'=>'2023-01-01','description'=>'Groceries'], ['amount'=>200,'date'=>'2023-01-02','description'=>'Utilities'], ],'message'=>'Retrieved financial data for user 99 from 2023-06-01 to 2023-07-01' ],])
Where function_name is the name of the function called as the name suggested, and result is the function call result can be anything (void, array, object, bool etc. whatever your function returns to.)
Optionally, you can also send function results to LLM so that regarding function_results_schema, it will return the answer. (Check Prepare Function Results Payload for further details)
$prompt ='Can tell me Mr. Boolean Bob credit score?';$model =config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models$functionResults = [newFunctionResultData(['function_name'=>'getFinancialData','result'=> ['totalAmount'=>122,'transactions'=> [ ['amount'=>12,'date'=>'2023-02-02','description'=>'food', ], ] ] ]),newFunctionResultData(['function_name'=>'getCreditScore','result'=> ['creditScore'=>0.8,'summary'=>'reliable', ] ]),...];// Prepare function results payload for the request.$content =LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults);$messageData =newMessageData(['content'=>json_encode($content),'role'=>RoleType::USER,]);$chatData =newChatData(['messages'=> [ $messageData, ],'model'=> $model,'temperature'=>0.1,// Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.]);// Send OpenRouter request for function results$response =LaravelOpenRouter::chatRequest($chatData);
Where $response is the function_results_schema formed answer returned from the LLM according to function_results_instructions.
🎨 Usage
This package provides two ways to interact with the Laravel Prompt Alchemist package:
Using the LaravelPromptAlchemist facade (i.e. Using Facade).
The LaravelPromptAlchemist facade offers a convenient way to make Laravel Prompt Alchemist requests. Following sections will lead you through further configuration and usage of LaravelPromptAlchemist facade.
Generate Function List
In order to generate function list (functions.yml), you can either:
Manually create function list
Create function signatures manually using your chosen naming convention in a flat associative array structure.
ℹ️ Ensure coherence with function_signature_mapping, which aligns your naming practices with the package format for integration. (Note: Please refer to Define Function Signature Mapping section for which function signatures should be defined in function list yml file for better performance)
Create yml file for function list with any preferred naming and directory.
e.g. You can create a file named functions.yml in the resources folder under your project
__DIR__ . '/../resources/functions.yml'
Define functions in yml file that you will be using for the package (for Tool Use - Function Calling).
e.g. You can create functions as samples below in a flat associative array structure or your choice of function naming convention.
Recommended naming convention (generateFunctionList method for generating a function list, also outputs the function list in this format):
-
function_name: getFinancialData
parameters: [{ name: userId, type: int, required: true, description: 'The unique identifier for the user.', example: 12345 }, { name: startDate, type: string, required: true, description: 'The starting date for the timeframe (inclusive).', example: '2023-01-01' }, { name: endDate, type: string, required: true, description: 'The ending date for the timeframe (inclusive).', example: '2023-01-31' }]
visibility: public
description: 'Retrieves financial data for a specific user and timeframe. '
return: { type: object, description: 'An object containing details like totalAmount, transactions (array), and other relevant financial data.' }
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\Example
-
function_name: categorizeTransactions
parameters: [{ name: transactions, type: array, required: true, description: 'An array of transactions with details like amount, date, and description.', example: [{ amount: 100, date: '2023-01-01', description: 'Groceries' }, { amount: 50, date: '2023-01-02', description: 'Entertainment' }] }]
visibility: public
description: 'Categorizes a list of transactions based on predefined rules or machine learning models. '
return: { type: array, description: 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.', example: [{ amount: 100, date: '2023-01-01', description: 'Groceries', category: 'Food', confidenceScore: 0.95 }, { amount: 50, date: '2023-01-02', description: 'Entertainment', category: 'Leisure', confidenceScore: 0.8 }] }
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\Example
Or, another naming convention in a flat associative array structure can be as:
- function: getFinancialData
input_schema:
type: object
properties:
- name: userId
type: int
required: true
description: 'The unique identifier for the user.'
- name: startDate
type: string
required: true
description: 'The starting date for the timeframe (inclusive).'
- name: endDate
type: string
required: true
description: 'The ending date for the timeframe (inclusive).'
description: 'Retrieves financial data for a specific user and timeframe.'
return:
type: object
description: 'An object containing details like totalAmount, transactions (array), and other relevant financial data.'
class: MoeMizrak\LaravelPromptAlchemist\Tests\Example
- function: categorizeTransactions
input_schema:
properties:
- name: transactions
type: array
required: true
description: 'An array of transactions with details like amount, date, and description.'
description: 'Categorizes a list of transactions based on predefined rules or machine learning models.'
return:
type: array
description: 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.'
class: MoeMizrak\LaravelPromptAlchemist\Tests\Example
Use generateFunctionList method
Use generateFunctionList function to generate a detailed function list from a given class and write it to a file in yml format.
(For each different class name, you need to run the generateFunctionList to generate function list - functions.yml, list gets appended whenever the generateFunctionList is called)
ℹ️ This is a good practice to automate the generation of function list. The fields that are defined in the function description (functionData) overwrites the function predefined fields if exists. If a function signature field is missing then it is added to function list definitions.
$class =Example::class; // Or you can give fully qualified class name string as $class = 'MoeMizrak\LaravelPromptAlchemist\Tests\Example'// Function descriptions that will be used to generate function list (functions.yml) $functionDataA =newFunctionData(['function_name'=>'getFinancialData','parameters'=> [newParameterData(['name'=>'userId','type'=>'int','required'=>true,'description'=>'The unique identifier for the user.','example'=>12345 ]),newParameterData(['name'=>'startDate','type'=>'string','required'=>true,'description'=>'The starting date for the timeframe (inclusive).','example'=>'2023-01-01' ]),newParameterData(['name'=>'endDate','type'=>'string','required'=>true,'description'=>'The ending date for the timeframe (inclusive).','example'=>'2023-01-31' ]), ],'visibility'=>VisibilityType::PUBLIC,'description'=>'Retrieves financial data for a specific user and timeframe.','return'=>newReturnData(['type'=>'object','description'=>'An object containing details like totalAmount, transactions (array), and other relevant financial data.' ]),]);$functionDataB =newFunctionData(['function_name'=>'categorizeTransactions','parameters'=> [newParameterData(['name'=>'transactions','type'=>'array','required'=>true,'description'=>'An array of transactions with details like amount, date, and description.','example'=> [ ['amount'=>100,'date'=>'2023-01-01','description'=>'Groceries'], ['amount'=>50,'date'=>'2023-01-02','description'=>'Entertainment'] ] ]), ],'visibility'=>VisibilityType::PUBLIC,'description'=>'Categorizes a list of transactions based on predefined rules or machine learning models.','return'=>newReturnData(['type'=>'array','description'=>'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.','example'=> [ ['amount'=>100,'date'=>'2023-01-01','description'=>'Groceries','category'=>'Food','confidenceScore'=>0.95], ['amount'=>50,'date'=>'2023-01-02','description'=>'Entertainment','category'=>'Leisure','confidenceScore'=>0.8] ] ]),]);$functions = [$functionDataA, $functionDataB];$fileName =__DIR__.'/../resources/functions.yml'; // Path and the name of the file that function list will be generated// Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed).LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
Alternative way for creating the functions array; when function signatures and docblock are well-defined, simply adding function names suffices to create a comprehensive function list:
$class =Example::class; // Or you can give fully qualified class name string as $class = 'MoeMizrak\LaravelPromptAlchemist\Tests\Example'// Name of the functions that will be added to the list - functions that will be used for Tool Use (Function Calling)$functions = ['getFinancialData','categorizeTransactions',];$fileName =__DIR__.'/../resources/functions.yml'; // Path and the name of the file that function list will be generated// Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed).LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
Sample of well-defined function signature and docblock. (Docblock description with enough info, type-hinted params, return type declaration and return tag description in docblock, parameter descriptions in docblock):
/** * This public function is intended for testing purposes. It accepts a string and int parameters and returns a string. * It has additional parameter descriptions and detailed docblock. * * @paramstring $stringParam This is the string param description * @paramint $intParam This is the int param description * * @returnstring This is the return value description */publicfunctiondetailedDocBlockFunction(string $stringParam,int $intParam =2):string{return'detailed docblock function return value '. $stringParam .' '. $intParam;}
Sample of poorly-defined function signature and docblock. (no docblock, no type-hint for params, no return type declaration):
functionnoExtraInfoProvidedFunction($stringParam, $intParam){return'missing parameter and docblock function return value '. $stringParam .' '. $intParam;}
Sample of partially-defined function signature and docblock:
/** * This public function is intended for testing purposes. * It has missing parameter descriptions and missing type-hint. * * @returnstring This is the return value description */publicfunctionfunctionWithSomeMissingDocBlockAndMissingTypeHint($stringParam, $intParam):string{return'missing parameter docblock function return value '. $stringParam .' '. $intParam;}
Based on your best practices in your codebase, you can choose how to generate function list (functions.yml).
If functions are well-defined in your codebase, you can use alternative approach to send the function names in functions array with no additional info (generateFunctionList does everything for you).
If functions are poorly-defined in your codebase, then it is best to use the main approach by creating FunctionData DTO for each function and setting function related info/descriptions to create functions array.
If functions are partially-defined in your codebase, then similarly using the main approach is the best option since more info is better for LLM to know about your functions to make the best decision.
Note: You can just add missing/ambiguous/poorly-defined fields in FunctionData DTO and skip the descriptions/fields that are well-defined since generateFunctionList analyses function and adds all possible information about the function. Also note that fields added to FunctionData DTO overwrite the existing predefined descriptions/fields in the function . (Basically if a field is added to FunctionData DTO, it is taken into account; if a field is NOT added to FunctionData and exists in function declaration then this predefined description/field is added to function list).
Define Function Signature Mapping
Based on the function list (functions.yml), align your naming practices for functions with the package format for integration. It is better practice to set all possible fields for function signature (FunctionSignatureMappingData) for better performance since more info provided for LLM means better result.
function_signature_mapping needs to be defined in the config file as following examples:
(As shown in examples below, use '[]' for the arrays. In package, it is replaced with the index key as e.g. parameters[].name becomes parameters.{key}.name which is parameters.0.name for the first array index, parameters.1.name for the second etc.)
'function_signature_mapping'=>newFunctionSignatureMappingData(['function_name'=>newMappingData(['path'=>'function_name','type'=>'string', ]),'function_description'=>newMappingData(['path'=>'description','type'=>'string', ]),'function_visibility'=>newMappingData(['path'=>'visibility','type'=>'string', ]),'function_return_type'=>newMappingData(['path'=>'return.type','type'=>'string', ]),'function_return_description'=>newMappingData(['path'=>'return.description','type'=>'string', ]),'function_return_example'=>newMappingData(['path'=>'return.example','type'=>'mixed', ]),'parameters'=>newMappingData(['path'=>'parameters',// could be arguments, parameter_definitions, input_schema.properties, parameters.properties'type'=>'array', ]),'parameter_name'=>newMappingData(['path'=>'parameters[].name',// since parameters field is array, '[]' states the index key which will be resolved in the package as 'parameters.0.name' for the first array and so on.'type'=>'string', ]),'parameter_type'=>newMappingData(['path'=>'parameters[].type','type'=>'string', ]),'parameter_required_info'=>newMappingData(['path'=>'parameters[].required','type'=>'boolean', ]),'parameter_description'=>newMappingData(['path'=>'parameters[].description','type'=>'string', ]),'parameter_example'=>newMappingData(['path'=>'parameters[].example','type'=>'mixed', ]),'parameter_default'=>newMappingData(['path'=>'parameters[].default','type'=>'mixed', ]),'class_name'=>newMappingData(['path'=>'class_name','type'=>'string', ])]),
'function_signature_mapping'=>newFunctionSignatureMappingData(['function_name'=>newMappingData(['path'=>'function','type'=>'string', ]),'function_description'=>newMappingData(['path'=>'description','type'=>'string', ]),'function_visibility'=>newMappingData(['path'=>'visibility','type'=>'string', ]),'function_return_type'=>newMappingData(['path'=>'return.type','type'=>'string', ]),'function_return_description'=>newMappingData(['path'=>'return.description','type'=>'string', ]),'function_return_example'=>newMappingData(['path'=>'return.example','type'=>'mixed', ]),'input_schema_type'=>newMappingData(['path'=>'input_schema.type','type'=>'string', ]),'parameters'=>newMappingData(['path'=>'input_schema.properties','type'=>'array', ]),'parameter_name'=>newMappingData(['path'=>'input_schema.properties[].name',// since properties field is array, '[]' states the index key which will be resolved in the package as 'properties.0.name' for the first array and so on.'type'=>'string', ]),'parameter_type'=>newMappingData(['path'=>'input_schema.properties[].type','type'=>'string', ]),'parameter_required_info'=>newMappingData(['path'=>'input_schema.properties[].required','type'=>'boolean', ]),'parameter_description'=>newMappingData(['path'=>'input_schema.properties[].description','type'=>'string', ]),'parameter_example'=>newMappingData(['path'=>'input_schema.properties[].example','type'=>'mixed', ]),'parameter_default'=>newMappingData(['path'=>'input_schema.properties[].default','type'=>'mixed', ]),'class_name'=>newMappingData(['path'=>'class','type'=>'string', ])]),
Regarding these 2 examples, you can define your function_signature_mapping in the configuration file depending on your choice of naming convention.
Generate Prompt Function Instructions
This section defines the instructions which gives strict description of desired format answers and how LLM provider will process provided prompt, schemas etc.
You can use your own created/generated instructions for prompt_function_instructions, or use generateInstructions method.
generateInstructions method simply generates customized instructions regarding your function list (functions.yml), function payload schema (function_payload_schema.yml) and prompt for generating instructions (generate_prompt_function_instructions in config).
(Note:generate_prompt_function_instructions is the prompt/instructions in config that describe how to generate specific/customized instructions with functions and function_payload_schema)
Sample generate_prompt_function_instructions for generateInstructions call:
You are an AI assistant tasked with providing instructions to another AI system on how to respond to a given prompt with a specific JSON format. Your role is to analyze the provided "functions" and "function_payload_schema" and create a set of instructions that will ensure the other AI system generates a response following the specified format.
The "functions" field contains a list of available functions, their parameters, descriptions, and return types. The "function_payload_schema" field specifies the expected format for the response, which should be a JSON array listing the required fields for each function.
Your instructions should cover the following points:
1. Read the provided "prompt" and the list of available "functions".
2. Identify which function(s) from the "functions" list are needed to answer the prompt.
3. Analyze the "function_payload_schema" to determine the required fields for each function in the response JSON array. Do not add or omit any fields from the schema.
4. Explain that the response should ONLY contain a JSON array following the exact format specified in the "function_payload_schema". No additional fields, values, text, or explanations should be included.
5. Specify that the JSON array should list the required fields for each function, as specified in the "function_payload_schema".
6. Emphasize that no other information beyond the JSON array matching the "function_payload_schema" format should be added.
7. Ensure that the response can be directly used in PHP code without any modifications.
8. For the parameter fields of each function, analyze the "function_payload_schema" and include the fields exactly as specified in the schema, without making any assumptions about the field names or structure.
9. Clarify that no actual values for the parameters should be provided. Only the parameter fields as specified in the "function_payload_schema" should be included.
10. If no relevant function is found in the "functions" list to answer the prompt, the response should be the string "NULL" without any additional description or text.
11. If the other AI system cannot understand or follow the instructions, it should return the string "NULL" without any additional description or text.
Your response should be a clear and concise set of instructions that the other AI system can follow to generate the desired JSON response format based on the provided "functions" and "function_payload_schema".
OK, now provide the instructions as described above for the other AI system to generate the desired JSON response format based on the provided "functions" and "function_payload_schema"
In order to generate prompt_function_instructions, you can call generateInstructions method:
LaravelPromptAlchemist::generateInstructions();
Sample response of generateInstructions will look like:
You are an AI assistant that strictly follows instructions and provides responses in a specific format.
Your task is to analyze a given prompt and identify the required functions from a provided list to answer the prompt.
Your response should be a JSON array that lists the required function names, their parameters (name and type only), and the class_name, following the exact format specified in the "function_payload_schema".
Do not include any additional information, explanations, or values beyond what is specified in the schema. Adhere to the following instructions:
1. Read the provided "prompt" and the list of available "functions".
2. Identify which function(s) from the "functions" list are needed to answer the prompt.
3. Your response should ONLY contain a JSON array following the exact format specified in the "function_payload_schema". Do not include any additional fields, values, text, or explanations.
4. The JSON array should list the required function names, their parameters (name and type only), and the class_name.
5. Do not add any other information beyond the JSON array matching the "function_payload_schema" format.
6. Ensure that the response can be directly used in PHP code without any modifications.
7. Do not provide any actual values for the parameters. Only include the parameter names and types as specified in the "function_payload_schema".
8. If you do not understand the instructions or cannot provide a response following the specified format, respond with "NULL".
9. If no relevant function is found in the "functions" list to answer the prompt, the response should be the string "NULL" without any additional description or text
Define Schemas
This section defines the desired schema samples that are used for formatting the function payload and function results. Basically schemas are for deciding the response format of LLM. They are sent to LLM along with other info (prompt, functions - function results, instructions etc.) and in instructions LLM asked to give results as same format as in schema provided. In this way, LLM response can be resolved in package so that it can be used for Tool Use (Function Calling).
Function Payload Schema
Schema that defines the structure of the function payload.
Create yml file for function payload schema with any preferred naming and directory, add it to config file under schemas.
Define schema as example given below. The naming convention should align with the function list (functions.yml) because LLM response will depend on this schema and response will be validated in the package by comparing the function list (functions.yml) file.
Schema that defines the structure of the function results. This schema is for deciding the final response where function results are sent to LLM to form the response after Tool Use (Function Calling).
This schema is not required if you will not be sending function call results to LLM. You might prefer using function results in your codebase to derive a response directly.
Create yml file for function results schema with any preferred naming and directory, add it to config file under schemas.
This method is responsible for creating a structured payload template based on a given prompt and a list of functions (functions.yml) which later will be sent to an LLM provider. This method constructs an array containing instructions, the prompt, a list of functions, and a function payload schema. The instructions detail how the prompt should be processed, ensuring the response strictly adheres to the provided format.
For the prompt which you will be used for Tool Use (Function Calling):
$prompt ='Can tell me Mr. Boolean Bob credit score?';
This is how you can call preparePromptFunctionPayload method by using facade:
And this is the expected prepared payload response sample:
["prompt"=>"Can tell me Mr. Boolean Bob credit score?","instructions"=>"You are an AI assistant that strictly follows instructions and provides responses in a specific format.\n Your task is to analyze a given prompt and identify the required functions from a provided list to answer the prompt.\n Your response should be a JSON array that lists the required function names, their parameters (name and type only), and the class_name, following the exact format specified in the \"function_payload_schema\".\n Do not include any additional information, explanations, or values beyond what is specified in the schema. Adhere to the following instructions:\n 1. Read the provided \"prompt\" and the list of available \"functions\".\n 2. Identify which function(s) from the \"functions\" list ...","functions"=> [ ["function_name"=>"getFinancialData","parameters"=> [ ["name"=>"userId","type"=>"int"], ["name"=>"startDate","type"=>"string"], ["name"=>"endDate","type"=>"string"] ],"visibility"=>"public","description"=>"Retrieves financial data for a specific user and timeframe.","return"=> ["type"=>"object","description"=>"An object containing details like totalAmount, transactions (array), and other relevant financial data." ],"class_name"=>"MoeMizrak\LaravelPromptAlchemist\Tests\Example" ], ["function_name"=>"getCreditScore","parameters"=> [ ["name"=>"userId","type"=>"int"] ],"visibility"=>"public","description"=>"Retrieves the current credit score for a specific user.","return"=> ["type"=>"object","description"=>"An object containing the credit score, credit report summary, and any relevant notes." ],"class_name"=>"MoeMizrak\LaravelPromptAlchemist\Tests\Example" ],... ],"function_payload_schema"=> [ ["function_name"=>"getFinancialData","parameters"=> [ ["name"=>"userId","type"=>"int"], ["name"=>"startDate","type"=>"string"], ["name"=>"endDate","type"=>"string"] ],"class_name"=>"MoeMizrak\LaravelPromptAlchemist\Tests\ExampleA" ], ["function_name"=>"getCreditScore","parameters"=> [ ["name"=>"userId","type"=>"int"] ],"class_name"=>"MoeMizrak\LaravelPromptAlchemist\Tests\ExampleD" ],... ]]
Send Tool Use (Function Calling) Request to OpenRouter
Since this package is designed in a flexible way, you may use Laravel OpenRouter (please check out OpenRouter github repository for more information) which is used as the default LLM provider for this package, or you may use any other LLM provider with this package to send Tool Use (Function Calling) request.
This is the sample OpenRouter request:
$prompt ='Can tell me Mr. Boolean Bob credit score?';$model =config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models$content =LaravelPromptAlchemist::preparePromptFunctionPayload($prompt);$messageData =newMessageData(['content'=>json_encode($content),'role'=>RoleType::USER,]);$chatData =newChatData(['messages'=> [ $messageData, ],'model'=> $model,'temperature'=>0.1,// Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.]);// Send OpenRouter request$response =LaravelOpenRouter::chatRequest($chatData);
Sample Laravel OpenRouter response (ResponseData is returned):
Validates a function signature returned by the LLM. It returns boolean or ErrorData for wrong function signature with missing/wrong field. You can retrieve LLM returned functions from ResponseData returned by Send Tool Use (Function Calling) Request to OpenRouter.
Sample LLM returned functions:
// $response = LaravelOpenRouter::chatRequest($chatData); as shown in [Send Tool Use (Function Calling) Request to OpenRouter] section$responseContentData =str_replace("\n","", (Arr::get($response->choices[0],'message.content'))); // Get content from the response.$llmReturnedFunctions =json_decode($responseContentData,true); // Functions returned from LLM.// Foreach $llmReturnedFunctions and get each function to validate:$llmReturnedFunction = [ // Sample LLM returned function"function_name"=>"getFinancialData","parameters"=> [ [ "name"=>"userId","type"=>"int"], [ "name"=>"startDate","type"=>"string"], [ "name"=>"endDate","type"=>"string"], ],'class_name'=>'MoeMizrak\LaravelPromptAlchemist\Tests\Example'];// Form LLM returned function data (FunctionData).$llmReturnedFunctionData =$this->request->formLlmReturnedFunctionData($llmReturnedFunction);// Add parameter values if exists, only name and value of the parameter are set.$parameters = [newParameterData(['name'=>'userId','value'=>99, ]),newParameterData(['name'=>'startDate','value'=>'2023-06-01', ]),newParameterData(['name'=>'endDate','value'=>'2023-07-01', ]),];// Set parameter values in $llmReturnedFunctionData DTO$llmReturnedFunctionData->setParameterValues($parameters);
And this is how to validate a function signature returned from LLM (formed llm returned function as above, also values are set for parameters):
$isValid =LaravelOpenRouter::validateFunctionSignature($llmReturnedFunctionData); // $isValid is bool or ErrorData
In case the LLM returned function signature is invalid, this is a sample ErrorData returned:
output:ErrorData(['code'=>400,'message'=>'Function invalidFunctionName does not exist in class MoeMizrak\LaravelPromptAlchemist\Tests\Example']);
Call Function (Actual Function Call)
This method make actual function call with provided function signatures along with values for parameters. Since Validate Function Signature is performed before this step, function signature is safe to make this function call (Also parameter values are set in Validate Function Signature step). Regardless of the function visibility (private, protected, public etc.), function can be called directly.
This package makes actual function calls unlike built-in capabilities of LLMs that may only list functions. Basically with this package, you can get the list of the functions regarding your prompt from LLM, and then make an actual function calls to retrieve function results.
Where function_name is the name of the function called, result is whatever this function returns (array, bool, int, mixed, object, void etc.)
Prepare Function Results Payload
This method is responsible for creating a structured payload template based on a given prompt and a function results which later will be sent to an LLM provider. This method constructs an array containing instructions, the prompt, a function results, and a function results schema. The instructions detail how the prompt should be processed, ensuring the response strictly adheres to the provided format as function results schema.
Prompt which will be used for function results payload preparation:
$prompt ='Can tell me Mr. Boolean Bob credit score?';$functionResults = [newFunctionResultData(['function_name'=>'getFinancialData','result'=> ['totalAmount'=>122,'transactions'=> [ ['amount'=>12,'date'=>'2023-02-02','description'=>'food', ], ] ] ]),newFunctionResultData(['function_name'=>'getCreditScore','result'=> ['creditScore'=>0.8,'summary'=>'reliable', ] ]),...];
This is how you can call prepareFunctionResultsPayload method by using facade:
And this is the expected prepared function results payload sample:
["prompt"=>"Can tell me Mr. Boolean Bob credit score?","instructions"=>"You will strictly follow the instructions:\n - Understand the provided prompt and answer the prompt using the function_results (needed info is provided in function_results). If function_results are not sufficient enough, then your answer will be \"Please provide more information about [missing information]\"\n - Respond based on the function_results_schema sample provided (Do not add any extra info, exactly the same format provided in function_results_schema).\n - Format the response as an array following ...","function_results"=> [ ["function_name"=>"getFinancialData","result"=> ["totalAmount"=>122,"transactions"=> [ ["amount"=>12,"date"=>"2023-02-02","description"=>"food" ] ] ] ], ["function_name"=>"getCreditScore","result"=> ["creditScore"=>0.8,"summary"=>"reliable" ] ]... ],"function_results_schema"=> [ ["function_name"=>"getFinancialData","result"=> [ ["name"=>"transactions","type"=>"array","value"=> [ ["amount"=>null,"date"=>"2023-02-02","description"=>"shoes" ] ] ], ["name"=>"totalAmount","type"=>"int","value"=>1234 ] ] ], ["function_name"=>"getCreditScore","result"=> [ ["name"=>"creditScore","type"=>"float","value"=>0.5 ], ["name"=>"summary","type"=>"string","value"=>"reliable" ] ] ]... ]]
Send Function Results to OpenRouter
Since this package is designed in a flexible way, you may use Laravel OpenRouter (please check out OpenRouter github repository for more information) which is used as the default LLM provider for this package, or you may use any other LLM provider with this package to send Tool Use (Function Calling) request.
This is the sample OpenRouter request for function results:
$prompt ='Can tell me Mr. Boolean Bob credit score?';$functionResults = [newFunctionResultData(['function_name'=>'getFinancialData','result'=> ['totalAmount'=>122,'transactions'=> [ ['amount'=>12,'date'=>'2023-02-02','description'=>'food', ], ] ] ]),newFunctionResultData(['function_name'=>'getCreditScore','result'=> ['creditScore'=>0.8,'summary'=>'reliable', ] ]),...];$content =LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults);$model =config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models$messageData =newMessageData(['content'=>json_encode($content),'role'=>RoleType::USER,]);$chatData =newChatData(['messages'=> [ $messageData, ],'model'=> $model,'temperature'=>0.1,// Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.]);// Send OpenRouter request$response =LaravelOpenRouter::chatRequest($chatData);
Sample Laravel OpenRouter response (ResponseData is returned):
While everything else stays same with the Using Facade, with PromptAlchemistRequest you can call methods as following:
// generateFunctionList request.$this->promptAlchemistRequest->generateFunctionList($class, $functions, $fileName);// Generate instructions request.$this->promptAlchemistRequest->generateInstructions();// Prepare prompt function payload request.$this->promptAlchemistRequest->preparePromptFunctionPayload($prompt);// Forms LLM returned function into the FunctionData.$this->promptAlchemistRequest->formLlmReturnedFunctionData($llmReturnedFunction);// Validate function signature returned by the LLM request.$this->promptAlchemistRequest->validateFunctionSignature($llmReturnedFunctionData); // $isValid is bool or ErrorData// Make an actual function call.$this->promptAlchemistRequest->callFunction($llmReturnedFunctionData);// Prepare function results payload request.$this->promptAlchemistRequest->prepareFunctionResultsPayload($prompt, $functionResults);
💫 Contributing
We welcome contributions! If you'd like to improve this package, simply create a pull request with your changes. Your efforts help enhance its functionality and documentation.
📜 License
Laravel Prompt Alchemist is an open-sourced software licensed under the MIT license.