diff --git a/Modules/LlmDriver/app/BaseClient.php b/Modules/LlmDriver/app/BaseClient.php index 604b4ee8..4c5aba1c 100644 --- a/Modules/LlmDriver/app/BaseClient.php +++ b/Modules/LlmDriver/app/BaseClient.php @@ -47,6 +47,13 @@ public function setToolType(ToolTypes $toolType): self return $this; } + public function setSystemPrompt(string $systemPrompt = ''): self + { + $this->system = $systemPrompt; + + return $this; + } + public function setLimitByShowInUi(bool $limitByShowInUi): self { $this->limitByShowInUi = $limitByShowInUi; diff --git a/app/Domains/Projects/DailyReportService.php b/app/Domains/Projects/DailyReportService.php new file mode 100644 index 00000000..4a696e41 --- /dev/null +++ b/app/Domains/Projects/DailyReportService.php @@ -0,0 +1,63 @@ +get() as $project) { + $this->sendReport($project); + } + } + + public function sendReport(Project $project) + { + $tasks = Task::where('project_id', $project->id) + ->notCompleted() + ->where('due_date', '>=', now()->addDays(7)) + ->get() + /** @phpstan-ignore-next-line */ + ->transform(function (Task $item) { + return sprintf( + 'Task: %s %s %s', + $item->name, + $item->details, + $item->due_date + ); + })->implode(', '); + + $prompt = ProjectDailyReportPrompt::getPrompt($project, $tasks); + + $project->getChat()->addInput( + message: $prompt, + role: RoleEnum::User, + ); + + $messages = $project->getChat()->getMessageThread(); + + $results = LlmDriverFacade::driver($project->getDriver()) + ->setToolType(ToolTypes::Chat) + ->chat($messages); + + $project->getChat()->addInput( + message: $results->content, + role: RoleEnum::Assistant, + ); + + Log::info('DailyReportService::handle', [ + 'results' => $results->content, + ]); + + Notification::send($project->team->allUsers(), new DailyReport($results->content, $project)); + } +} diff --git a/app/Domains/Projects/KickOffProject.php b/app/Domains/Projects/KickOffProject.php index 7d060ca3..73035b52 100644 --- a/app/Domains/Projects/KickOffProject.php +++ b/app/Domains/Projects/KickOffProject.php @@ -20,7 +20,7 @@ public function handle(Project $project) $project->tasks()->delete(); - Orchestrate::handle($chat, $project->content, $project->system_prompt); + Orchestrate::handle($chat, $project->getContent(), $project->getSystemPrompt()); $chat->updateQuietly([ 'chat_status' => UiStatusEnum::Complete, diff --git a/app/Domains/Projects/Orchestrate.php b/app/Domains/Projects/Orchestrate.php index 940d5c80..338a2fd5 100644 --- a/app/Domains/Projects/Orchestrate.php +++ b/app/Domains/Projects/Orchestrate.php @@ -20,6 +20,7 @@ public function handle(Chat $chat, string $prompt, string $systemPrompt = ''): v $messages = $chat->getMessageThread(); $response = LlmDriverFacade::driver($chat->getDriver()) + ->setSystemPrompt($systemPrompt) ->setToolType(ToolTypes::Chat) ->chat($messages); @@ -61,6 +62,8 @@ public function handle(Chat $chat, string $prompt, string $systemPrompt = ''): v $response = LlmDriverFacade::driver( $chat->getDriver() ) + ->setToolType(ToolTypes::Chat) + ->setSystemPrompt($systemPrompt) ->chat($messages); $chat->addInput( diff --git a/app/Domains/Projects/ProjectDailyReportPrompt.php b/app/Domains/Projects/ProjectDailyReportPrompt.php new file mode 100644 index 00000000..baf85936 --- /dev/null +++ b/app/Domains/Projects/ProjectDailyReportPrompt.php @@ -0,0 +1,48 @@ +toISOString(); + + $context = $project->content; + + return << +You are an Ai marketing assistant specialized in digital marketing campaigns. Below is info about the campaign and previous conversations. + + +Using the ` section below, give the user a report of tasks that are due and when +so they can see what they need to do today and or in the next day or two. They +get this report every day so they can see what they need to do today and what. + +If there are not tasks then put in the Tasks sections "No tasks today or tomorrow" + + +TLDR: What is on the schedule + +Tasks: + * Task Name and due date + * Task Name and due date + + +Today's date is $now + +The list of tasks if any are: +$tasks + +The campaign content is: + +$context + +PROMPT; + } +} diff --git a/app/Http/Controllers/DailyReportSendController.php b/app/Http/Controllers/DailyReportSendController.php new file mode 100644 index 00000000..4f51b607 --- /dev/null +++ b/app/Http/Controllers/DailyReportSendController.php @@ -0,0 +1,16 @@ +session()->flash('flash.banner', 'Sent!'); + return back(); + } +} diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 89bf4b9e..9bd21aa0 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -113,7 +113,11 @@ public function chat(Project $project, Chat $chat) 'chat_status' => UiStatusEnum::InProgress->value, ]); - Orchestrate::handle($chat, $validated['input']); + Orchestrate::handle( + chat: $chat, + prompt: $validated['input'], + systemPrompt: $project->getSystemPrompt() + ); $chat->update([ 'chat_status' => UiStatusEnum::Complete->value, diff --git a/app/Models/Project.php b/app/Models/Project.php index 4d8c3c67..9eedee78 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Domains\Projects\StatusEnum; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -43,6 +44,12 @@ public function getDriver(): string return $this->chats()->first()->chat_driver?->value; } + public function scopeActive(Builder $query): Builder + { + return $query->where('end_date', '>=', now()) + ->orWhere('end_date', null); + } + public function getEmbeddingDriver(): string { return $this->chats()->first()->embedding_driver->value; @@ -77,4 +84,30 @@ public function getChat(): ?Chat { return $this->chats->first(); } + + public function getContent(): string + { + $context = $this->content; + $now = now()->toISOString(); + + return <<system_prompt; + $now = now()->toISOString(); + + return << + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage) + ->subject('Daily Report') + ->markdown('mail.daily_report', [ + 'message' => $this->message, + 'url' => route('projects.show', $this->project), + ]); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } +} diff --git a/database/factories/ChatFactory.php b/database/factories/ChatFactory.php index 3757f6ac..030da130 100644 --- a/database/factories/ChatFactory.php +++ b/database/factories/ChatFactory.php @@ -5,6 +5,7 @@ use App\Models\Collection; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; +use LlmLaraHub\LlmDriver\DriversEnum; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Chat> @@ -25,4 +26,15 @@ public function definition(): array 'chatable_type' => Collection::class, ]; } + + public function withDrivers(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'chat_driver' => DriversEnum::Claude->value, + 'embedding_driver' => DriversEnum::Claude->value, + ]; + }); + } + } diff --git a/resources/views/mail/daily_report.blade.php b/resources/views/mail/daily_report.blade.php new file mode 100644 index 00000000..c350cfe8 --- /dev/null +++ b/resources/views/mail/daily_report.blade.php @@ -0,0 +1,11 @@ + + +{{ $message }} + + +View the Project + + +Thanks,
+Your assistant! +
diff --git a/routes/web.php b/routes/web.php index bda7241d..9ecad88f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -124,6 +124,9 @@ function () { } ); + Route::post('/daily-report/{project}', \App\Http\Controllers\DailyReportSendController::class)->name('daily-report.send'); + + Route::controller(\App\Http\Controllers\AssistantEmailBoxSourceController::class)->group( function () { Route::get('/collections/{collection}/sources/email_source/create', 'create') diff --git a/tests/Feature/DailyReportServiceTest.php b/tests/Feature/DailyReportServiceTest.php new file mode 100644 index 00000000..67bbfeef --- /dev/null +++ b/tests/Feature/DailyReportServiceTest.php @@ -0,0 +1,62 @@ +create(); + + LlmDriverFacade::shouldReceive('driver->chat') + ->once() + ->andReturn( + CompletionResponse::from([ + 'content' => 'Test Content', + ]) + ); + + $team = Team::factory()->create(); + $team->users()->attach($user); + + $project = Project::factory()->create([ + 'end_date' => now()->addDays(7), + 'team_id' => $team->id, + ]); + + + + $chat = Chat::factory()->withDrivers()->create([ + 'chatable_id' => $project->id, + 'chatable_type' => Project::class, + ]); + + Task::factory()->create([ + 'user_id' => $user->id, + 'project_id' => $project->id, + ]); + + (new DailyReportService())->handle(); + + Notification::assertSentTo($user, DailyReport::class); + + } +}