Skip to content

Commit 41b19cf

Browse files
committed
allow anon text processing scheduling
add a textprocessing_tasks index convert anotations to method attributes refactor TP manager add mapper methods Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
1 parent 9986e02 commit 41b19cf

File tree

9 files changed

+159
-38
lines changed

9 files changed

+159
-38
lines changed

core/Controller/TextProcessingApiController.php

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
use InvalidArgumentException;
3030
use OCA\Core\ResponseDefinitions;
3131
use OCP\AppFramework\Http;
32+
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
33+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
34+
use OCP\AppFramework\Http\Attribute\PublicPage;
35+
use OCP\AppFramework\Http\Attribute\UserRateLimit;
3236
use OCP\AppFramework\Http\DataResponse;
3337
use OCP\Common\Exception\NotFoundException;
3438
use OCP\IL10N;
@@ -47,26 +51,25 @@
4751
*/
4852
class TextProcessingApiController extends \OCP\AppFramework\OCSController {
4953
public function __construct(
50-
string $appName,
51-
IRequest $request,
52-
private IManager $languageModelManager,
53-
private IL10N $l,
54-
private ?string $userId,
54+
string $appName,
55+
IRequest $request,
56+
private IManager $textProcessingManager,
57+
private IL10N $l,
58+
private ?string $userId,
5559
private ContainerInterface $container,
56-
private LoggerInterface $logger,
60+
private LoggerInterface $logger,
5761
) {
5862
parent::__construct($appName, $request);
5963
}
6064

6165
/**
6266
* This endpoint returns all available LanguageModel task types
6367
*
64-
* @PublicPage
65-
*
6668
* @return DataResponse<Http::STATUS_OK, array{types: array{id: string, name: string, description: string}[]}, array{}>
6769
*/
70+
#[PublicPage]
6871
public function taskTypes(): DataResponse {
69-
$typeClasses = $this->languageModelManager->getAvailableTaskTypes();
72+
$typeClasses = $this->textProcessingManager->getAvailableTaskTypes();
7073
$types = [];
7174
/** @var string $typeClass */
7275
foreach ($typeClasses as $typeClass) {
@@ -92,10 +95,6 @@ public function taskTypes(): DataResponse {
9295
/**
9396
* This endpoint allows scheduling a language model task
9497
*
95-
* @PublicPage
96-
* @UserRateThrottle(limit=20, period=120)
97-
* @AnonRateThrottle(limit=5, period=120)
98-
*
9998
* @param string $input Input text
10099
* @param string $type Type of the task
101100
* @param string $appId ID of the app that will execute the task
@@ -107,14 +106,17 @@ public function taskTypes(): DataResponse {
107106
* 400: Scheduling task is not possible
108107
* 412: Scheduling task is not possible
109108
*/
109+
#[PublicPage]
110+
#[UserRateLimit(limit: 20, period: 120)]
111+
#[AnonRateLimit(limit: 5, period: 120)]
110112
public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse {
111113
try {
112114
$task = new Task($type, $input, $appId, $this->userId, $identifier);
113115
} catch (InvalidArgumentException) {
114116
return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST);
115117
}
116118
try {
117-
$this->languageModelManager->scheduleTask($task);
119+
$this->textProcessingManager->scheduleTask($task);
118120

119121
$json = $task->jsonSerialize();
120122

@@ -130,21 +132,17 @@ public function schedule(string $input, string $type, string $appId, string $ide
130132
* This endpoint allows checking the status and results of a task.
131133
* Tasks are removed 1 week after receiving their last update.
132134
*
133-
* @PublicPage
134135
* @param int $id The id of the task
135136
*
136137
* @return DataResponse<Http::STATUS_OK, array{task: CoreTextProcessingTask}, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
137138
*
138139
* 200: Task returned
139140
* 404: Task not found
140141
*/
142+
#[PublicPage]
141143
public function getTask(int $id): DataResponse {
142144
try {
143-
$task = $this->languageModelManager->getTask($id);
144-
145-
if ($this->userId !== $task->getUserId()) {
146-
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND);
147-
}
145+
$task = $this->textProcessingManager->getUserTask($id, $this->userId);
148146

149147
$json = $task->jsonSerialize();
150148

@@ -159,25 +157,19 @@ public function getTask(int $id): DataResponse {
159157
}
160158

161159
/**
162-
* This endpoint returns a list of tasks related with a specific appId and identifier
163-
*
164-
* @PublicPage
165-
* @UserRateThrottle(limit=20, period=120)
160+
* This endpoint returns a list of tasks of a user that are related
161+
* with a specific appId and optionally with an identifier
166162
*
167163
* @param string $appId
168164
* @param string|null $identifier
169165
* @return DataResponse<Http::STATUS_OK, array{tasks: array{CoreTextProcessingTask}}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
170166
*
171167
* 200: Task list returned
172168
*/
169+
#[NoAdminRequired]
173170
public function listTasksByApp(string $appId, ?string $identifier = null): DataResponse {
174-
if ($this->userId === null) {
175-
return new DataResponse([
176-
'tasks' => [],
177-
]);
178-
}
179171
try {
180-
$tasks = $this->languageModelManager->getTasksByApp($this->userId, $appId, $identifier);
172+
$tasks = $this->textProcessingManager->getUserTasksByApp($this->userId, $appId, $identifier);
181173
$json = array_map(static function (Task $task) {
182174
return $task->jsonSerialize();
183175
}, $tasks);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023 Julien Veyssier <julien-nc@posteo.net>
7+
*
8+
* @author Julien Veyssier <julien-nc@posteo.net>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OC\Core\Migrations;
28+
29+
use Closure;
30+
use OCP\DB\ISchemaWrapper;
31+
use OCP\Migration\IOutput;
32+
use OCP\Migration\SimpleMigrationStep;
33+
34+
/**
35+
* Adjust textprocessing_tasks table
36+
*/
37+
class Version28000Date20230803221055 extends SimpleMigrationStep {
38+
/**
39+
* @param IOutput $output
40+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
41+
* @param array $options
42+
* @return null|ISchemaWrapper
43+
*/
44+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
45+
/** @var ISchemaWrapper $schema */
46+
$schema = $schemaClosure();
47+
$changed = false;
48+
49+
if ($schema->hasTable('textprocessing_tasks')) {
50+
$table = $schema->getTable('textprocessing_tasks');
51+
52+
$column = $table->getColumn('user_id');
53+
$column->setNotnull(false);
54+
55+
$table->addIndex(['user_id', 'app_id', 'identifier'], 'tp_tasks_uid_appid_ident');
56+
57+
$changed = true;
58+
}
59+
60+
if ($changed) {
61+
return $schema;
62+
}
63+
64+
return null;
65+
}
66+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,7 @@
11531153
'OC\\Core\\Migrations\\Version27000Date20230309104802' => $baseDir . '/core/Migrations/Version27000Date20230309104802.php',
11541154
'OC\\Core\\Migrations\\Version28000Date20230616104802' => $baseDir . '/core/Migrations/Version28000Date20230616104802.php',
11551155
'OC\\Core\\Migrations\\Version28000Date20230728104802' => $baseDir . '/core/Migrations/Version28000Date20230728104802.php',
1156+
'OC\\Core\\Migrations\\Version28000Date20230803221055' => $baseDir . '/core/Migrations/Version28000Date20230803221055.php',
11561157
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
11571158
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
11581159
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
11861186
'OC\\Core\\Migrations\\Version27000Date20230309104802' => __DIR__ . '/../../..' . '/core/Migrations/Version27000Date20230309104802.php',
11871187
'OC\\Core\\Migrations\\Version28000Date20230616104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230616104802.php',
11881188
'OC\\Core\\Migrations\\Version28000Date20230728104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230728104802.php',
1189+
'OC\\Core\\Migrations\\Version28000Date20230803221055' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230803221055.php',
11891190
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
11901191
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
11911192
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',

lib/private/TextProcessing/Db/Task.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@
3939
* @method string getOutput()
4040
* @method setStatus(int $type)
4141
* @method int getStatus()
42-
* @method setUserId(string $type)
43-
* @method string getuserId()
42+
* @method setUserId(?string $userId)
43+
* @method string|null getUserId()
4444
* @method setAppId(string $type)
4545
* @method string getAppId()
46-
* @method setIdentifier(string $type)
46+
* @method setIdentifier(string $identifier)
4747
* @method string getIdentifier()
4848
*/
4949
class Task extends Entity {

lib/private/TextProcessing/Db/TaskMapper.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,35 @@ public function find(int $id): Task {
5959
return $this->findEntity($qb);
6060
}
6161

62+
/**
63+
* @param int $id
64+
* @param string|null $userId
65+
* @return Task
66+
* @throws DoesNotExistException
67+
* @throws Exception
68+
* @throws MultipleObjectsReturnedException
69+
*/
70+
public function findByIdAndUser(int $id, ?string $userId): Task {
71+
$qb = $this->db->getQueryBuilder();
72+
$qb->select(Task::$columns)
73+
->from($this->tableName)
74+
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
75+
if ($userId === null) {
76+
$qb->andWhere($qb->expr()->isNull('user_id'));
77+
} else {
78+
$qb->andWhere($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)));
79+
}
80+
return $this->findEntity($qb);
81+
}
82+
6283
/**
6384
* @param string $userId
6485
* @param string $appId
6586
* @param string|null $identifier
6687
* @return array
6788
* @throws Exception
6889
*/
69-
public function findByApp(string $userId, string $appId, ?string $identifier = null): array {
90+
public function findUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array {
7091
$qb = $this->db->getQueryBuilder();
7192
$qb->select(Task::$columns)
7293
->from($this->tableName)

lib/private/TextProcessing/Manager.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ public function scheduleTask(OCPTask $task): void {
178178
}
179179

180180
/**
181+
* Get a task from its id
182+
*
181183
* @param int $id The id of the task
182184
* @return OCPTask
183185
* @throws RuntimeException If the query failed
@@ -197,14 +199,41 @@ public function getTask(int $id): OCPTask {
197199
}
198200

199201
/**
202+
* Get a task from its user id and task id
203+
* If userId is null, this can only get a task that was scheduled anonymously
204+
*
205+
* @param int $id The id of the task
206+
* @param string|null $userId The user id that scheduled the task
207+
* @return OCPTask
208+
* @throws RuntimeException If the query failed
209+
* @throws NotFoundException If the task could not be found
210+
*/
211+
public function getUserTask(int $id, ?string $userId): OCPTask {
212+
try {
213+
$taskEntity = $this->taskMapper->findByIdAndUser($id, $userId);
214+
return $taskEntity->toPublicTask();
215+
} catch (DoesNotExistException $e) {
216+
throw new NotFoundException('Could not find task with the provided id and user id');
217+
} catch (MultipleObjectsReturnedException $e) {
218+
throw new RuntimeException('Could not uniquely identify task with given id and user id', 0, $e);
219+
} catch (Exception $e) {
220+
throw new RuntimeException('Failure while trying to find task by id and user id: ' . $e->getMessage(), 0, $e);
221+
}
222+
}
223+
224+
/**
225+
* Get a list of tasks scheduled by a specific user for a specific app
226+
* and optionally with a specific identifier.
227+
* This cannot be used to get anonymously scheduled tasks
228+
*
200229
* @param string $userId
201230
* @param string $appId
202231
* @param string|null $identifier
203232
* @return array
204233
*/
205-
public function getTasksByApp(string $userId, string $appId, ?string $identifier = null): array {
234+
public function getUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array {
206235
try {
207-
$taskEntities = $this->taskMapper->findByApp($userId, $appId, $identifier);
236+
$taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $identifier);
208237
return array_map(static function (DbTask $taskEntity) {
209238
return $taskEntity->toPublicTask();
210239
}, $taskEntities);

lib/public/TextProcessing/FreePromptTaskType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ public function getName(): string {
6161
* @since 27.1.0
6262
*/
6363
public function getDescription(): string {
64-
return $this->l->t('Runs an arbitrary prompt through the built-in language model.');
64+
return $this->l->t('Runs an arbitrary prompt through the language model.');
6565
}
6666
}

lib/public/TextProcessing/IManager.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,22 @@ public function scheduleTask(Task $task) : void;
8181
*/
8282
public function getTask(int $id): Task;
8383

84+
/**
85+
* @param int $id The id of the task
86+
* @param string|null $userId The user id that scheduled the task
87+
* @return Task
88+
* @throws RuntimeException If the query failed
89+
* @throws NotFoundException If the task could not be found
90+
* @since 27.1.0
91+
*/
92+
public function getUserTask(int $id, ?string $userId): Task;
93+
8494
/**
8595
* @param string $userId
8696
* @param string $appId
8797
* @param string|null $identifier
8898
* @return array
99+
* @since 27.1.0
89100
*/
90-
public function getTasksByApp(string $userId, string $appId, ?string $identifier = null): array;
101+
public function getUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array;
91102
}

0 commit comments

Comments
 (0)