Skip to content

Commit

Permalink
Merge pull request #43 from nextcloud/enh/task-history
Browse files Browse the repository at this point in the history
Add task history
  • Loading branch information
julien-nc committed Mar 4, 2024
2 parents 931a33d + 8623028 commit 072ed92
Show file tree
Hide file tree
Showing 28 changed files with 1,018 additions and 224 deletions.
3 changes: 3 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

['name' => 'assistant#getAssistantTaskResultPage', 'url' => '/task/view/{metaTaskId}', 'verb' => 'GET'],
['name' => 'assistant#getAssistantTask', 'url' => '/task/{metaTaskId}', 'verb' => 'GET'],
['name' => 'assistant#getUserTasks', 'url' => '/tasks', '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#parseTextFromFile', 'url' => '/parse-file', 'verb' => 'POST'],
['name' => 'assistant#deleteTask', 'url' => '/task/{metaTaskId}', 'verb' => 'DELETE'],
['name' => 'assistant#cancelTask', 'url' => '/task/cancel/{metaTaskId}', 'verb' => 'PUT'],

['name' => 'Text2Image#processPrompt', 'url' => '/i/process_prompt', 'verb' => 'POST'],
['name' => 'Text2Image#getPromptHistory', 'url' => '/i/prompt_history', 'verb' => 'GET'],
Expand Down
14 changes: 14 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use OCP\SpeechToText\Events\TranscriptionSuccessfulEvent;
use OCP\TextProcessing\Events\TaskFailedEvent as TextTaskFailedEvent;
use OCP\TextProcessing\Events\TaskSuccessfulEvent as TextTaskSuccessfulEvent;
use OCP\TextProcessing\Task as OCPTextprocessingTask;
use OCP\TextToImage\Events\TaskFailedEvent as TextToImageTaskFailedEvent;
use OCP\TextToImage\Events\TaskSuccessfulEvent as TextToImageTaskSuccessfulEvent;

Expand All @@ -45,6 +46,19 @@ class Application extends App implements IBootstrap {
public const STT_TASK_SUCCESSFUL = 1;
public const STT_TASK_FAILED = -1;

public const STATUS_META_TASK_UNKNOWN = 0;
public const STATUS_META_TASK_SCHEDULED = 1;
public const STATUS_META_TASK_RUNNING = 2;
public const STATUS_META_TASK_SUCCESSFUL = 3;
public const STATUS_META_TASK_FAILED = 4;
public const TP_STATUS_TO_META_STATUS = [
OCPTextprocessingTask::STATUS_UNKNOWN => self::STATUS_META_TASK_UNKNOWN,
OCPTextprocessingTask::STATUS_SCHEDULED => self::STATUS_META_TASK_SCHEDULED,
OCPTextprocessingTask::STATUS_RUNNING => self::STATUS_META_TASK_RUNNING,
OCPTextprocessingTask::STATUS_SUCCESSFUL => self::STATUS_META_TASK_SUCCESSFUL,
OCPTextprocessingTask::STATUS_FAILED => self::STATUS_META_TASK_FAILED,
];

public const TASK_CATEGORY_TEXT_GEN = 0;
public const TASK_CATEGORY_TEXT_TO_IMAGE = 1;
public const TASK_CATEGORY_SPEECH_TO_TEXT = 2;
Expand Down
62 changes: 58 additions & 4 deletions lib/Controller/AssistantController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace OCA\TpAssistant\Controller;

use OCA\TpAssistant\AppInfo\Application;
use OCA\TpAssistant\Db\MetaTask;
use OCA\TpAssistant\Db\MetaTaskMapper;
use OCA\TpAssistant\Service\AssistantService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
Expand All @@ -11,20 +13,56 @@
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\DB\Exception;
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 MetaTaskMapper $metaTaskMapper,
private IInitialState $initialStateService,
private ?string $userId,
) {
parent::__construct($appName, $request);
}

/**
* @param int $metaTaskId
* @return DataResponse
*/
#[NoAdminRequired]
public function deleteTask(int $metaTaskId): DataResponse {
if ($this->userId !== null) {
try {
$this->assistantService->deleteAssistantTask($this->userId, $metaTaskId);
return new DataResponse('');
} catch (\Exception $e) {
}
}

return new DataResponse('', Http::STATUS_NOT_FOUND);
}

/**
* @param int $metaTaskId
* @return DataResponse
*/
#[NoAdminRequired]
public function cancelTask(int $metaTaskId): DataResponse {
if ($this->userId !== null) {
try {
$this->assistantService->cancelAssistantTask($this->userId, $metaTaskId);
return new DataResponse('');
} catch (\Exception $e) {
}
}

return new DataResponse('', Http::STATUS_NOT_FOUND);
}

/**
* @param int $metaTaskId
* @return TemplateResponse
Expand Down Expand Up @@ -59,6 +97,22 @@ public function getAssistantTask(int $metaTaskId): DataResponse {
return new DataResponse('', Http::STATUS_NOT_FOUND);
}

#[NoAdminRequired]
public function getUserTasks(?string $taskType = null, ?int $category = null): DataResponse {
if ($this->userId !== null) {
try {
$tasks = $this->metaTaskMapper->getUserMetaTasks($this->userId, $taskType, $category);
$serializedTasks = array_map(static function (MetaTask $task) {
return $task->jsonSerializeCc();
}, $tasks);
return new DataResponse(['tasks' => $serializedTasks]);
} catch (Exception $e) {
return new DataResponse(['tasks' => []]);
}
}
return new DataResponse('', Http::STATUS_NOT_FOUND);
}

/**
* @param array $inputs
* @param string $type
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/SpeechToTextController.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function getTranscript(int $id): DataResponse {
*/
private function internalGetTask(int $id): MetaTask {
try {
$metaTask = $this->metaTaskMapper->getMetaTaskOfUser($id, $this->userId);
$metaTask = $this->metaTaskMapper->getUserMetaTask($id, $this->userId);

if($metaTask->getCategory() !== Application::TASK_CATEGORY_SPEECH_TO_TEXT) {
throw new Exception('Task is not a speech to text task.', Http::STATUS_BAD_REQUEST);
Expand Down
4 changes: 3 additions & 1 deletion lib/Controller/Text2ImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ public function getImage(string $imageGenId, int $fileNameId): DataDisplayRespon
}
*/

return new DataDisplayResponse(
$response = new DataDisplayResponse(
$result['image'] ?? '',
Http::STATUS_OK,
['Content-Type' => $result['content-type'] ?? 'image/jpeg']
);
$response->cacheFor(60 * 60 * 24);
return $response;
}

/**
Expand Down
17 changes: 15 additions & 2 deletions lib/Db/MetaTaskMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function getMetaTasksByOcpTaskIdAndCategory(int $ocpTaskId, int $category
* @throws Exception
* @throws MultipleObjectsReturnedException
*/
public function getMetaTaskOfUser(int $id, string $userId): MetaTask {
public function getUserMetaTask(int $id, string $userId): MetaTask {
$qb = $this->db->getQueryBuilder();

$qb->select('*')
Expand All @@ -136,17 +136,30 @@ public function getMetaTaskOfUser(int $id, string $userId): MetaTask {

/**
* @param string $userId
* @param string|null $taskType
* @param int|null $category
* @return array
* @throws Exception
*/
public function getMetaTasksOfUser(string $userId): array {
public function getUserMetaTasks(string $userId, ?string $taskType = null, ?int $category = null): array {
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))
);
if ($taskType !== null) {
$qb->andWhere(
$qb->expr()->eq('task_type', $qb->createNamedParameter($taskType, IQueryBuilder::PARAM_STR))
);
}
if ($category !== null) {
$qb->andWhere(
$qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_INT))
);
}
$qb->orderBy('timestamp', 'DESC');

return $this->findEntities($qb);
}
Expand Down
19 changes: 4 additions & 15 deletions lib/Db/Text2Image/ImageGeneration.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
* @method \void setUserId(string $userId)
* @method \int getTimestamp()
* @method \void setTimestamp(int $timestamp)
* @method \boolean getIsGenerated()
* @method \void setIsGenerated(bool $isGenerated)
* @method \boolean getFailed()
* @method \void setFailed(bool $failed)
* @method \boolean getNotifyReady()
* @method \void setNotifyReady(bool $notifyReady)
* @method \int getExpGenTime()
Expand Down Expand Up @@ -67,19 +71,4 @@ public function jsonSerialize() {
'exp_gen_time' => $this->expGenTime,
];
}

public function setIsGenerated(?bool $isGenerated): void {
$this->isGenerated = $isGenerated === true;
}
public function getIsGenerated(): bool {
return $this->isGenerated === true;
}

public function setFailed(?bool $failed): void {
$this->failed = $failed === true;
}

public function getFailed(): bool {
return $this->failed === true;
}
}
43 changes: 33 additions & 10 deletions lib/Db/Text2Image/ImageGenerationMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,39 @@ public function createImageGeneration(
string $imageGenId, string $prompt = '', string $userId = '', ?int $expCompletionTime = null,
bool $notifyReady = false
): ImageGeneration {
$imageGeneration = new ImageGeneration();
$imageGeneration->setImageGenId($imageGenId);
$imageGeneration->setTimestamp((new DateTime())->getTimestamp());
$imageGeneration->setPrompt($prompt);
$imageGeneration->setUserId($userId);
$imageGeneration->setIsGenerated(false);
$imageGeneration->setFailed(false);
$imageGeneration->setNotifyReady($notifyReady);
$imageGeneration->setExpGenTime($expCompletionTime ?? (new DateTime())->getTimestamp());
return $this->insert($imageGeneration);
$nowTimestamp = (new DateTime())->getTimestamp();

$qb = $this->db->getQueryBuilder();
$qb->insert($this->getTableName())
->values([
'image_gen_id' => $qb->createNamedParameter($imageGenId, IQueryBuilder::PARAM_STR),
'timestamp' => $qb->createNamedParameter($nowTimestamp, IQueryBuilder::PARAM_INT),
'prompt' => $qb->createNamedParameter($prompt, IQueryBuilder::PARAM_STR),
'user_id' => $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR),
'is_generated' => $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL),
'failed' => $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL),
'notify_ready' => $qb->createNamedParameter($notifyReady, IQueryBuilder::PARAM_BOOL),
'exp_gen_time' => $qb->createNamedParameter($expCompletionTime ?? $nowTimestamp, IQueryBuilder::PARAM_INT),
]);
$qb->executeStatement();
$qb->resetQueryParts();

return $this->getImageGenerationOfImageGenId($imageGenId);

// TODO figure out why inserting an entity does not work on PostgreSQL and produces:
// An exception occurred while executing a query: SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for type boolean: \"\""
// could there be a bug in the query generation?
//$imageGeneration = new ImageGeneration();
//$imageGeneration->setImageGenId($imageGenId);
//$imageGeneration->setTimestamp((new DateTime())->getTimestamp());
//$imageGeneration->setPrompt($prompt);
//$imageGeneration->setUserId($userId);
//$imageGeneration->setIsGenerated(false);
//$imageGeneration->setFailed(false);
//$imageGeneration->setNotifyReady($notifyReady);
//$imageGeneration->setExpGenTime($expCompletionTime ?? (new DateTime())->getTimestamp());
//return $this->insert($imageGeneration);
}

/**
Expand Down
16 changes: 9 additions & 7 deletions lib/Listener/SpeechToText/SpeechToTextResultListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

use OCA\TpAssistant\AppInfo\Application;
use OCA\TpAssistant\Db\MetaTaskMapper;
use OCA\TpAssistant\Service\AssistantService;
use OCA\TpAssistant\Service\NotificationService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\SpeechToText\Events\AbstractTranscriptionEvent;
Expand All @@ -39,7 +39,7 @@ class SpeechToTextResultListener implements IEventListener {
public function __construct(
private LoggerInterface $logger,
private MetaTaskMapper $metaTaskMapper,
private AssistantService $assistantService,
private NotificationService $notificationService,
) {
}

Expand Down Expand Up @@ -72,17 +72,18 @@ public function handle(Event $event): void {

// Update the meta task with the output and new status
$assistantTask->setOutput($transcript);
$assistantTask->setStatus(Application::STT_TASK_SUCCESSFUL);
$assistantTask->setStatus(Application::STATUS_META_TASK_SUCCESSFUL);
$assistantTask = $this->metaTaskMapper->update($assistantTask);

try {
$this->assistantService->sendNotification($assistantTask, null, null, $transcript);
$this->notificationService->sendNotification($assistantTask, null, null, $transcript);
} catch (\InvalidArgumentException $e) {
$this->logger->error('Failed to dispatch notification for successful transcription: ' . $e->getMessage());
}
}

if ($event instanceof TranscriptionFailedEvent) {
$file = $event->getFile();
$this->logger->error('Transcript generation failed: ' . $event->getErrorMessage());

$metaTasks = $this->metaTaskMapper->getMetaTasksByOcpTaskIdAndCategory($file->getId(), Application::TASK_CATEGORY_SPEECH_TO_TEXT);
Expand All @@ -99,16 +100,17 @@ public function handle(Event $event): void {
}

if ($assistantTask === null) {
$this->logger->error('No assistant task found for speech to text result');
$this->logger->error('No assistant task found for speech to text result (task id: ' . $file->getId() . ')');
return;
}

// Update the meta task with the new status
$assistantTask->setStatus(Application::STT_TASK_FAILED);
$assistantTask->setStatus(Application::STATUS_META_TASK_FAILED);
$assistantTask->setOutput($event->getErrorMessage());
$assistantTask = $this->metaTaskMapper->update($assistantTask);

try {
$this->assistantService->sendNotification($assistantTask);
$this->notificationService->sendNotification($assistantTask);
} catch (\InvalidArgumentException $e) {
$this->logger->error('Failed to dispatch notification for failed transcription: ' . $e->getMessage());
}
Expand Down
10 changes: 5 additions & 5 deletions lib/Listener/TaskFailedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use OCA\TpAssistant\AppInfo\Application;
use OCA\TpAssistant\Db\MetaTaskMapper;
use OCA\TpAssistant\Event\BeforeAssistantNotificationEvent;
use OCA\TpAssistant\Service\AssistantService;
use OCA\TpAssistant\Service\NotificationService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
Expand All @@ -18,7 +18,7 @@
class TaskFailedListener implements IEventListener {

public function __construct(
private AssistantService $assistantService,
private NotificationService $notificationService,
private IEventDispatcher $eventDispatcher,
private MetaTaskMapper $metaTaskMapper,
) {
Expand Down Expand Up @@ -58,10 +58,10 @@ public function handle(Event $event): void {
}

// Update task status and output:
$assistantTask->setStatus($task->getStatus());
$assistantTask->setOutput($task->getOutput());
$assistantTask->setStatus(Application::TP_STATUS_TO_META_STATUS[$task->getStatus()]);
$assistantTask->setOutput($event->getErrorMessage());
$assistantTask = $this->metaTaskMapper->update($assistantTask);

$this->assistantService->sendNotification($assistantTask, $notificationTarget, $notificationActionLabel);
$this->notificationService->sendNotification($assistantTask, $notificationTarget, $notificationActionLabel);
}
}
Loading

0 comments on commit 072ed92

Please sign in to comment.