Skip to content

Commit 7c80d66

Browse files
authored
Merge pull request #38854 from nextcloud/enh/llm-api
2 parents e9b8a34 + 6d568b0 commit 7c80d66

30 files changed

Lines changed: 1989 additions & 1 deletion
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net>
7+
*
8+
* @author Marcel Klehr <mklehr@gmx.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\Controller;
28+
29+
use InvalidArgumentException;
30+
use OCP\AppFramework\Http;
31+
use OCP\AppFramework\Http\DataResponse;
32+
use OCP\Common\Exception\NotFoundException;
33+
use OCP\IL10N;
34+
use OCP\IRequest;
35+
use OCP\TextProcessing\ITaskType;
36+
use OCP\TextProcessing\Task;
37+
use OCP\TextProcessing\IManager;
38+
use OCP\PreConditionNotMetException;
39+
use Psr\Container\ContainerExceptionInterface;
40+
use Psr\Container\ContainerInterface;
41+
use Psr\Container\NotFoundExceptionInterface;
42+
use Psr\Log\LoggerInterface;
43+
44+
class TextProcessingApiController extends \OCP\AppFramework\OCSController {
45+
public function __construct(
46+
string $appName,
47+
IRequest $request,
48+
private IManager $languageModelManager,
49+
private IL10N $l,
50+
private ?string $userId,
51+
private ContainerInterface $container,
52+
private LoggerInterface $logger,
53+
) {
54+
parent::__construct($appName, $request);
55+
}
56+
57+
/**
58+
* This endpoint returns all available LanguageModel task types
59+
*
60+
* @PublicPage
61+
*/
62+
public function taskTypes(): DataResponse {
63+
$typeClasses = $this->languageModelManager->getAvailableTaskTypes();
64+
$types = [];
65+
foreach ($typeClasses as $typeClass) {
66+
try {
67+
/** @var ITaskType $object */
68+
$object = $this->container->get($typeClass);
69+
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
70+
$this->logger->warning('Could not find ' . $typeClass, ['exception' => $e]);
71+
continue;
72+
}
73+
$types[] = [
74+
'id' => $typeClass,
75+
'name' => $object->getName(),
76+
'description' => $object->getDescription(),
77+
];
78+
}
79+
80+
return new DataResponse([
81+
'types' => $types,
82+
]);
83+
}
84+
85+
/**
86+
* This endpoint allows scheduling a language model task
87+
*
88+
* @PublicPage
89+
* @UserRateThrottle(limit=20, period=120)
90+
* @AnonRateThrottle(limit=5, period=120)
91+
*/
92+
public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse {
93+
try {
94+
$task = new Task($type, $input, $appId, $this->userId, $identifier);
95+
} catch (InvalidArgumentException) {
96+
return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST);
97+
}
98+
try {
99+
$this->languageModelManager->scheduleTask($task);
100+
101+
$json = $task->jsonSerialize();
102+
103+
return new DataResponse([
104+
'task' => $json,
105+
]);
106+
} catch (PreConditionNotMetException) {
107+
return new DataResponse(['message' => $this->l->t('Necessary language model provider is not available')], Http::STATUS_PRECONDITION_FAILED);
108+
}
109+
}
110+
111+
/**
112+
* This endpoint allows checking the status and results of a task.
113+
* Tasks are removed 1 week after receiving their last update.
114+
*
115+
* @PublicPage
116+
* @param int $id The id of the task
117+
*/
118+
public function getTask(int $id): DataResponse {
119+
try {
120+
$task = $this->languageModelManager->getTask($id);
121+
122+
if ($this->userId !== $task->getUserId()) {
123+
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND);
124+
}
125+
126+
$json = $task->jsonSerialize();
127+
128+
return new DataResponse([
129+
'task' => $json,
130+
]);
131+
} catch (NotFoundException $e) {
132+
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND);
133+
} catch (\RuntimeException $e) {
134+
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR);
135+
}
136+
}
137+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net>
7+
*
8+
* @author Marcel Klehr <mklehr@gmx.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\DB\Types;
32+
use OCP\Migration\IOutput;
33+
use OCP\Migration\SimpleMigrationStep;
34+
35+
/**
36+
* Introduce llm_tasks table
37+
*/
38+
class Version28000Date20230616104802 extends SimpleMigrationStep {
39+
/**
40+
* @param IOutput $output
41+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
42+
* @param array $options
43+
* @return null|ISchemaWrapper
44+
*/
45+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
46+
/** @var ISchemaWrapper $schema */
47+
$schema = $schemaClosure();
48+
49+
if (!$schema->hasTable('llm_tasks')) {
50+
$table = $schema->createTable('llm_tasks');
51+
52+
$table->addColumn('id', Types::BIGINT, [
53+
'notnull' => true,
54+
'length' => 64,
55+
'autoincrement' => true,
56+
]);
57+
$table->addColumn('type', Types::STRING, [
58+
'notnull' => true,
59+
'length' => 255,
60+
]);
61+
$table->addColumn('input', Types::TEXT, [
62+
'notnull' => true,
63+
]);
64+
$table->addColumn('output', Types::TEXT, [
65+
'notnull' => false,
66+
]);
67+
$table->addColumn('status', Types::INTEGER, [
68+
'notnull' => false,
69+
'length' => 6,
70+
'default' => 0,
71+
]);
72+
$table->addColumn('user_id', Types::STRING, [
73+
'notnull' => true,
74+
'length' => 64,
75+
]);
76+
$table->addColumn('app_id', Types::STRING, [
77+
'notnull' => true,
78+
'length' => 32,
79+
'default' => '',
80+
]);
81+
$table->addColumn('identifier', Types::STRING, [
82+
'notnull' => true,
83+
'length' => 255,
84+
'default' => '',
85+
]);
86+
$table->addColumn('last_updated', 'integer', [
87+
'notnull' => false,
88+
'length' => 4,
89+
'default' => 0,
90+
'unsigned' => true,
91+
]);
92+
93+
$table->setPrimaryKey(['id'], 'llm_tasks_id_index');
94+
$table->addUniqueIndex(['status', 'type'], 'llm_tasks_status_type');
95+
$table->addIndex(['last_updated'], 'llm_tasks_updated');
96+
97+
return $schema;
98+
}
99+
100+
return null;
101+
}
102+
}

core/routes.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@
145145

146146
['root' => '/translation', 'name' => 'TranslationApi#languages', 'url' => '/languages', 'verb' => 'GET'],
147147
['root' => '/translation', 'name' => 'TranslationApi#translate', 'url' => '/translate', 'verb' => 'POST'],
148+
149+
['root' => '/textprocessing', 'name' => 'TextProcessingApi#taskTypes', 'url' => '/tasktypes', 'verb' => 'GET'],
150+
['root' => '/textprocessing', 'name' => 'TextProcessingApi#schedule', 'url' => '/schedule', 'verb' => 'POST'],
151+
['root' => '/textprocessing', 'name' => 'TextProcessingApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'],
148152
],
149153
]);
150154

lib/composer/composer/autoload_classmap.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
'OCP\\Comments\\IllegalIDChangeException' => $baseDir . '/lib/public/Comments/IllegalIDChangeException.php',
198198
'OCP\\Comments\\MessageTooLongException' => $baseDir . '/lib/public/Comments/MessageTooLongException.php',
199199
'OCP\\Comments\\NotFoundException' => $baseDir . '/lib/public/Comments/NotFoundException.php',
200+
'OCP\\Common\\Exception\\NotFoundException' => $baseDir . '/lib/public/Common/Exception/NotFoundException.php',
200201
'OCP\\Config\\BeforePreferenceDeletedEvent' => $baseDir . '/lib/public/Config/BeforePreferenceDeletedEvent.php',
201202
'OCP\\Config\\BeforePreferenceSetEvent' => $baseDir . '/lib/public/Config/BeforePreferenceSetEvent.php',
202203
'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php',
@@ -626,6 +627,17 @@
626627
'OCP\\Talk\\IConversationOptions' => $baseDir . '/lib/public/Talk/IConversationOptions.php',
627628
'OCP\\Talk\\ITalkBackend' => $baseDir . '/lib/public/Talk/ITalkBackend.php',
628629
'OCP\\Template' => $baseDir . '/lib/public/Template.php',
630+
'OCP\\TextProcessing\\Events\\AbstractTextProcessingEvent' => $baseDir . '/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php',
631+
'OCP\\TextProcessing\\Events\\TaskFailedEvent' => $baseDir . '/lib/public/TextProcessing/Events/TaskFailedEvent.php',
632+
'OCP\\TextProcessing\\Events\\TaskSuccessfulEvent' => $baseDir . '/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php',
633+
'OCP\\TextProcessing\\FreePromptTaskType' => $baseDir . '/lib/public/TextProcessing/FreePromptTaskType.php',
634+
'OCP\\TextProcessing\\HeadlineTaskType' => $baseDir . '/lib/public/TextProcessing/HeadlineTaskType.php',
635+
'OCP\\TextProcessing\\IManager' => $baseDir . '/lib/public/TextProcessing/IManager.php',
636+
'OCP\\TextProcessing\\IProvider' => $baseDir . '/lib/public/TextProcessing/IProvider.php',
637+
'OCP\\TextProcessing\\ITaskType' => $baseDir . '/lib/public/TextProcessing/ITaskType.php',
638+
'OCP\\TextProcessing\\SummaryTaskType' => $baseDir . '/lib/public/TextProcessing/SummaryTaskType.php',
639+
'OCP\\TextProcessing\\Task' => $baseDir . '/lib/public/TextProcessing/Task.php',
640+
'OCP\\TextProcessing\\TopicsTaskType' => $baseDir . '/lib/public/TextProcessing/TopicsTaskType.php',
629641
'OCP\\Translation\\CouldNotTranslateException' => $baseDir . '/lib/public/Translation/CouldNotTranslateException.php',
630642
'OCP\\Translation\\IDetectLanguageProvider' => $baseDir . '/lib/public/Translation/IDetectLanguageProvider.php',
631643
'OCP\\Translation\\ITranslationManager' => $baseDir . '/lib/public/Translation/ITranslationManager.php',
@@ -1049,6 +1061,7 @@
10491061
'OC\\Core\\Controller\\ReferenceController' => $baseDir . '/core/Controller/ReferenceController.php',
10501062
'OC\\Core\\Controller\\SearchController' => $baseDir . '/core/Controller/SearchController.php',
10511063
'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php',
1064+
'OC\\Core\\Controller\\TextProcessingApiController' => $baseDir . '/core/Controller/TextProcessingApiController.php',
10521065
'OC\\Core\\Controller\\TranslationApiController' => $baseDir . '/core/Controller/TranslationApiController.php',
10531066
'OC\\Core\\Controller\\TwoFactorChallengeController' => $baseDir . '/core/Controller/TwoFactorChallengeController.php',
10541067
'OC\\Core\\Controller\\UnifiedSearchController' => $baseDir . '/core/Controller/UnifiedSearchController.php',
@@ -1127,6 +1140,7 @@
11271140
'OC\\Core\\Migrations\\Version27000Date20220613163520' => $baseDir . '/core/Migrations/Version27000Date20220613163520.php',
11281141
'OC\\Core\\Migrations\\Version27000Date20230309104325' => $baseDir . '/core/Migrations/Version27000Date20230309104325.php',
11291142
'OC\\Core\\Migrations\\Version27000Date20230309104802' => $baseDir . '/core/Migrations/Version27000Date20230309104802.php',
1143+
'OC\\Core\\Migrations\\Version28000Date20230616104802' => $baseDir . '/core/Migrations/Version28000Date20230616104802.php',
11301144
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
11311145
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
11321146
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
@@ -1500,6 +1514,7 @@
15001514
'OC\\RepairException' => $baseDir . '/lib/private/RepairException.php',
15011515
'OC\\Repair\\AddBruteForceCleanupJob' => $baseDir . '/lib/private/Repair/AddBruteForceCleanupJob.php',
15021516
'OC\\Repair\\AddCleanupUpdaterBackupsJob' => $baseDir . '/lib/private/Repair/AddCleanupUpdaterBackupsJob.php',
1517+
'OC\\Repair\\AddRemoveOldTasksBackgroundJob' => $baseDir . '/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php',
15031518
'OC\\Repair\\CleanTags' => $baseDir . '/lib/private/Repair/CleanTags.php',
15041519
'OC\\Repair\\CleanUpAbandonedApps' => $baseDir . '/lib/private/Repair/CleanUpAbandonedApps.php',
15051520
'OC\\Repair\\ClearFrontendCaches' => $baseDir . '/lib/private/Repair/ClearFrontendCaches.php',
@@ -1651,6 +1666,11 @@
16511666
'OC\\Template\\ResourceLocator' => $baseDir . '/lib/private/Template/ResourceLocator.php',
16521667
'OC\\Template\\ResourceNotFoundException' => $baseDir . '/lib/private/Template/ResourceNotFoundException.php',
16531668
'OC\\Template\\TemplateFileLocator' => $baseDir . '/lib/private/Template/TemplateFileLocator.php',
1669+
'OC\\TextProcessing\\Db\\Task' => $baseDir . '/lib/private/TextProcessing/Db/Task.php',
1670+
'OC\\TextProcessing\\Db\\TaskMapper' => $baseDir . '/lib/private/TextProcessing/Db/TaskMapper.php',
1671+
'OC\\TextProcessing\\Manager' => $baseDir . '/lib/private/TextProcessing/Manager.php',
1672+
'OC\\TextProcessing\\RemoveOldTasksBackgroundJob' => $baseDir . '/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php',
1673+
'OC\\TextProcessing\\TaskBackgroundJob' => $baseDir . '/lib/private/TextProcessing/TaskBackgroundJob.php',
16541674
'OC\\Translation\\TranslationManager' => $baseDir . '/lib/private/Translation/TranslationManager.php',
16551675
'OC\\URLGenerator' => $baseDir . '/lib/private/URLGenerator.php',
16561676
'OC\\Updater' => $baseDir . '/lib/private/Updater.php',

0 commit comments

Comments
 (0)