Skip to content

Commit

Permalink
enh: custom UI for scoped context chat
Browse files Browse the repository at this point in the history
Signed-off-by: Anupam Kumar <[email protected]>
  • Loading branch information
kyteinsky committed Mar 19, 2024
1 parent e87a8e0 commit 0a5aab5
Show file tree
Hide file tree
Showing 8 changed files with 630 additions and 78 deletions.
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
1 change: 0 additions & 1 deletion lib/Controller/FreePromptController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
71 changes: 71 additions & 0 deletions lib/Controller/PreviewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Nextcloud - TpAssistant
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Julien Veyssier <[email protected]>
* @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);
}
}
98 changes: 56 additions & 42 deletions lib/Service/AssistantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -132,6 +133,28 @@ 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) !== 3)
|| !isset($inputs['prompt'])
|| !is_string($inputs['prompt'])
) {
throw new \Exception('Invalid input(s)');
}

if (count($inputs) === 3) {
if (!isset($inputs['scopeType']) || !is_string($inputs['scopeType'])
|| !isset($inputs['scopeList']) || !is_array($inputs['scopeList'])) {
throw new \Exception('Invalid input(s)');
}
$inputs['scopeList'] = array_filter(
array_map(fn (array $scope) => $scope['id'] ?? null, $inputs['scopeList']),
fn ($scope) => is_string($scope),
);
}

break;
}
default:
Expand Down Expand Up @@ -260,29 +283,52 @@ 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':
{
// 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,
Expand All @@ -301,24 +347,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,
Expand All @@ -338,24 +368,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,
Expand Down
64 changes: 64 additions & 0 deletions lib/Service/PreviewService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

/**
* Nextcloud - TpAssistant
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Julien Veyssier
* @copyright Julien Veyssier 2022
*/

namespace OCA\TpAssistant\Service;

use OCP\Files\File;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use Psr\Log\LoggerInterface;

class PreviewService {

public function __construct(
private IRootFolder $root,
private LoggerInterface $logger,
private IPreview $previewManager,
private IMimeTypeDetector $mimeTypeDetector,
) {
}

/**
* @param int $fileId
* @param string $userId
* @param int $x
* @param int $y
* @return array|null
* @throws \OCP\Files\NotPermittedException
* @throws \OC\User\NoUserException
*/
public function getFilePreviewFile(int $fileId, string $userId, int $x = 100, int $y = 100): ?array {
$userFolder = $this->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;
}
}
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<errorLevel type="suppress">
<referencedClass name="Doctrine\DBAL\Schema\Table" />
<referencedClass name="Doctrine\DBAL\Schema\Schema" />
<referencedClass name="OC\User\NoUserException" />
</errorLevel>
</UndefinedDocblockClass>
<InvalidClass>
Expand Down
Loading

0 comments on commit 0a5aab5

Please sign in to comment.