diff --git a/resources/views/Prompts/Rat/DraftPrompt.blade.php b/resources/views/Prompts/Rat/DraftPrompt.blade.php new file mode 100644 index 0000000..50f30ba --- /dev/null +++ b/resources/views/Prompts/Rat/DraftPrompt.blade.php @@ -0,0 +1,10 @@ + +### Instruction +Try to answer this question/instruction with step-by-step thoughts and make the answer more structural. Split the answer into several paragraphs. +{!! $question !!} + +@include('synapse::Parts.OutputSchema') + + + + diff --git a/resources/views/Prompts/Rat/GetQueryPrompt.blade.php b/resources/views/Prompts/Rat/GetQueryPrompt.blade.php new file mode 100644 index 0000000..4e19d4d --- /dev/null +++ b/resources/views/Prompts/Rat/GetQueryPrompt.blade.php @@ -0,0 +1,11 @@ + +## User input: {{ $question }} + +## Response +{{ $answer }} + +## Instruction +Summarize the content with a focus on the last sentences to create a concise search query for Bing. Use search syntax to make the query specific and relevant for content verification. + +@include('synapse::Parts.OutputSchema') + diff --git a/resources/views/Prompts/Rat/SplitAnswerPrompt.blade.php b/resources/views/Prompts/Rat/SplitAnswerPrompt.blade.php new file mode 100644 index 0000000..4fd5efa --- /dev/null +++ b/resources/views/Prompts/Rat/SplitAnswerPrompt.blade.php @@ -0,0 +1,12 @@ + +## User input: {{ $question }} + +## Response +{{ $answer }} + +## Instruction +Split the answer of the question into multiple paragraphs with each paragraph containing a complete thought. +The answer should be splited into less than {{ $number_of_paragraphs }} paragraphs. + +@include('synapse::Parts.OutputSchema') + diff --git a/src/AgentChain.php b/src/AgentChain.php new file mode 100644 index 0000000..d08a373 --- /dev/null +++ b/src/AgentChain.php @@ -0,0 +1,76 @@ +config()->add('persistInputs', []); + $this->config()->add('agents', collect($agents)); + $this->pendingAgentChain = $this->createPendingAgentChain(); + + } + + /** + * Create a new PendingAgentTask + */ + public function createPendingAgentChain(): PendingAgentChain + { + return new PendingAgentChain($this); + } + + /** + * Handles the user input and extra agent arguments to retrieve the response. + * + * @param array|null $input The input array. + * @param array|null $extraAgentArgs The extra agent arguments array. + * @return Message The final message from the agent. + * + * @throws Throwable + */ + public function handle(?array $input): Message + { + $this->config()->add('input', $input); + $this->config()->get('agents')->each(function ($agent) use ($input) { + $input = [ + ...$this->config()->get('input'), + ...$this->config()->get('persistInputs') + ]; + $response = $agent->handle($input); + $this->config()->add('input', $response->content()); + }); + + return Message::make([ + 'role' => 'agent', + 'finish_reason' => 'stop', + 'content' => $this->config()->get('input') + ]); + } + + public function persistInputs(array $inputs): static + { + $this->config()->add('persistInputs', $inputs); + + return $this; + } + } diff --git a/src/AgentTask/PendingAgentChain.php b/src/AgentTask/PendingAgentChain.php new file mode 100644 index 0000000..24f8803 --- /dev/null +++ b/src/AgentTask/PendingAgentChain.php @@ -0,0 +1,46 @@ +agentChain = $agentChain; + + $this + ->tap(new BootTraits); + + } + + /** + * Tap into the agent chain + * + * @return $this + */ + protected function tap(callable $callable): static + { + $callable($this); + + return $this; + } + + /** + * Retrieve the agent associated with the current task. + * + * @return AgentChain The current agent instance. + */ + public function agent(): AgentChain + { + return $this->agentChain; + } + +} diff --git a/src/AgentTask/StartTasks/BootTraits.php b/src/AgentTask/StartTasks/BootTraits.php index 45d40da..6714aeb 100644 --- a/src/AgentTask/StartTasks/BootTraits.php +++ b/src/AgentTask/StartTasks/BootTraits.php @@ -5,6 +5,7 @@ namespace UseTheFork\Synapse\AgentTask\StartTasks; + use UseTheFork\Synapse\AgentTask\PendingAgentChain; use UseTheFork\Synapse\AgentTask\PendingAgentTask; use UseTheFork\Synapse\Helpers\Helpers; @@ -13,7 +14,7 @@ class BootTraits /** * Boot the plugins */ - public function __invoke(PendingAgentTask $pendingAgentTask): PendingAgentTask + public function __invoke(PendingAgentTask|PendingAgentChain $pendingAgentTask): PendingAgentTask|PendingAgentChain { $agent = $pendingAgentTask->agent(); diff --git a/src/Agents/Rat/GetQueryAgent.php b/src/Agents/Rat/GetQueryAgent.php new file mode 100644 index 0000000..97a44f8 --- /dev/null +++ b/src/Agents/Rat/GetQueryAgent.php @@ -0,0 +1,27 @@ + 'query', + 'rules' => 'required|string', + 'description' => 'The query to search for.', + ]), + ]; + } +} diff --git a/src/Agents/Rat/RatDraftAgent.php b/src/Agents/Rat/RatDraftAgent.php new file mode 100644 index 0000000..4fc878b --- /dev/null +++ b/src/Agents/Rat/RatDraftAgent.php @@ -0,0 +1,27 @@ + 'answer', + 'rules' => 'required|string', + 'description' => 'Your answer split in to paragraphs.', + ]), + ]; + } +} diff --git a/src/Agents/Rat/RatSplitAnswerAgent.php b/src/Agents/Rat/RatSplitAnswerAgent.php new file mode 100644 index 0000000..bb26cac --- /dev/null +++ b/src/Agents/Rat/RatSplitAnswerAgent.php @@ -0,0 +1,32 @@ + 'paragraphs', + 'rules' => 'required|array', + 'description' => 'Your answer split in to paragraphs.', + ]), + SchemaRule::make([ + 'name' => 'paragraphs.*', + 'rules' => 'required|string', + 'description' => 'A paragraph of your answer.', + ]), + ]; + } +} diff --git a/src/Contracts/ArrayStore.php b/src/Contracts/ArrayStore.php new file mode 100644 index 0000000..0b58038 --- /dev/null +++ b/src/Contracts/ArrayStore.php @@ -0,0 +1,60 @@ + + */ + public function all(): array; + + /** + * Retrieve a single item. + */ + public function get(string $key, mixed $default = null): mixed; + + /** + * Determine if the store is empty + */ + public function isEmpty(): bool; + + /** + * Determine if the store is not empty + */ + public function isNotEmpty(): bool; + + /** + * Merge in other arrays. + * + * @param array ...$arrays + * @return $this + */ + public function merge(array ...$arrays): static; + + /** + * Remove an item from the store. + * + * @return $this + */ + public function remove(string $key): static; + + /** + * Overwrite the entire repository's contents. + * + * @param array $data + * @return $this + */ + public function set(array $data): static; + } diff --git a/src/Helpers/Helpers.php b/src/Helpers/Helpers.php index 30242af..4a42e7a 100644 --- a/src/Helpers/Helpers.php +++ b/src/Helpers/Helpers.php @@ -6,6 +6,7 @@ use Closure; use ReflectionClass; +use UseTheFork\Synapse\AgentTask\PendingAgentChain; use UseTheFork\Synapse\AgentTask\PendingAgentTask; /** @@ -22,7 +23,7 @@ final class Helpers * * @throws \ReflectionException */ - public static function bootPlugin(PendingAgentTask $pendingAgentTask, string $trait): void + public static function bootPlugin(PendingAgentTask|PendingAgentChain $pendingAgentTask, string $trait): void { $agent = $pendingAgentTask->agent(); diff --git a/src/Repositories/ArrayStore.php b/src/Repositories/ArrayStore.php new file mode 100644 index 0000000..52b967a --- /dev/null +++ b/src/Repositories/ArrayStore.php @@ -0,0 +1,121 @@ + + */ + protected array $data = []; + + /** + * Constructor + * + * @param array $data + */ + public function __construct(array $data = []) + { + $this->data = $data; + } + + /** + * Add an item to the repository. + * + * @return $this + */ + public function add(string $key, mixed $value): static + { + $this->data[$key] = Helpers::value($value); + + return $this; + } + + /** + * Retrieve a single item. + */ + public function get(string $key, mixed $default = null): mixed + { + return $this->all()[$key] ?? $default; + } + + /** + * Retrieve all the items. + * + * @return array + */ + public function all(): array + { + return $this->data; + } + + /** + * Determine if the store is not empty + * + * + * @phpstan-assert-if-true non-empty-array $this->data + */ + public function isNotEmpty(): bool + { + return ! $this->isEmpty(); + } + + /** + * Determine if the store is empty + * + * + * @phpstan-assert-if-false non-empty-array $this->data + */ + public function isEmpty(): bool + { + return empty($this->data); + } + + /** + * Merge in other arrays. + * + * @param array ...$arrays + * @return $this + */ + public function merge(array ...$arrays): static + { + $this->data = array_merge($this->data, ...$arrays); + + return $this; + } + + /** + * Remove an item from the store. + * + * @return $this + */ + public function remove(string $key): static + { + unset($this->data[$key]); + + return $this; + } + + /** + * Overwrite the entire repository. + * + * @param array $data + * @return $this + */ + public function set(array $data): static + { + $this->data = $data; + + return $this; + } + } diff --git a/src/Traits/Conditionable.php b/src/Traits/Conditionable.php new file mode 100644 index 0000000..7eb4417 --- /dev/null +++ b/src/Traits/Conditionable.php @@ -0,0 +1,60 @@ +config ??= new ArrayStore($this->defaultConfig()); + } + + /** + * Default Config + * + * @return array + */ + protected function defaultConfig(): array + { + return []; + } + } diff --git a/tests/AgentChains/RatAgentChainTest.php b/tests/AgentChains/RatAgentChainTest.php new file mode 100644 index 0000000..1007064 --- /dev/null +++ b/tests/AgentChains/RatAgentChainTest.php @@ -0,0 +1,43 @@ + function (PendingRequest $pendingRequest): \Saloon\Http\Faking\Fixture { + $hash = md5(json_encode($pendingRequest->body()->get('messages'))); + + return MockResponse::fixture("AgentChains/RatAgentChain-{$hash}"); + }, + ]); + + $agentChain = AgentChain::make([ + new RatDraftAgent, + new RatSplitAnswerAgent, + ])->persistInputs([ + 'question' => 'how so I improve my heart health?', + 'number_of_paragraphs' => '5', + ]); + + $message = $agentChain->handle([]); + $agentResponseArray = $message->toArray(); + + expect($agentResponseArray['content'])->toBeArray() + ->and($agentResponseArray['content'])->toHaveKey('paragraphs') + ->and($agentResponseArray['content']['paragraphs'])->toBeArray(); + + $answer = ''; + foreach ($agentResponseArray['content']['paragraphs'] as $paragraph) { + $answer = "{$answer}\n\n{$paragraph}"; + } + +}); diff --git a/tests/Fixtures/Saloon/AgentChains/RatAgentChain-0dac52efdcd81f0dae4be83cc7dd9134.json b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-0dac52efdcd81f0dae4be83cc7dd9134.json new file mode 100644 index 0000000..c0b7531 --- /dev/null +++ b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-0dac52efdcd81f0dae4be83cc7dd9134.json @@ -0,0 +1,10 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Thu, 17 Oct 2024 01:56:08 GMT", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive" + }, + "data": "{\n \"id\": \"chatcmpl-AJA25VaNBB4t2ZMS8lc44qZO7WB9x\",\n \"object\": \"chat.completion\",\n \"created\": 1729130161,\n \"model\": \"gpt-4-turbo-2024-04-09\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"```json\\n{\\n \\\"paragraphs\\\": [\\n \\\"Improving heart health is a multifaceted endeavor that includes lifestyle changes and medical strategies. First and foremost, diet plays a critical role in heart health. Reducing the intake of saturated fats, trans fats, and cholesterol-rich foods is advisable, as these can contribute to plaque buildup in arteries. Instead, focus on eating a variety of fruits, vegetables, whole grains, and lean proteins to keep your heart healthy.\\\",\\n \\\"Regular physical activity is also crucial for maintaining a healthy heart. Aim for at least 150 minutes of moderate aerobic exercise or 75 minutes of vigorous exercise each week, combined with muscle-strengthening activities on two or more days a week.\\\",\\n \\\"Avoiding tobacco in any form is one of the most significant steps you can take to protect your heart. Smoking and the use of tobacco products increase the risk of developing cardiovascular diseases. If you smoke, seek help to quit as soon as possible.\\\",\\n \\\"Monitoring your health with regular check-ups can help catch and address potential heart issues before they become severe. This includes keeping an eye on blood pressure, cholesterol levels, and blood sugar, especially if you have a family history of heart disease. Lastly, managing stress and ensuring adequate sleep each night contribute to overall heart health. Engage in stress-reducing activities that you enjoy and aim for 7-9 hours of sleep per night to promote heart health.\\\"\\n ]\\n}\\n```\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 381,\n \"completion_tokens\": 294,\n \"total_tokens\": 675,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_83975a045a\"\n}\n" +} diff --git a/tests/Fixtures/Saloon/AgentChains/RatAgentChain-9ce86b41e54066cf052469d1d46c848d.json b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-9ce86b41e54066cf052469d1d46c848d.json new file mode 100644 index 0000000..ede9289 --- /dev/null +++ b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-9ce86b41e54066cf052469d1d46c848d.json @@ -0,0 +1,10 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Thu, 17 Oct 2024 01:51:50 GMT", + "Content-Type": "application/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive" + }, + "data": "{\n \"id\": \"chatcmpl-AJ9xp1Dm5MRjmPh5TsT1gZUsvvbBF\",\n \"object\": \"chat.completion\",\n \"created\": 1729129897,\n \"model\": \"gpt-4-turbo-2024-04-09\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"```json\\n{\\n \\\"answer\\\": \\\"Improving heart health is a multifaceted endeavor that includes lifestyle changes and medical strategies. First and foremost, diet plays a critical role in heart health. Reducing the intake of saturated fats, trans fats, and cholesterol-rich foods is advisable, as these can contribute to plaque buildup in arteries. Instead, focus on eating a variety of fruits, vegetables, whole grains, and lean proteins to keep your heart healthy.\\\\n\\\\nRegular physical activity is also crucial for maintaining a healthy heart. Aim for at least 150 minutes of moderate aerobic exercise or 75 minutes of vigorous exercise each week, combined with muscle-strengthening activities on two or more days a week. Avoiding tobacco in any form is one of the most significant steps you can take to protect your heart. Smoking and the use of tobacco products increase the risk of developing cardiovascular diseases. If you smoke, seek help to quit as soon as possible.\\\\n\\\\nMonitoring your health with regular check-ups can help catch and address potential heart issues before they become severe. This includes keeping an eye on blood pressure, cholesterol levels, and blood sugar, especially if you have a family history of heart disease. Lastly, managing stress and ensuring adequate sleep each night contribute to overall heart health. Engage in stress-reducing activities that you enjoy and aim for 7-9 hours of sleep per night to promote heart health.\\\"\\n}\\n```\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 364,\n \"completion_tokens\": 287,\n \"total_tokens\": 651,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_83975a045a\"\n}\n" +} diff --git a/tests/Fixtures/Saloon/AgentChains/RatAgentChain-c8b871f5b66ceee6cdd6a4f41ca4809e.json b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-c8b871f5b66ceee6cdd6a4f41ca4809e.json new file mode 100644 index 0000000..0d8758f --- /dev/null +++ b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-c8b871f5b66ceee6cdd6a4f41ca4809e.json @@ -0,0 +1,10 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Thu, 17 Oct 2024 01:50:28 GMT", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive" + }, + "data": "{\n \"id\": \"chatcmpl-AJ9wZnNOMYPTf1cGDenfRqL1FZVs8\",\n \"object\": \"chat.completion\",\n \"created\": 1729129819,\n \"model\": \"gpt-4-turbo-2024-04-09\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"```json\\n{\\n \\\"answer\\\": \\\"Improving heart health is a multifaceted endeavor that includes lifestyle changes and medical strategies. First and foremost, diet plays a critical role in heart health. Reducing the intake of saturated fats, trans fats, and cholesterol-rich foods is advisable, as these can contribute to plaque buildup in arteries. Instead, focus on eating a variety of fruits, vegetables, whole grains, and lean proteins to keep your heart healthy.\\\\n\\\\nRegular physical activity is also crucial for maintaining a healthy heart. Aim for at least 150 minutes of moderate aerobic exercise or 75 minutes of vigorous exercise each week, combined with muscle-strengthening activities on two or more days a week.\\\\n\\\\nAvoiding tobacco in any form is one of the most significant steps you can take to protect your heart. Smoking and the use of tobacco products increase the risk of developing cardiovascular diseases. If you smoke, seek help to quit as soon as possible.\\\\n\\\\nMonitoring your health with regular check-ups can help catch and address potential heart issues before they become severe. This includes keeping an eye on blood pressure, cholesterol levels, and blood sugar, especially if you have a family history of heart disease.\\\\n\\\\nLastly, managing stress and ensuring adequate sleep each night contribute to overall heart health. Engage in stress-reducing activities that you enjoy and aim for 7-9 hours of sleep per night to promote heart health.\\\"\\n}\\n```\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 145,\n \"completion_tokens\": 291,\n \"total_tokens\": 436,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_83975a045a\"\n}\n" +} diff --git a/tests/Fixtures/Saloon/AgentChains/RatAgentChain-e6be3b2a969ae75116175e3c039cc2f2.json b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-e6be3b2a969ae75116175e3c039cc2f2.json new file mode 100644 index 0000000..89a5763 --- /dev/null +++ b/tests/Fixtures/Saloon/AgentChains/RatAgentChain-e6be3b2a969ae75116175e3c039cc2f2.json @@ -0,0 +1,11 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Thu, 17 Oct 2024 01:50:18 GMT", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "access-control-expose-headers": "X-Request-ID" + }, + "data": "{\n \"id\": \"chatcmpl-AJ9wS3DmlVd0DuHRikV5zpDZekJLG\",\n \"object\": \"chat.completion\",\n \"created\": 1729129812,\n \"model\": \"gpt-4-turbo-2024-04-09\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"```json\\n{\\n \\\"answer\\\": \\\"Improving heart health is a significant concern for maintaining overall well-being and can be achieved through a combination of lifestyle changes, dietary adjustments, and regular medical check-ups. \\n\\nFirstly, engaging in regular physical activity is one of the most effective ways to strengthen the heart. Aim for at least 150 minutes of moderate aerobic exercise or 75 minutes of vigorous exercise each week. Activities could include walking, cycling, swimming, or sprinting. Exercise helps reduce hypertension, lower cholesterol, and keep body weight under control.\\n\\nSecondly, diet plays a crucial role in heart health. Incorporating heart-healthy foods into your diet can enhance cardiovascular function and reduce risk factors. Focus on eating a variety of fruits and vegetables, whole grains, lean proteins such as fish and legumes, and nuts and seeds. Limit the intake of saturated fats, trans fats, cholesterol, salt (sodium), and added sugars. Opt for cooking methods that require less fat, such as boiling, baking, or steaming rather than frying.\\n\\nAdditionally, managing stress and ensuring adequate sleep each night are vital components of heart health. Chronic stress and poor sleep patterns can lead to increased heart rate and blood pressure, potentially causing long-term harm to the heart. Employ relaxation techniques such as meditation, deep breathing exercises, or yoga. Strive for 7-9 hours of quality sleep per night.\\n\\nRegularly monitoring health metrics and consulting with healthcare professionals are also crucial. Keep track of your blood pressure, cholesterol levels, and body weight. Regular check-ups will help in early identification and management of any potential heart issues. Quitting smoking and limiting alcohol intake are also beneficial for maintaining a healthy heart.\\n\\nBy consistently applying these strategies, you can significantly improve and maintain your heart health over time.\\\"\\n}\\n```\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 81,\n \"completion_tokens\": 362,\n \"total_tokens\": 443,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_83975a045a\"\n}\n" +}