diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 74297db3..3bb0d957 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -92,6 +92,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(TaskSuccessfulEvent::class, TaskSuccessfulListener::class); $context->registerEventListener(TaskFailedEvent::class, TaskFailedListener::class); $context->registerEventListener(TaskSuccessfulEvent::class, ChattyLLMTaskListener::class); + $context->registerEventListener(TaskFailedEvent::class, ChattyLLMTaskListener::class); $context->registerEventListener(TaskSuccessfulEvent::class, FileActionTaskSuccessfulListener::class); $context->registerEventListener(TaskSuccessfulEvent::class, NewFileMenuTaskSuccessfulListener::class); $context->registerEventListener(TaskFailedEvent::class, FileActionTaskFailedListener::class); diff --git a/lib/Listener/ChattyLLMTaskListener.php b/lib/Listener/ChattyLLMTaskListener.php index 5cdc3882..cbb7e4cb 100644 --- a/lib/Listener/ChattyLLMTaskListener.php +++ b/lib/Listener/ChattyLLMTaskListener.php @@ -13,15 +13,17 @@ use OCA\Assistant\Db\ChattyLLM\Message; use OCA\Assistant\Db\ChattyLLM\MessageMapper; use OCA\Assistant\Db\ChattyLLM\SessionMapper; +use OCA\Assistant\Service\NotificationService; use OCA\Assistant\Service\TaskProcessingService; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCP\TaskProcessing\Events\TaskFailedEvent; use OCP\TaskProcessing\Events\TaskSuccessfulEvent; use OCP\TaskProcessing\Task; use Psr\Log\LoggerInterface; /** - * @template-implements IEventListener + * @template-implements IEventListener */ class ChattyLLMTaskListener implements IEventListener { @@ -30,10 +32,26 @@ public function __construct( private SessionMapper $sessionMapper, private TaskProcessingService $taskProcessingService, private LoggerInterface $logger, + private NotificationService $notificationService, ) { } public function handle(Event $event): void { + if ($event instanceof TaskFailedEvent) { + $task = $event->getTask(); + $customId = $task->getCustomId(); + if (preg_match('/^chatty-llm:(\d+)/', $customId, $matches)) { + $sessionId = (int)$matches[1]; + $session = $this->sessionMapper->getUserSession($task->getUserId(), $sessionId); + $assignmentId = $session->getAssignmentId(); + if ($assignmentId !== null) { + $this->notificationService->sendAssignmentNotification( + $task->getUserId(), $task, $session + ); + } + } + return; + } if (!($event instanceof TaskSuccessfulEvent)) { return; } @@ -127,6 +145,13 @@ public function handle(Event $event): void { // Set flag that the conversation summary needs to be regenerated $session->setIsSummaryUpToDate(false); + $assignmentId = $session->getAssignmentId(); + if ($assignmentId !== null) { + $this->notificationService->sendAssignmentNotification( + $task->getUserId(), $task, $session + ); + } + $this->sessionMapper->update($session); } } diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index d1695d13..2f38de2a 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -338,6 +338,81 @@ public function prepare(INotification $notification, string $languageCode): INot return $notification; + case 'assignment_approval_pending': + $subject = $l->t('Assignment run pending review'); + + $iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')); + + $message = $l->t('"%s" awaits your review before continuing.', [$params['sessionTitle']]); + + $notification + ->setParsedSubject($subject) + ->setParsedMessage($message) + // TODO: link directly to assignment + ->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']])) + ->setIcon($iconUrl); + + $actionLabel = $l->t('View assignment'); + $action = $notification->createAction(); + $action->setLabel($actionLabel) + ->setParsedLabel($actionLabel) + ->setLink($notification->getLink(), IAction::TYPE_WEB) + ->setPrimary(true); + + $notification->addParsedAction($action); + + return $notification; + + case 'assignment_successful': + $subject = $l->t('Assignment succeeded'); + + $iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')); + + $message = $l->t('"%s" was run successfully.', [$params['sessionTitle']]); + + $notification + ->setParsedSubject($subject) + ->setParsedMessage($message) + // TODO: link directly to assignment + ->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']])) + ->setIcon($iconUrl); + + $actionLabel = $l->t('View assignment result'); + $action = $notification->createAction(); + $action->setLabel($actionLabel) + ->setParsedLabel($actionLabel) + ->setLink($notification->getLink(), IAction::TYPE_WEB) + ->setPrimary(true); + + $notification->addParsedAction($action); + + return $notification; + + case 'assignment_failure': + $subject = $l->t('Assignment failed'); + + $iconUrl = $this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')); + + $message = $l->t('"%s" failed to run.', [$params['sessionTitle']]); + + $notification + ->setParsedSubject($subject) + ->setParsedMessage($message) + // TODO: link directly to assignment + ->setLink($this->url->linkToRouteAbsolute(Application::APP_ID . '.assistant.getAssistantStandalonePage', ['sessionId' => $params['sessionId']])) + ->setIcon($iconUrl); + + $actionLabel = $l->t('View assignment result'); + $action = $notification->createAction(); + $action->setLabel($actionLabel) + ->setParsedLabel($actionLabel) + ->setLink($notification->getLink(), IAction::TYPE_WEB) + ->setPrimary(true); + + $notification->addParsedAction($action); + + return $notification; + default: // Unknown subject => Unknown notification => throw throw new InvalidArgumentException(); diff --git a/lib/Service/NotificationService.php b/lib/Service/NotificationService.php index 82a59664..5fc3ab14 100644 --- a/lib/Service/NotificationService.php +++ b/lib/Service/NotificationService.php @@ -9,6 +9,7 @@ use DateTime; use OCA\Assistant\AppInfo\Application; +use OCA\Assistant\Db\ChattyLLM\Session; use OCP\IURLGenerator; use OCP\Notification\IManager as INotificationManager; use OCP\TaskProcessing\Task; @@ -133,4 +134,34 @@ public function sendNewImageFileNotification( $manager->notify($notification); } + + public function sendAssignmentNotification(?string $userId, Task $task, Session $session): void { + if ($userId === null) { + return; + } + $manager = $this->notificationManager; + $notification = $manager->createNotification(); + + $taskSuccessful = $task->getStatus() === Task::STATUS_SUCCESSFUL; + $pendingActions = $session->getAgencyPendingActions(); + + $params = [ + 'sessionId' => (string)$session->getId(), + 'sessionTitle' => $session->getTitle(), + ]; + + $subject = $taskSuccessful + ? ($pendingActions === null + ? 'assignment_successful' + : 'assignment_approval_pending') + : 'assignment_failure'; + + $notification->setApp(Application::APP_ID) + ->setUser($userId) + ->setDateTime(new DateTime()) + ->setSubject($subject, $params) + ->setObject('session', (string)$session->getId()); + + $manager->notify($notification); + } }