Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rudimentary copywriter functionality, implement assistant metatask wrapper #34

Merged
merged 31 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ab645a
Implement copywriter
MB-Finski Jan 29, 2024
5970c0d
Cs:fix and psalm appeasal
MB-Finski Jan 29, 2024
46e43ae
Typo fix and minor layout fix for text processing output
MB-Finski Jan 29, 2024
7364add
Rename task modality to task 'category'
MB-Finski Jan 29, 2024
37f6cd6
Add command and cron job for task cleanup
MB-Finski Jan 29, 2024
e579ad5
Fix a typo in UI
MB-Finski Jan 29, 2024
1e5684d
fix psalm errors
julien-nc Jan 30, 2024
292bbb0
Improve copywriter prompt
MB-Finski Jan 30, 2024
dd2b2f1
Don't rely on injected userId in services
MB-Finski Jan 30, 2024
d0e4210
Enable CSRF for getTextProcessingResul
MB-Finski Jan 30, 2024
9b67447
Fix error handling and logging
MB-Finski Jan 30, 2024
415aa6f
More psalm fixes
MB-Finski Jan 30, 2024
421c140
Fix typos in app constants
MB-Finski Jan 31, 2024
f1f53de
More typo fixes
MB-Finski Jan 31, 2024
2fbb25b
Check that user id is set in the controllers
MB-Finski Jan 31, 2024
84494cf
Fix/improve task cleanup default idle time logic
MB-Finski Jan 31, 2024
e779a7a
Improve route naming
MB-Finski Jan 31, 2024
e8e9aa9
Use typed arguments
MB-Finski Jan 31, 2024
9972d47
Improve text parsing method
MB-Finski Jan 31, 2024
6c141c7
Disallow access to assistant task endpoints by anonymous users
MB-Finski Jan 31, 2024
36f5a05
Remove unnecessary UI component
MB-Finski Jan 31, 2024
7733d87
Cs:fix
MB-Finski Jan 31, 2024
95b456b
Fix regeneration of results when displaying a notification
MB-Finski Jan 31, 2024
42167ad
fix notification target logic to allow custom targets later
julien-nc Jan 31, 2024
bfb7cf2
fix passing task category prop to plain text modal
julien-nc Jan 31, 2024
192edc2
split STT result in 2 components: modal and result itself so we can d…
julien-nc Jan 31, 2024
11a9537
rename task to metaTask everywhere for clarity, create a new table, d…
julien-nc Jan 31, 2024
c5c94ae
rename Copywriter to 'Context write'
julien-nc Feb 1, 2024
15b317c
fix resolveMetaTaskToOcpTask, keep OCA.TPAssistant.openAssistantResul…
julien-nc Feb 1, 2024
7d446d0
fix resolving meta task to ocp task
julien-nc Feb 1, 2024
2591760
use initial prompt as source material if copywriter gets selected
julien-nc Feb 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ include text processing providers to:
* Get an answer from a free prompt
* Reformulate (OpenAi/LocalAi only)
]]> </description>
<version>1.0.3</version>
<version>1.0.5</version>
<licence>agpl</licence>
<author>Julien Veyssier</author>
<namespace>TpAssistant</namespace>
Expand All @@ -49,10 +49,11 @@ include text processing providers to:
<screenshot>https://github.com/nextcloud/assistant/raw/main/img/screenshot3.jpg</screenshot>
<background-jobs>
<job>OCA\TpAssistant\Cron\CleanupImageGenerations</job>
<job>OCA\TpAssistant\Cron\CleanupTranscriptions</job>
<job>OCA\TpAssistant\Cron\CleanupAssistantTasks</job>
</background-jobs>
<commands>
<command>OCA\TpAssistant\Command\CleanupImageGenerations</command>
<command>OCA\TpAssistant\Command\CleanupAssistantTasks</command>
MB-Finski marked this conversation as resolved.
Show resolved Hide resolved
</commands>
<dependencies>
<nextcloud min-version="28" max-version="29"/>
Expand Down
10 changes: 6 additions & 4 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
['name' => 'config#setConfig', 'url' => '/config', 'verb' => 'PUT'],
['name' => 'config#setAdminConfig', 'url' => '/admin-config', 'verb' => 'PUT'],

['name' => 'assistant#getTextProcessingTaskResultPage', 'url' => '/t/{taskId}', 'verb' => 'GET'],
['name' => 'assistant#runTextProcessingTask', 'url' => '/run', 'verb' => 'POST'],
['name' => 'assistant#runOrScheduleTextProcessingTask', 'url' => '/run-or-schedule', 'verb' => 'POST'],
['name' => 'assistant#getTextProcessingTaskResultPage', 'url' => '/task/view/{taskId}', 'verb' => 'GET'],
['name' => 'assistant#runTextProcessingTask', 'url' => '/task/run', 'verb' => 'POST'],
['name' => 'assistant#scheduleTextProcessingTask', 'url' => '/task/schedule', 'verb' => 'POST'],
['name' => 'assistant#runOrScheduleTextProcessingTask', 'url' => '/task/run-or-schedule', 'verb' => 'POST'],
['name' => 'assistant#getTextProcessingResult', 'url' => '/task/{taskId}', 'verb' => 'GET'],
['name' => 'assistant#parseTextFromFile', 'url' => '/parse-file', 'verb' => 'POST'],

['name' => 'Text2Image#processPrompt', 'url' => '/i/process_prompt', 'verb' => 'POST'],
['name' => 'Text2Image#getPromptHistory', 'url' => '/i/prompt_history', 'verb' => 'GET'],
Expand All @@ -25,7 +28,6 @@
['name' => 'FreePrompt#cancelGeneration', 'url' => '/f/cancel_generation', 'verb' => 'POST'],

['name' => 'SpeechToText#getResultPage', 'url' => '/stt/resultPage', 'verb' => 'GET'],
['name' => 'SpeechToText#getTranscript', 'url' => '/stt/transcript', 'verb' => 'GET'],
['name' => 'SpeechToText#transcribeAudio', 'url' => '/stt/transcribeAudio', 'verb' => 'POST'],
['name' => 'SpeechToText#transcribeFile', 'url' => '/stt/transcribeFile', 'verb' => 'POST'],
],
Expand Down
12 changes: 11 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
}
],
"require": {
"php": "^8.0"
"php": "^8.0",
"erusev/parsedown": "^1.7",
"phpoffice/phpword": "^1.2"
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
Expand All @@ -25,5 +27,13 @@
"psalm/phar": "^5.16",
"nextcloud/ocp": "dev-master",
"phpunit/phpunit": "^9.5"
},
"config": {
"sort-packages": true,
"optimize-autoloader": true,
"platform": {
"php": "8.0"
},
"autoloader-suffix": "TpAssistant"
}
}
11 changes: 8 additions & 3 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
class Application extends App implements IBootstrap {

public const APP_ID = 'assistant';
public const DEFAULT_ASSISTANT_TASK_IDLE_TIME = 60 * 60 * 24 * 14; // 14 days

public const MAX_STORED_IMAGE_PROMPTS_PER_USER = 5;
public const MAX_STORED_TEXT_PROMPTS_PER_USER = 5;
Expand All @@ -40,9 +41,13 @@ class Application extends App implements IBootstrap {
public const IMAGE_FOLDER = 'generated_images';
public const SPEECH_TO_TEXT_REC_FOLDER = 'stt_recordings';

public const TASK_TYPE_TEXT_GEN = 0;
public const TASK_TYPE_TEXT_TO_IMAGE = 1;
public const TASK_TYPE_SPEECH_TO_TEXT = 2;
public const STT_TASK_SCHEDULED = 0;
public const STT_TASK_SUCCESSFUL = 1;
public const STT_TASK_FAILED = -1;

public const TASK_CATEGORY_TEXT_GEN = 0;
public const TASK_CATEGORY_TEXT_TO_IMAGE = 1;
public const TASK_CATEGORY_SPEECH_TO_TEXT = 2;

public function __construct(array $urlParams = []) {
parent::__construct(self::APP_ID, $urlParams);
Expand Down
54 changes: 54 additions & 0 deletions lib/Command/CleanupAssistantTasks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

// SPDX-FileCopyrightText: Sami Finnilä <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OCA\TpAssistant\Command;

use Exception;
use OC\Core\Command\Base;
MB-Finski marked this conversation as resolved.
Show resolved Hide resolved
use OCA\TpAssistant\AppInfo\Application;
use OCA\TpAssistant\Db\MetaTaskMapper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CleanupAssistantTasks extends Base {
public function __construct(
private MetaTaskMapper $metaTaskMapper,
) {
parent::__construct();
}

protected function configure() {
$maxIdleTimeSetting = Application::DEFAULT_ASSISTANT_TASK_IDLE_TIME;
$this->setName('assistant:task_cleanup')
->setDescription('Cleanup assistant tasks')
->addArgument(
'max_age',
InputArgument::OPTIONAL,
'The max idle time (in seconds)',
$maxIdleTimeSetting
);
}

protected function execute(InputInterface $input, OutputInterface $output) {
$maxAge = intval($input->getArgument('max_age'));

if ($maxAge < 1) {
$output->writeln('Invalid value for max_age: ' . $maxAge);
return 1;
}

$output->writeln('Cleanning up assistant tasks older than ' . $maxAge . ' seconds.');
try {
$cleanedUp = $this->metaTaskMapper->cleanupOldMetaTasks($maxAge);
} catch (Exception $e) {
$output->writeln('Error: ' . $e->getMessage());
return 1;
}

$output->writeln('Deleted ' . $cleanedUp . ' idle tasks.');
return 0;
}
}
119 changes: 91 additions & 28 deletions lib/Controller/AssistantController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@
use OCA\TpAssistant\Service\AssistantService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;

use OCP\IRequest;

class AssistantController extends Controller {

public function __construct(
string $appName,
IRequest $request,
string $appName,
IRequest $request,
private AssistantService $assistantService,
private IInitialState $initialStateService,
private ?string $userId
private IInitialState $initialStateService,
private ?string $userId,
) {
parent::__construct($appName, $request);
}
Expand All @@ -33,59 +31,124 @@ public function __construct(
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[BruteForceProtection(action: 'taskResultPage')]
public function getTextProcessingTaskResultPage(int $taskId): TemplateResponse {
$task = $this->assistantService->getTextProcessingTask($this->userId, $taskId);
if ($task === null) {
$response = new TemplateResponse(
'',
'403',
[],
TemplateResponse::RENDER_AS_ERROR
);
$response->setStatus(Http::STATUS_NOT_FOUND);
$response->throttle(['userId' => $this->userId, 'taskId' => $taskId]);
return $response;

if ($this->userId !== null) {
$task = $this->assistantService->getTextProcessingTask($this->userId, $taskId);
if ($task !== null) {
$this->initialStateService->provideInitialState('task', $task->jsonSerializeCc());
return new TemplateResponse(Application::APP_ID, 'taskResultPage');
}
}
return new TemplateResponse('', '403', [], TemplateResponse::RENDER_AS_ERROR, Http::STATUS_FORBIDDEN);
}

/**
* @param int $taskId
* @return DataResponse
*/
#[NoAdminRequired]
public function getTextProcessingResult(int $taskId): DataResponse {
MB-Finski marked this conversation as resolved.
Show resolved Hide resolved

if ($this->userId !== null) {
$task = $this->assistantService->getTextProcessingTask($this->userId, $taskId);
if ($task !== null) {
return new DataResponse([
'task' => $task->jsonSerializeCc(),
]);
}
}
return new DataResponse('', Http::STATUS_NOT_FOUND);
}

/**
* @param array $inputs
* @param string $type
* @param string $appId
* @param string $identifier
* @return DataResponse
*/
#[NoAdminRequired]
public function runTextProcessingTask(string $type, array $inputs, string $appId, string $identifier): DataResponse {
if ($this->userId === null) {
return new DataResponse('Unknow user', Http::STATUS_BAD_REQUEST);
}

try {
$task = $this->assistantService->runTextProcessingTask($type, $inputs, $appId, $this->userId, $identifier);
} catch (\Exception | \Throwable $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
}
$this->initialStateService->provideInitialState('task', $task->jsonSerialize());
return new TemplateResponse(Application::APP_ID, 'taskResultPage');
return new DataResponse([
'task' => $task->jsonSerializeCc(),
]);
}

/**
* @param string $input
* @param array $inputs
* @param string $type
* @param string $appId
* @param string $identifier
* @return DataResponse
*/
#[NoAdminRequired]
public function runTextProcessingTask(string $type, string $input, string $appId, string $identifier): DataResponse {
public function scheduleTextProcessingTask(string $type, array $inputs, string $appId, string $identifier): DataResponse {
if ($this->userId === null) {
return new DataResponse('Unknow user', Http::STATUS_BAD_REQUEST);
}

try {
$task = $this->assistantService->runTextProcessingTask($type, $input, $appId, $this->userId, $identifier);
$task = $this->assistantService->scheduleTextProcessingTask($type, $inputs, $appId, $this->userId, $identifier);
} catch (\Exception | \Throwable $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
}
return new DataResponse([
'task' => $task->jsonSerialize(),
'task' => $task->jsonSerializeCc(),
]);
}

/**
* @param string $input
* @param array $inputs
* @param string $type
* @param string $appId
* @param string $identifier
* @return DataResponse
*/
#[NoAdminRequired]
public function runOrScheduleTextProcessingTask(string $type, string $input, string $appId, string $identifier): DataResponse {
public function runOrScheduleTextProcessingTask(string $type, array $inputs, string $appId, string $identifier): DataResponse {
if ($this->userId === null) {
return new DataResponse('Unknow user', Http::STATUS_BAD_REQUEST);
}

try {
$task = $this->assistantService->runOrScheduleTextProcessingTask($type, $inputs, $appId, $this->userId, $identifier);
} catch (\Exception | \Throwable $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
}
return new DataResponse([
'task' => $task->jsonSerializeCc(),
]);
}

/**
* Parse text from file (if parsing the file type is supported)
*
* @param string $filePath
* @return DataResponse
*/
#[NoAdminRequired]
public function parseTextFromFile(string $filePath): DataResponse {
if ($this->userId === null) {
return new DataResponse('Unknow user', Http::STATUS_BAD_REQUEST);
}

try {
$task = $this->assistantService->runOrScheduleTextProcessingTask($type, $input, $appId, $this->userId, $identifier);
$text = $this->assistantService->parseTextFromFile($filePath, $this->userId);
} catch (\Exception | \Throwable $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
}
return new DataResponse([
'task' => $task->jsonSerialize(),
'parsedText' => $text,
]);
}
}
Loading
Loading