From 4dbe5c140e0e3ea5a8b9a998407b756691273eb9 Mon Sep 17 00:00:00 2001 From: Alfred Nutile Date: Sun, 4 Aug 2024 14:48:00 -0400 Subject: [PATCH] this will start to make just chat work with tools then we can limit the open area to the tools below --- Modules/LlmDriver/app/BaseClient.php | 17 +- Modules/LlmDriver/app/Functions/Chat.php | 2 + .../app/Functions/CreateDocument.php | 2 +- .../app/Functions/FunctionContract.php | 2 + Modules/LlmDriver/app/LlmDriverClient.php | 6 +- Modules/LlmDriver/app/OllamaClient.php | 35 +- .../OllamaChatCompletionResponse.php | 26 ++ .../Responses/OllamaCompletionResponse.php | 4 +- .../Orchestration/OrchestrateVersionTwo.php | 41 ++- app/Jobs/SummarizeDocumentJob.php | 25 -- app/Models/Document.php | 1 + app/Models/Message.php | 44 +-- phpstan.neon | 1 + resources/js/Pages/Chat/Chatv2.vue | 2 +- tests/Feature/Models/DocumentTest.php | 1 + .../OllamaChatCompletionResponseTest.php | 32 ++ .../Feature/OllamaCompletionResponseTest.php | 7 +- .../messages_before_final_job_claude.json | 50 ++- tests/fixtures/ollama_chat_payload.json | 49 +++ .../ollama_chat_payload_2PfZH8kbVf.json | 53 +++ .../ollama_chat_payload_EtYk2sKjXU.json | 49 +++ .../ollama_chat_payload_YjW0qG4t9a.json | 53 +++ .../ollama_chat_payload_gotOM5jLyq.json | 91 +++++ .../ollama_chat_payload_hk9XtfTEg4.json | 53 +++ .../ollama_chat_payload_k6kd0VUyzG.json | 53 +++ .../ollama_chat_payload_qfnfFEijG7.json | 91 +++++ .../ollama_chat_payload_r8fV6ct8E6.json | 91 +++++ .../ollama_chat_payload_vPqq0btEY0.json | 91 +++++ tests/fixtures/ollama_completion.json | 338 ++++++++++++++++++ tests/fixtures/ollama_completion_v2.json | 316 ++++++++++++++++ tests/fixtures/ollama_functions.json | 158 +++++--- .../fixtures/ollama_messages_after_remap.json | 10 +- tests/fixtures/ollama_modified_payload.json | 53 +++ .../orchestrate_messages_first_send.json | 52 +++ tests/fixtures/summarize_document_prompt.json | 1 + .../tool_complete_response_claude.json | 20 +- 36 files changed, 1740 insertions(+), 180 deletions(-) create mode 100644 Modules/LlmDriver/app/Responses/OllamaChatCompletionResponse.php create mode 100644 tests/Feature/OllamaChatCompletionResponseTest.php create mode 100644 tests/fixtures/ollama_chat_payload.json create mode 100644 tests/fixtures/ollama_chat_payload_2PfZH8kbVf.json create mode 100644 tests/fixtures/ollama_chat_payload_EtYk2sKjXU.json create mode 100644 tests/fixtures/ollama_chat_payload_YjW0qG4t9a.json create mode 100644 tests/fixtures/ollama_chat_payload_gotOM5jLyq.json create mode 100644 tests/fixtures/ollama_chat_payload_hk9XtfTEg4.json create mode 100644 tests/fixtures/ollama_chat_payload_k6kd0VUyzG.json create mode 100644 tests/fixtures/ollama_chat_payload_qfnfFEijG7.json create mode 100644 tests/fixtures/ollama_chat_payload_r8fV6ct8E6.json create mode 100644 tests/fixtures/ollama_chat_payload_vPqq0btEY0.json create mode 100644 tests/fixtures/ollama_completion.json create mode 100644 tests/fixtures/ollama_completion_v2.json create mode 100644 tests/fixtures/ollama_modified_payload.json create mode 100644 tests/fixtures/orchestrate_messages_first_send.json create mode 100644 tests/fixtures/summarize_document_prompt.json diff --git a/Modules/LlmDriver/app/BaseClient.php b/Modules/LlmDriver/app/BaseClient.php index afa9c8d0..c9daf6ff 100644 --- a/Modules/LlmDriver/app/BaseClient.php +++ b/Modules/LlmDriver/app/BaseClient.php @@ -32,6 +32,8 @@ abstract class BaseClient protected ToolTypes $toolType; + protected bool $limitByShowInUi = false; + public function setToolType(ToolTypes $toolType): self { $this->toolType = $toolType; @@ -39,6 +41,13 @@ public function setToolType(ToolTypes $toolType): self return $this; } + public function setLimitByShowInUi(bool $limitByShowInUi): self + { + $this->limitByShowInUi = $limitByShowInUi; + + return $this; + } + public function setForceTool(FunctionDto $tool): self { $this->forceTool = $tool; @@ -61,6 +70,7 @@ public function modifyPayload(array $payload, bool $noTools = false): array $payload['tools'] = $this->getFunctions(); } + put_fixture('ollama_modified_payload.json', $payload); return $payload; } @@ -219,12 +229,17 @@ public function getFunctions(): array }); } + if ($this->limitByShowInUi) { + $functions = $functions->filter(function (FunctionContract $function) { + return $function->showInUi; + }); + } + return $functions->transform( function (FunctionContract $function) { return $function->getFunction(); } )->toArray(); - } public function remapFunctions(array $functions): array diff --git a/Modules/LlmDriver/app/Functions/Chat.php b/Modules/LlmDriver/app/Functions/Chat.php index 0f1b76a6..24a761b3 100644 --- a/Modules/LlmDriver/app/Functions/Chat.php +++ b/Modules/LlmDriver/app/Functions/Chat.php @@ -13,6 +13,8 @@ class Chat extends FunctionContract { use ChatHelperTrait, ToolsHelper; + public bool $showInUi = false; + public array $toolTypes = [ ToolTypes::NoFunction, ]; diff --git a/Modules/LlmDriver/app/Functions/CreateDocument.php b/Modules/LlmDriver/app/Functions/CreateDocument.php index 36fb6f78..b7fa3335 100644 --- a/Modules/LlmDriver/app/Functions/CreateDocument.php +++ b/Modules/LlmDriver/app/Functions/CreateDocument.php @@ -20,7 +20,7 @@ class CreateDocument extends FunctionContract protected string $name = 'create_document'; - protected string $description = 'Create a document in the collection of this local system'; + protected string $description = 'Create or Save a document into the collection of this local system using the content provided'; public function handle( Message $message): FunctionResponse diff --git a/Modules/LlmDriver/app/Functions/FunctionContract.php b/Modules/LlmDriver/app/Functions/FunctionContract.php index b2f0a302..3ea57766 100644 --- a/Modules/LlmDriver/app/Functions/FunctionContract.php +++ b/Modules/LlmDriver/app/Functions/FunctionContract.php @@ -10,6 +10,8 @@ abstract class FunctionContract { protected string $name; + public bool $showInUi = true; + public array $toolTypes = [ ToolTypes::Chat, ToolTypes::ChatCompletion, diff --git a/Modules/LlmDriver/app/LlmDriverClient.php b/Modules/LlmDriver/app/LlmDriverClient.php index 1a8680d0..9deaf3f7 100644 --- a/Modules/LlmDriver/app/LlmDriverClient.php +++ b/Modules/LlmDriver/app/LlmDriverClient.php @@ -2,6 +2,8 @@ namespace LlmLaraHub\LlmDriver; +use LlmLaraHub\LlmDriver\Functions\FunctionDto; + class LlmDriverClient { protected $drivers = []; @@ -42,7 +44,9 @@ protected function createDriver($name) public function getFunctionsForUi(): array { return collect( - LlmDriverFacade::driver('mock')->getFunctions() + LlmDriverFacade::driver('mock') + ->setLimitByShowInUi(true) + ->getFunctions() ) ->map(function ($item) { $item['name_formatted'] = str($item['name'])->headline()->toString(); diff --git a/Modules/LlmDriver/app/OllamaClient.php b/Modules/LlmDriver/app/OllamaClient.php index 3a4211aa..1583f2b5 100644 --- a/Modules/LlmDriver/app/OllamaClient.php +++ b/Modules/LlmDriver/app/OllamaClient.php @@ -7,10 +7,12 @@ use Illuminate\Http\Client\Pool; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; use Laravel\Pennant\Feature; use LlmLaraHub\LlmDriver\Requests\MessageInDto; use LlmLaraHub\LlmDriver\Responses\CompletionResponse; use LlmLaraHub\LlmDriver\Responses\EmbeddingsResponseDto; +use LlmLaraHub\LlmDriver\Responses\OllamaChatCompletionResponse; use LlmLaraHub\LlmDriver\Responses\OllamaCompletionResponse; class OllamaClient extends BaseClient @@ -93,7 +95,7 @@ public function functionPromptChat(array $messages, array $only = []): array */ public function chat(array $messages): CompletionResponse { - Log::info('LlmDriver::OllamaClient::completion'); + Log::info('LlmDriver::OllamaClient::chat'); $messages = $this->remapMessages($messages); @@ -108,10 +110,6 @@ public function chat(array $messages): CompletionResponse $payload = $this->modifyPayload($payload); - Log::info('LlmDriver::Ollama::chat', [ - 'payload' => $payload, - ]); - $response = $this->getClient()->post('/chat', $payload); if ($response->failed()) { @@ -121,7 +119,7 @@ public function chat(array $messages): CompletionResponse throw new \Exception('Ollama API Error Chat'); } - return OllamaCompletionResponse::from($response->json()); + return OllamaChatCompletionResponse::from($response->json()); } /** @@ -194,6 +192,14 @@ public function completion(string $prompt): CompletionResponse 'stream' => false, ]); + if ($response->failed()) { + Log::error('Ollama Completion API Error ', [ + 'error' => $response->body(), + ]); + throw new \Exception('Ollama API Error Completion'); + } + + put_fixture('ollama_completion.json', $response->json()); return OllamaCompletionResponse::from($response->json()); } @@ -222,7 +228,7 @@ public function getFunctions(): array { $functions = parent::getFunctions(); - return collect($functions)->map(function ($function) { + $results = collect($functions)->map(function ($function) { $properties = []; $required = []; @@ -254,7 +260,11 @@ public function getFunctions(): array ]; - })->toArray(); + })->values()->toArray(); + + put_fixture('ollama_functions.json', $results); + + return $results; } public function isAsync(): bool @@ -278,13 +288,6 @@ public function remapMessages(array $messages): array ->toArray(); })->toArray(); - if (in_array($this->getConfig('ollama')['models']['completion_model'], ['llama3.1', 'llama3'])) { - Log::info('[LaraChain] LlmDriver::OllamaClient::remapMessages'); - $messages = collect($messages)->reverse(); - } - - put_fixture('ollama_messages_after_remap.json', $messages->values()->toArray()); - - return $messages->values()->toArray(); + return $messages; } } diff --git a/Modules/LlmDriver/app/Responses/OllamaChatCompletionResponse.php b/Modules/LlmDriver/app/Responses/OllamaChatCompletionResponse.php new file mode 100644 index 00000000..68f4f529 --- /dev/null +++ b/Modules/LlmDriver/app/Responses/OllamaChatCompletionResponse.php @@ -0,0 +1,26 @@ + */ + #[MapInputName('message.tool_calls')] + public array $tool_calls = [], + #[MapInputName('prompt_eval_count')] + public ?int $input_tokens = null, + #[MapInputName('eval_count')] + public ?int $output_tokens = null, + public ?string $model = null, + ) { + } +} diff --git a/Modules/LlmDriver/app/Responses/OllamaCompletionResponse.php b/Modules/LlmDriver/app/Responses/OllamaCompletionResponse.php index abc1f769..8bf38b9d 100644 --- a/Modules/LlmDriver/app/Responses/OllamaCompletionResponse.php +++ b/Modules/LlmDriver/app/Responses/OllamaCompletionResponse.php @@ -8,13 +8,13 @@ class OllamaCompletionResponse extends CompletionResponse { public function __construct( - #[MapInputName('message.content')] + #[MapInputName('response')] public mixed $content, #[MapInputName('done_reason')] public string|Optional $stop_reason, public ?string $tool_used = '', /** @var array */ - #[MapInputName('message.tool_calls')] + #[MapInputName('tool_calls')] public array $tool_calls = [], #[MapInputName('prompt_eval_count')] public ?int $input_tokens = null, diff --git a/app/Domains/Orchestration/OrchestrateVersionTwo.php b/app/Domains/Orchestration/OrchestrateVersionTwo.php index ac8c3032..62d4be99 100644 --- a/app/Domains/Orchestration/OrchestrateVersionTwo.php +++ b/app/Domains/Orchestration/OrchestrateVersionTwo.php @@ -35,6 +35,7 @@ public function handle( Chat $chat, Message $message) { + $toolType = ToolTypes::ChatCompletion; //OpenAI //@see https://platform.openai.com/docs/guides/function-calling @@ -78,28 +79,32 @@ public function handle( * Here the user is just forcing a chat * they want to continue with the thread */ - if ($message->meta_data?->tool === 'chat') { - Log::info('[LaraChain] - Just Chatting'); - $this->justChat($chat, $message, ToolTypes::Chat); - } else { - $messages = $chat->getChatResponse(); + if ($message->meta_data?->tool === ToolTypes::Chat->value) { + $toolType = ToolTypes::Chat; - put_fixture('orchestrate_messages_fist_send.json', $messages); + Log::info('[LaraChain] - Setting it as a chat tool scope', [ + 'tool_type' => $toolType, + ]); + } - Log::info('[LaraChain] - Looking for Tools'); - $response = LlmDriverFacade::driver($message->getDriver()) - ->setToolType(ToolTypes::ChatCompletion) - ->chat($messages); + $messages = $chat->getChatResponse(); - if (! empty($response->tool_calls)) { - Log::info('[LaraChain] - Tools Found'); - $this->chatWithTools($chat, $message, $response); + put_fixture('orchestrate_messages_first_send.json', $messages); - } else { - //hmm - Log::info('[LaraChain] - No Tools found just gonna chat'); - $this->justChat($chat, $message, ToolTypes::NoFunction); - } + Log::info('[LaraChain] - Looking for Tools'); + + $response = LlmDriverFacade::driver($message->getDriver()) + ->setToolType($toolType) + ->chat($messages); + + if (! empty($response->tool_calls)) { + Log::info('[LaraChain] - Tools Found'); + $this->chatWithTools($chat, $message, $response); + + } else { + //hmm + Log::info('[LaraChain] - No Tools found just gonna chat'); + $this->justChat($chat, $message, ToolTypes::NoFunction); } } diff --git a/app/Jobs/SummarizeDocumentJob.php b/app/Jobs/SummarizeDocumentJob.php index 6565c044..980e1891 100644 --- a/app/Jobs/SummarizeDocumentJob.php +++ b/app/Jobs/SummarizeDocumentJob.php @@ -71,7 +71,6 @@ public function handle(): void } else { $prompt = Templatizer::appendContext(true) ->handle($this->prompt, $content); - } /** @var CompletionResponse $results */ @@ -81,30 +80,6 @@ public function handle(): void $this->results = $results->content; - if (Feature::active('verification_prompt_summary')) { - - $verifyPrompt = <<<'PROMPT' - This the content from all the documents in this collection. - Then that was passed into the LLM to summarize the results. - PROMPT; - - $dto = VerifyPromptInputDto::from( - [ - 'chattable' => $this->document->collection, - 'originalPrompt' => $prompt, - 'context' => $content, - 'llmResponse' => $this->results, - 'verifyPrompt' => $verifyPrompt, - ] - ); - - /** @var VerifyPromptOutputDto $response */ - $response = VerifyResponseAgent::verify($dto); - - $this->results = $response->response; - - } - $this->document->update([ 'summary' => $this->results, 'status_summary' => StatusEnum::SummaryComplete, diff --git a/app/Models/Document.php b/app/Models/Document.php index 838b5089..4e016824 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -171,6 +171,7 @@ public static function make( 'collection_id' => $collection->id, 'type' => TypesEnum::Txt, 'subject' => str($content)->limit(256)->toString(), + 'summary' => $content, 'original_content' => $content, 'status_summary' => StatusEnum::Pending, ]); diff --git a/app/Models/Message.php b/app/Models/Message.php index 5092abb3..3a4b8866 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -5,6 +5,7 @@ use App\Domains\Chat\MetaDataDto; use App\Domains\Chat\ToolsDto; use App\Domains\Messages\RoleEnum; +use Facades\App\Domains\Orchestration\OrchestrateVersionTwo; use App\Events\ChatUiUpdateEvent; use App\Events\MessageCreatedEvent; use App\Jobs\OrchestrateJob; @@ -217,53 +218,12 @@ public function reRun(): void public function run(): void { $message = $this; - $chat = $message->getChat(); - - notify_ui($chat, 'Working on it!'); - $meta_data = $message->meta_data; $meta_data->driver = $chat->getDriver(); $message->updateQuietly(['meta_data' => $meta_data]); - if ($message->meta_data?->tool === 'completion') { - Log::info('[LaraChain] Running Simple Completion'); - - $messages = $chat->getChatResponse(); - $response = LlmDriverFacade::driver($chat->getDriver())->chat($messages); - $response = $response->content; - - $chat->addInput( - message: $response, - role: RoleEnum::Assistant, - show_in_thread: true); - - notify_ui_complete($chat); - } elseif ($message->meta_data?->tool) { - /** - * @NOTE - * Quick win area for Ollama - */ - Log::info('[LaraChain] Running Tool that was chosen'); - - /** @phpstan-ignore-next-line */ - $tool = $message->meta_data?->tool; - - $this->batchJob([ - new OrchestrateJob($chat, $message), - ], $chat, $tool); - - } elseif (LlmDriverFacade::driver($chat->getDriver())->hasFunctions()) { - Log::info('[LaraChain] Running Orchestrate added to queue'); - $this->batchJob([ - new OrchestrateJob($chat, $message), - ], $chat, 'orchestrate'); - } else { - Log::info('[LaraChain] Simple Search and Summarize added to queue'); - $this->batchJob([ - new SimpleRetrieveRelatedOrchestrateJob($message), - ], $chat, 'simple_search_and_summarize'); - } + OrchestrateVersionTwo::handle($chat, $message); } diff --git a/phpstan.neon b/phpstan.neon index c88ed6f0..9a59a04f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,6 +28,7 @@ parameters: - vendor - app/Actions/* - app/Http/Resources/* + - Modules/*/tests/* - app/Http/Controllers/GoogleController.php checkMissingIterableValueType: false diff --git a/resources/js/Pages/Chat/Chatv2.vue b/resources/js/Pages/Chat/Chatv2.vue index d51db011..06c109d2 100644 --- a/resources/js/Pages/Chat/Chatv2.vue +++ b/resources/js/Pages/Chat/Chatv2.vue @@ -365,7 +365,7 @@ text-secondary"> Click a button below to choose a focus for your chat. This will help the system to be more specific on how it integrates your Collection and the Prompt. -
+