From 93b90aa0c2323e6e15caa3315a48af18fc6b5270 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 4 Mar 2024 18:05:35 +0530 Subject: [PATCH] enh: custom UI for scoped context chat Signed-off-by: Anupam Kumar --- appinfo/routes.php | 2 + lib/Controller/FreePromptController.php | 1 - lib/Controller/PreviewController.php | 71 ++++ lib/Service/AssistantService.php | 95 +++-- lib/Service/PreviewService.php | 64 +++ psalm.xml | 1 + src/components/AssistantFormInputs.vue | 120 ++++-- .../ContextChat/ContextChatInputForm.vue | 368 ++++++++++++++++++ 8 files changed, 644 insertions(+), 78 deletions(-) create mode 100644 lib/Controller/PreviewController.php create mode 100644 lib/Service/PreviewService.php create mode 100644 src/components/ContextChat/ContextChatInputForm.vue diff --git a/appinfo/routes.php b/appinfo/routes.php index 324554f4..dd9ce373 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -20,6 +20,8 @@ ['name' => 'FreePrompt#cancelGeneration', 'url' => '/f/cancel_generation', 'verb' => 'POST'], ['name' => 'SpeechToText#getResultPage', 'url' => '/stt/result-page/{metaTaskId}', 'verb' => 'GET'], + + ['name' => 'preview#getFileImage', 'url' => '/preview', 'verb' => 'GET'], ], 'ocs' => [ ['name' => 'assistantApi#getAvailableTaskTypes', 'url' => '/api/{apiVersion}/task-types', 'verb' => 'GET', 'requirements' => $requirements], diff --git a/lib/Controller/FreePromptController.php b/lib/Controller/FreePromptController.php index 67c84683..4907c76b 100644 --- a/lib/Controller/FreePromptController.php +++ b/lib/Controller/FreePromptController.php @@ -12,7 +12,6 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; - use OCP\IL10N; use OCP\IRequest; diff --git a/lib/Controller/PreviewController.php b/lib/Controller/PreviewController.php new file mode 100644 index 00000000..6f15b7a3 --- /dev/null +++ b/lib/Controller/PreviewController.php @@ -0,0 +1,71 @@ + + * @copyright Julien Veyssier 2022 + */ + +namespace OCA\TpAssistant\Controller; + +use Exception; +use OCA\TpAssistant\Service\PreviewService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\DataDownloadResponse; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\Response; +use OCP\IRequest; +use Psr\Log\LoggerInterface; +use Throwable; + +class PreviewController extends Controller { + + public function __construct( + string $appName, + IRequest $request, + private PreviewService $imageService, + private LoggerInterface $logger, + private ?string $userId + ) { + parent::__construct($appName, $request); + } + + /** + * @param int $id + * @param int $x + * @param int $y + * @return DataDownloadResponse|DataResponse|RedirectResponse + */ + #[NoAdminRequired] + #[NoCSRFRequired] + public function getFileImage(int $id, int $x = 100, int $y = 100): Response { + try { + $preview = $this->imageService->getFilePreviewFile($id, $this->userId, $x, $y); + if ($preview === null) { + $this->logger->error('No preview for user "' . $this->userId . '"'); + return new DataResponse('', Http::STATUS_NOT_FOUND); + } + + if ($preview['type'] === 'file') { + return new DataDownloadResponse( + $preview['file']->getContent(), + (string)Http::STATUS_OK, + $preview['file']->getMimeType() + ); + } elseif ($preview['type'] === 'icon') { + return new RedirectResponse($preview['icon']); + } + } catch (Exception | Throwable $e) { + $this->logger->error('getImage error', ['exception' => $e]); + return new DataResponse('', Http::STATUS_NOT_FOUND); + } + return new DataResponse('', Http::STATUS_NOT_FOUND); + } +} diff --git a/lib/Service/AssistantService.php b/lib/Service/AssistantService.php index 4ec949ec..d392be11 100644 --- a/lib/Service/AssistantService.php +++ b/lib/Service/AssistantService.php @@ -22,6 +22,7 @@ use OCP\Lock\LockedException; use OCP\PreConditionNotMetException; use OCP\SpeechToText\ISpeechToTextManager; +use OCP\TextProcessing\Exception\TaskFailureException; use OCP\TextProcessing\FreePromptTaskType; use OCP\TextProcessing\IManager as ITextProcessingManager; use OCP\TextProcessing\ITaskType; @@ -132,6 +133,25 @@ private function sanitizeInputs(string $type, array $inputs): array { if (count($inputs) !== 2) { throw new \Exception('Invalid input(s)'); } + break; + } + case 'OCA\\ContextChat\\TextProcessing\\ContextChatTaskType': + { + if ((count($inputs) !== 1 && count($inputs) !== 4) + || !isset($inputs['prompt']) + || !is_string($inputs['prompt']) + ) { + throw new \Exception('Invalid input(s)'); + } + + if (count($inputs) === 4) { + if (!isset($inputs['scopeType']) || !is_string($inputs['scopeType']) + || !isset($inputs['scopeList']) || !is_array($inputs['scopeList']) + || !isset($inputs['scopeListMeta']) || !is_array($inputs['scopeListMeta'])) { + throw new \Exception('Invalid input(s)'); + } + } + break; } default: @@ -260,11 +280,9 @@ private function cancelOcpTaskOfMetaTask(string $userId, MetaTask $metaTask): vo * @param string $appId * @param string $userId * @param string $identifier - * @return MetaTask - * @throws PreConditionNotMetException - * @throws Exception + * @return TextProcessingTask */ - public function runTextProcessingTask(string $type, array $inputs, string $appId, string $userId, string $identifier): MetaTask { + private function createTextProcessingTask(string $type, array $inputs, string $appId, string $userId, string $identifier): TextProcessingTask { $inputs = $this->sanitizeInputs($type, $inputs); switch ($type) { case 'copywriter': @@ -272,17 +290,42 @@ public function runTextProcessingTask(string $type, array $inputs, string $appId // Format the input prompt $input = $this->formattedCopywriterPrompt($inputs['writingStyle'], $inputs['sourceMaterial']); $task = new TextProcessingTask(FreePromptTaskType::class, $input, $appId, $userId, $identifier); - $this->textProcessingManager->runTask($task); + break; + } + case 'OCA\\ContextChat\\TextProcessing\\ContextChatTaskType': + { + $input = json_encode($inputs); + if ($input === false) { + throw new \Exception('Invalid inputs for ContextChatTaskType'); + } + + $task = new TextProcessingTask($type, $input, $appId, $userId, $identifier); break; } default: { $input = $inputs['prompt']; $task = new TextProcessingTask($type, $input, $appId, $userId, $identifier); - $this->textProcessingManager->runTask($task); break; } } + return $task; + } + + /** + * @param string $type + * @param array $inputs + * @param string $appId + * @param string $userId + * @param string $identifier + * @return MetaTask + * @throws Exception + * @throws PreConditionNotMetException + * @throws TaskFailureException + */ + public function runTextProcessingTask(string $type, array $inputs, string $appId, string $userId, string $identifier): MetaTask { + $task = $this->createTextProcessingTask($type, $inputs, $appId, $userId, $identifier); + $this->textProcessingManager->runTask($task); return $this->metaTaskMapper->createMetaTask( $userId, $inputs, $task->getOutput(), time(), $task->getId(), $type, @@ -301,24 +344,8 @@ public function runTextProcessingTask(string $type, array $inputs, string $appId * @throws PreConditionNotMetException */ public function scheduleTextProcessingTask(string $type, array $inputs, string $appId, string $userId, string $identifier): MetaTask { - $inputs = $this->sanitizeInputs($type, $inputs); - switch ($type) { - case 'copywriter': - { - // Format the input prompt - $input = $this->formattedCopywriterPrompt($inputs['writingStyle'], $inputs['sourceMaterial']); - $task = new TextProcessingTask(FreePromptTaskType::class, $input, $appId, $userId, $identifier); - $this->textProcessingManager->scheduleTask($task); - break; - } - default: - { - $input = $inputs['prompt']; - $task = new TextProcessingTask($type, $input, $appId, $userId, $identifier); - $this->textProcessingManager->scheduleTask($task); - break; - } - } + $task = $this->createTextProcessingTask($type, $inputs, $appId, $userId, $identifier); + $this->textProcessingManager->scheduleTask($task); return $this->metaTaskMapper->createMetaTask( $userId, $inputs, $task->getOutput(), time(), $task->getId(), $type, @@ -338,24 +365,8 @@ public function scheduleTextProcessingTask(string $type, array $inputs, string $ * @throws \Exception */ public function runOrScheduleTextProcessingTask(string $type, array $inputs, string $appId, string $userId, string $identifier): MetaTask { - $inputs = $this->sanitizeInputs($type, $inputs); - switch ($type) { - case 'copywriter': - { - // Format the input prompt - $input = $this->formattedCopywriterPrompt($inputs['writingStyle'], $inputs['sourceMaterial']); - $task = new TextProcessingTask(FreePromptTaskType::class, $input, $appId, $userId, $identifier); - $this->textProcessingManager->runOrScheduleTask($task); - break; - } - default: - { - $input = $inputs['prompt']; - $task = new TextProcessingTask($type, $input, $appId, $userId, $identifier); - $this->textProcessingManager->runOrScheduleTask($task); - break; - } - } + $task = $this->createTextProcessingTask($type, $inputs, $appId, $userId, $identifier); + $this->textProcessingManager->runOrScheduleTask($task); return $this->metaTaskMapper->createMetaTask( $userId, $inputs, $task->getOutput(), time(), $task->getId(), $type, diff --git a/lib/Service/PreviewService.php b/lib/Service/PreviewService.php new file mode 100644 index 00000000..180a74f5 --- /dev/null +++ b/lib/Service/PreviewService.php @@ -0,0 +1,64 @@ +root->getUserFolder($userId); + $files = $userFolder->getById($fileId); + if (count($files) > 0 && $files[0] instanceof File) { + $file = $files[0]; + if ($this->previewManager->isMimeSupported($file->getMimeType())) { + try { + return [ + 'type' => 'file', + 'file' => $this->previewManager->getPreview($file, $x, $y), + ]; + } catch (NotFoundException $e) { + $this->logger->error('Mimetype is supported but no preview available', ['exception' => $e]); + } + } + // fallback: mimetype icon + return [ + 'type' => 'icon', + 'icon' => $this->mimeTypeDetector->mimeTypeIcon($file->getMimeType()), + ]; + } + return null; + } +} diff --git a/psalm.xml b/psalm.xml index 812a7f51..14681373 100644 --- a/psalm.xml +++ b/psalm.xml @@ -37,6 +37,7 @@ + diff --git a/src/components/AssistantFormInputs.vue b/src/components/AssistantFormInputs.vue index 6a422038..8071ec4d 100644 --- a/src/components/AssistantFormInputs.vue +++ b/src/components/AssistantFormInputs.vue @@ -57,6 +57,12 @@
+
+ + {{ t('assistant', 'Reduce context') }} + + +