diff --git a/README.md b/README.md
index 5b24c14..33c883e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Laravel Prompt Alchemist
-Versatile **LLM Tool Use (Function Calling)** package for Laravel, compatible with **all LLMs**.
+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***).
[![Latest Version on Packagist](https://img.shields.io/badge/packagist-v1.0-blue)](https://packagist.org/packages/moe-mizrak/laravel-prompt-alchemist)
@@ -9,7 +9,8 @@ Versatile **LLM Tool Use (Function Calling)** package for Laravel, compatible wi
---
> **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 which function to call based on the prompt, compatible with **all LLMs** regardless of built-in capabilities.
+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.
## Table of Contents
@@ -252,25 +253,76 @@ $llmReturnedFunction = [ // Sample LLM returned function
],
'class_name' => 'MoeMizrak\LaravelPromptAlchemist\Tests\Example'
];
+
+// Formed LLM returned function data (FunctionData).
+$llmReturnedFunctionData = LaravelPromptAlchemist::formLlmReturnedFunctionData($llmReturnedFunction);
```
-Call `validateFunctionSignature` for function signature **validation**:
+Call `validateFunctionSignature` for function signature **validation** of `$llmReturnedFunctionData`:
```php
-LaravelOpenRouter::validateFunctionSignature($llmReturnedFunction);
+$isValid = LaravelOpenRouter::validateFunctionSignature($llmReturnedFunctionData);
```
-- And finally in your codebase, 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**).
+- 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**).
+
+
+(**Note:** You need to set **parameter values** to be able to call the function. **Required parameters** have to be set, otherwise **ErrorData** is returned.)
-> **Note:** Automatic function calls will be available in an **upcoming release!** Stay tuned for **updates!**
+Set parameter values before calling function:
+```php
+// Create parameter values, you just need parameter name and its value in correct type (int, string, array, object ...)
+$parameters = [
+ new ParameterData([
+ 'name' => 'userId',
+ 'value' => 99, // int userId
+ ]),
+ new ParameterData([
+ 'name' => 'startDate',
+ 'value' => '2023-06-01', // string startDate
+ ]),
+ new ParameterData([
+ 'name' => 'endDate',
+ 'value' => '2023-07-01', // string endDate
+ ]),
+];
+
+if (true === $isValid) {
+ // Set parameter values
+ $llmReturnedFunctionData->setParameterValues($parameters);
+}
+```
+
+Call the function as following:
+```php
+$functionResultData = LaravelPromptAlchemist::callFunction($llmReturnedFunctionData);
+```
+
+Sample function result (**$functionResultData**) DTO object (FunctionResultData):
+```php
+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 return 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](#prepare-function-results-payload) for more details)
```php
$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 = [
- [
+ new FunctionResultData([
'function_name' => 'getFinancialData',
'result' => [
'totalAmount' => 122,
@@ -281,19 +333,35 @@ $functionResults = [
'description' => 'food',
],
]
- ],
- ],
- [
+ ]
+ ]),
+ new FunctionResultData([
'function_name' => 'getCreditScore',
'result' => [
'creditScore' => 0.8,
'summary' => 'reliable',
]
- ],
+ ]),
...
];
+// Prepare function results payload for the request.
+$content = LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults);
+
+$messageData = new MessageData([
+ 'content' => json_encode($content),
+ 'role' => RoleType::USER,
+]);
-$response = LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults);
+$chatData = new ChatData([
+ '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`.
@@ -856,7 +924,7 @@ Prompt which will be used for Tool Use (Function Calling):
$prompt = 'Can tell me Mr. Boolean Bob credit score?';
$functionResults = [
- [
+ new FunctionResultData([
'function_name' => 'getFinancialData',
'result' => [
'totalAmount' => 122,
@@ -867,15 +935,15 @@ $functionResults = [
'description' => 'food',
],
]
- ],
- ],
- [
+ ]
+ ]),
+ new FunctionResultData([
'function_name' => 'getCreditScore',
'result' => [
'creditScore' => 0.8,
'summary' => 'reliable',
]
- ],
+ ]),
...
];
```
diff --git a/composer.json b/composer.json
index 8b340b3..99a25db 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "moe-mizrak/laravel-prompt-alchemist",
- "description": "Versatile LLM Tool Use (Function Calling) package for Laravel, compatible with all LLMs.",
+ "description": "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).",
"keywords": [
"Moe Mizrak",
"laravel",
diff --git a/src/DTO/FunctionData.php b/src/DTO/FunctionData.php
index 16a6bc7..7c10c4d 100644
--- a/src/DTO/FunctionData.php
+++ b/src/DTO/FunctionData.php
@@ -56,4 +56,21 @@ class FunctionData extends DataTransferObject
* @var string|null
*/
public ?string $class_name;
+
+ /**
+ * Set parameter values.
+ *
+ * @param array $parameters
+ *
+ * @return void
+ */
+ public function setParameterValues(array $parameters): void
+ {
+ foreach ($this->parameters as $llmReturnedParameter) {
+ $matchingParameter = array_filter($parameters, fn($param) => $param->name === $llmReturnedParameter->name);
+ if ($matchingParameter) {
+ $llmReturnedParameter->value = current($matchingParameter)->value;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/DTO/FunctionResultData.php b/src/DTO/FunctionResultData.php
new file mode 100644
index 0000000..c4906c4
--- /dev/null
+++ b/src/DTO/FunctionResultData.php
@@ -0,0 +1,28 @@
+newInstance();
// Call the function and get the result.
- return $method->invoke($instance, ...$params);
+ $result = $method->invoke($instance, ...$params);
+
+ // Map result to DTO object
+ return new FunctionResultData([
+ 'function_name' => $functionName,
+ 'result' => $result,
+ ]);
}
}
\ No newline at end of file
diff --git a/src/PromptAlchemistRequest.php b/src/PromptAlchemistRequest.php
index 7d82819..9415431 100644
--- a/src/PromptAlchemistRequest.php
+++ b/src/PromptAlchemistRequest.php
@@ -5,6 +5,7 @@
use MoeMizrak\LaravelOpenrouter\Exceptions\XorValidationException;
use MoeMizrak\LaravelPromptAlchemist\DTO\ErrorData;
use MoeMizrak\LaravelPromptAlchemist\DTO\FunctionData;
+use MoeMizrak\LaravelPromptAlchemist\DTO\FunctionResultData;
use ReflectionException;
use Spatie\DataTransferObject\Exceptions\UnknownProperties;
@@ -110,11 +111,11 @@ public function generateInstructions(): mixed
*
* @param FunctionData $function
*
- * @return mixed
+ * @return FunctionResultData|ErrorData
* @throws UnknownProperties
* @throws ReflectionException
*/
- public function callFunction(FunctionData $function): mixed
+ public function callFunction(FunctionData $function): FunctionResultData|ErrorData
{
return $this->functionCaller->call($function);
}
diff --git a/src/Resources/Templates/ResponsePayloadTemplate.php b/src/Resources/Templates/ResponsePayloadTemplate.php
index 877c7c4..4b8726a 100644
--- a/src/Resources/Templates/ResponsePayloadTemplate.php
+++ b/src/Resources/Templates/ResponsePayloadTemplate.php
@@ -24,6 +24,8 @@ final class ResponsePayloadTemplate
*/
public static function createPayload(string $prompt, string $instructions, ?array $functionResults, ?array $resultsSchema): array
{
+ $functionResults = array_map(fn($functionResultData) => $functionResultData->toArray(), $functionResults);
+
return [
'prompt' => $prompt,
'instructions' => $instructions,
diff --git a/tests/PromptAlchemistTest.php b/tests/PromptAlchemistTest.php
index 15c5fb1..d9bcb4f 100644
--- a/tests/PromptAlchemistTest.php
+++ b/tests/PromptAlchemistTest.php
@@ -10,6 +10,7 @@
use MoeMizrak\LaravelOpenrouter\Types\RoleType;
use MoeMizrak\LaravelPromptAlchemist\DTO\ErrorData;
use MoeMizrak\LaravelPromptAlchemist\DTO\FunctionData;
+use MoeMizrak\LaravelPromptAlchemist\DTO\FunctionResultData;
use MoeMizrak\LaravelPromptAlchemist\DTO\ParameterData;
use MoeMizrak\LaravelPromptAlchemist\DTO\ReturnData;
use MoeMizrak\LaravelPromptAlchemist\Facades\LaravelPromptAlchemist;
@@ -170,7 +171,7 @@ public function it_prepares_function_results_payload()
{
/* SETUP */
$functionResults = [
- [
+ new FunctionResultData([
'function_name' => 'getFinancialData',
'result' => [
'totalAmount' => 122,
@@ -181,22 +182,22 @@ public function it_prepares_function_results_payload()
'description' => 'food',
],
]
- ],
- ],
- [
+ ]
+ ]),
+ new FunctionResultData([
'function_name' => 'getCreditScore',
'result' => [
'creditScore' => 0.8,
'summary' => 'reliable',
]
- ],
- [
+ ]),
+ new FunctionResultData([
'function_name' => 'getAccountBalance',
'result' => [
'currentBalance' => 12502,
'status' => 'active',
]
- ],
+ ]),
];
/* EXECUTE */
@@ -217,7 +218,7 @@ public function it_sends_function_results_to_open_route_and_retrieves_formed_ans
{
/* SETUP */
$functionResults = [
- [
+ new FunctionResultData([
'function_name' => 'getFinancialData',
'result' => [
'totalAmount' => 122,
@@ -228,22 +229,22 @@ public function it_sends_function_results_to_open_route_and_retrieves_formed_ans
'description' => 'food',
],
]
- ],
- ],
- [
+ ]
+ ]),
+ new FunctionResultData([
'function_name' => 'getCreditScore',
'result' => [
'creditScore' => 0.8,
'summary' => 'reliable',
]
- ],
- [
+ ]),
+ new FunctionResultData([
'function_name' => 'getAccountBalance',
'result' => [
'currentBalance' => 12502,
'status' => 'active',
]
- ],
+ ]),
];
$prompt = 'Can tell me Mr. Boolean Bob credit score?';
$content = $this->request->prepareFunctionResultsPayload($prompt, $functionResults);
@@ -774,19 +775,23 @@ public function it_successfully_calls_function_returned_from_llm()
$llmReturnedFunctionData = $this->request->formLlmReturnedFunctionData($llmReturnedFunction);
// $llmReturnedFunctionData should be validated before function calling
$validationResponse = $this->request->validateFunctionSignature($llmReturnedFunctionData);
+ // Set parameter values
+ $parameters = [
+ new ParameterData([
+ 'name' => 'userId',
+ 'value' => 99,
+ ]),
+ new ParameterData([
+ 'name' => 'startDate',
+ 'value' => '2023-06-01',
+ ]),
+ new ParameterData([
+ 'name' => 'endDate',
+ 'value' => '2023-07-01',
+ ]),
+ ];
if (true === $validationResponse) {
- // here set values of the parameter to call them
- foreach ($llmReturnedFunctionData->parameters as $key => $parameter) {
- if ($parameter->name == 'userId') {
- $llmReturnedFunctionData->parameters[$key]->value = 1;
- }
- if ($parameter->name == 'startDate') {
- $llmReturnedFunctionData->parameters[$key]->value = '2023-06-01';
- }
- if ($parameter->name == 'endDate') {
- $llmReturnedFunctionData->parameters[$key]->value = '2023-07-01';
- }
- }
+ $llmReturnedFunctionData->setParameterValues($parameters);
}
/* EXECUTE */
@@ -794,6 +799,7 @@ public function it_successfully_calls_function_returned_from_llm()
/* ASSERT */
$this->assertNotNull($functionResult);
+ $this->assertEquals('getFinancialData', $functionResult->function_name);
}
/**
@@ -824,16 +830,19 @@ public function it_successfully_calls_private_function_returned_from_llm()
$llmReturnedFunctionData = $this->request->formLlmReturnedFunctionData($llmReturnedFunction);
// $llmReturnedFunctionData should be validated before function calling
$validationResponse = $this->request->validateFunctionSignature($llmReturnedFunctionData);
+ // Set parameter values
+ $parameters = [
+ new ParameterData([
+ 'name' => 'stringParam',
+ 'value' => $stringValue,
+ ]),
+ new ParameterData([
+ 'name' => 'intParam',
+ 'value' => $intValue,
+ ]),
+ ];
if (true === $validationResponse) {
- // here set values of the parameter to call them
- foreach ($llmReturnedFunctionData->parameters as $key => $parameter) {
- if ($parameter->name == 'stringParam') {
- $llmReturnedFunctionData->parameters[$key]->value = $stringValue;
- }
- if ($parameter->name == 'intParam') {
- $llmReturnedFunctionData->parameters[$key]->value = $intValue;
- }
- }
+ $llmReturnedFunctionData->setParameterValues($parameters);
}
/* EXECUTE */
@@ -841,6 +850,6 @@ public function it_successfully_calls_private_function_returned_from_llm()
/* ASSERT */
$this->assertNotNull($functionResult);
- $this->assertEquals('private return value ' . $stringValue . ' ' . $intValue, $functionResult);
+ $this->assertEquals('private return value ' . $stringValue . ' ' . $intValue, $functionResult->result);
}
}
\ No newline at end of file