From 2e9dea20613fbc2cb2227cda19ade2daee7e6664 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 15 Jun 2023 10:30:04 +0200 Subject: [PATCH 001/100] Initial work on lm OCP API Signed-off-by: Marcel Klehr (cherry picked from commit 70d5bf79e315a1bc8e6380ff4c5a14842362c9c7) --- .../Events/AbstractLanguageModelEvent.php | 66 ++++++++++++++ .../Events/PromptFailedEvent.php | 24 +++++ .../Events/PromptSuccessfulEvent.php | 23 +++++ .../Events/SummaryFailedEvent.php | 24 +++++ .../Events/SummarySuccessfulEvent.php | 23 +++++ .../LanguageModel/ILanguageModelManager.php | 89 +++++++++++++++++++ .../LanguageModel/ILanguageModelProvider.php | 47 ++++++++++ lib/public/LanguageModel/ISummaryProvider.php | 43 +++++++++ 8 files changed, 339 insertions(+) create mode 100644 lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php create mode 100644 lib/public/LanguageModel/Events/PromptFailedEvent.php create mode 100644 lib/public/LanguageModel/Events/PromptSuccessfulEvent.php create mode 100644 lib/public/LanguageModel/Events/SummaryFailedEvent.php create mode 100644 lib/public/LanguageModel/Events/SummarySuccessfulEvent.php create mode 100644 lib/public/LanguageModel/ILanguageModelManager.php create mode 100644 lib/public/LanguageModel/ILanguageModelProvider.php create mode 100644 lib/public/LanguageModel/ISummaryProvider.php diff --git a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php new file mode 100644 index 0000000000000..593385b02f0ed --- /dev/null +++ b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php @@ -0,0 +1,66 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCP\LanguageModel\Events; + +use OCP\EventDispatcher\Event; + +/** + * @since 28.0.0 + */ +abstract class AbstractLanguageModelEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct( + private int $requestId, + private ?string $userId, + private string $appId, + ) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getRequestId(): int { + return $this->requestId; + } + + + /** + * @since 28.0.0 + */ + public function getUserId(): ?string { + return $this->userId; + } + + /** + * @since 28.0.0 + */ + public function getAppId(): string { + return $this->appId; + } +} diff --git a/lib/public/LanguageModel/Events/PromptFailedEvent.php b/lib/public/LanguageModel/Events/PromptFailedEvent.php new file mode 100644 index 0000000000000..2ad8848d0845a --- /dev/null +++ b/lib/public/LanguageModel/Events/PromptFailedEvent.php @@ -0,0 +1,24 @@ +errorMessage; + } +} diff --git a/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php b/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php new file mode 100644 index 0000000000000..f9e3a6123183a --- /dev/null +++ b/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php @@ -0,0 +1,23 @@ +output; + } +} diff --git a/lib/public/LanguageModel/Events/SummaryFailedEvent.php b/lib/public/LanguageModel/Events/SummaryFailedEvent.php new file mode 100644 index 0000000000000..49070656ec4a6 --- /dev/null +++ b/lib/public/LanguageModel/Events/SummaryFailedEvent.php @@ -0,0 +1,24 @@ +errorMessage; + } +} diff --git a/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php b/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php new file mode 100644 index 0000000000000..353394438f0a3 --- /dev/null +++ b/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php @@ -0,0 +1,23 @@ +output; + } +} diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php new file mode 100644 index 0000000000000..6805309995502 --- /dev/null +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -0,0 +1,89 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\LanguageModel; + +use InvalidArgumentException; +use OCP\PreConditionNotMetException; +use RuntimeException; + +interface ILanguageModelManager { + /** + * @since 28.0.0 + */ + public function hasProviders(): bool; + + /** + * @since 28.0.0 + */ + public function hasSummaryProviders(): bool; + + /** + * @param string $prompt The prompt to call the Language model with + * @returns string The output + * @throws PreConditionNotMetException If no provider was registered but this method was still called + * @throws InvalidArgumentException If the file could not be found or is not of a supported type + * @throws RuntimeException If the transcription failed for other reasons + * @since 28.0.0 + */ + public function prompt(string $prompt): string; + + /** + * Will schedule an LLM inference process in the background. The result will become available + * with the \OCP\LanguageModel\Events\PromptFinishedEvent + * + * @param string $prompt The prompt to call the Language model with + * @param ?string $userId The user that triggered this request (only for convenience, will be available on the TranscriptEvents) + * @param string $appId The app that triggered this request (only for convenience, will be available on the TranscriptEvents) + * @returns int The id of the prompt request + * @throws PreConditionNotMetException If no provider was registered but this method was still called + * @since 28.0.0 + */ + public function schedulePrompt(string $prompt, ?string $userId, string $appId): int; + + /** + * Will schedule an LLM inference process in the background. The result will become available + * with the \OCP\LanguageModel\Events\PromptFinishedEvent + * + * @param string $text The text to summarize + * @param ?string $userId The user that triggered this request (only for convenience, will be available on the TranscriptEvents) + * @param string $appId The app that triggered this request (only for convenience, will be available on the TranscriptEvents) + * @returns int The id of the prompt request + * @throws PreConditionNotMetException If no summary provider was registered but this method was still called + * @since 28.0.0 + */ + public function scheduleSummary(string $text, ?string $userId, string $appId): int; + + /** + * @param string $text The text to summarize + * @returns string The output + * @throws PreConditionNotMetException If no summary provider was registered but this method was still called + * @throws InvalidArgumentException If the file could not be found or is not of a supported type + * @throws RuntimeException If the transcription failed for other reasons + * @since 28.0.0 + */ + public function summarize(string $text): string; +} diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/LanguageModel/ILanguageModelProvider.php new file mode 100644 index 0000000000000..f44ee64f15169 --- /dev/null +++ b/lib/public/LanguageModel/ILanguageModelProvider.php @@ -0,0 +1,47 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\LanguageModel; + +use RuntimeException; + +/** + * @since 28.0.0 + */ +interface ILanguageModelProvider { + /** + * @since 28.0.0 + */ + public function getName(): string; + + /** + * @param string $prompt The prompt to call the model with + * @return string the output + * @since 28.0.0 + * @throws RuntimeException If the text could not be transcribed + */ + public function prompt(string $prompt): string; +} diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php new file mode 100644 index 0000000000000..125ef313f8094 --- /dev/null +++ b/lib/public/LanguageModel/ISummaryProvider.php @@ -0,0 +1,43 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\LanguageModel; + +use RuntimeException; + +/** + * @since 28.0.0 + */ +interface ISummaryProvider { + + /** + * @param string $text The text to summarize + * @returns string the summary + * @since 28.0.0 + * @throws RuntimeException If the text could not be transcribed + */ + public function summarize(string $text): string; +} From 457f1eb4075a01f42fef8df4ccffa8d7a4b5e826 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 15 Jun 2023 13:22:16 +0200 Subject: [PATCH 002/100] LLM OCP API: Rework to use Task objects Signed-off-by: Marcel Klehr (cherry picked from commit 01dd1a894dbf9eb6ed1fb013c4b5ee4816c32904) --- .../AbstractLanguageModelTask.php | 72 +++++++++++++++++++ .../Events/AbstractLanguageModelEvent.php | 25 ++----- .../Events/PromptFailedEvent.php | 24 ------- .../Events/PromptSuccessfulEvent.php | 23 ------ .../Events/SummaryFailedEvent.php | 24 ------- .../Events/SummarySuccessfulEvent.php | 23 ------ .../LanguageModel/Events/TaskFailedEvent.php | 23 ++++++ .../Events/TaskSuccessfulEvent.php | 23 ++++++ lib/public/LanguageModel/FreePromptTask.php | 25 +++++++ .../LanguageModel/ILanguageModelManager.php | 43 +++-------- lib/public/LanguageModel/ISummaryProvider.php | 2 +- lib/public/LanguageModel/SummaryTask.php | 28 ++++++++ 12 files changed, 185 insertions(+), 150 deletions(-) create mode 100644 lib/public/LanguageModel/AbstractLanguageModelTask.php delete mode 100644 lib/public/LanguageModel/Events/PromptFailedEvent.php delete mode 100644 lib/public/LanguageModel/Events/PromptSuccessfulEvent.php delete mode 100644 lib/public/LanguageModel/Events/SummaryFailedEvent.php delete mode 100644 lib/public/LanguageModel/Events/SummarySuccessfulEvent.php create mode 100644 lib/public/LanguageModel/Events/TaskFailedEvent.php create mode 100644 lib/public/LanguageModel/Events/TaskSuccessfulEvent.php create mode 100644 lib/public/LanguageModel/FreePromptTask.php create mode 100644 lib/public/LanguageModel/SummaryTask.php diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php new file mode 100644 index 0000000000000..50ae209523561 --- /dev/null +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -0,0 +1,72 @@ +status; + } + + /** + * @param int $status + */ + public function setStatus(int $status): void { + $this->status = $status; + } + + /** + * @return int|null + */ + public function getId(): ?int { + return $this->id; + } + + /** + * @param int|null $id + */ + public function setId(?int $id): void { + $this->id = $id; + } + + /** + * @return string + */ + public function getInput(): string { + return $this->input; + } + + /** + * @return string + */ + public function getAppId(): string { + return $this->appId; + } + + /** + * @return string|null + */ + public function getUserId(): ?string { + return $this->userId; + } +} diff --git a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php index 593385b02f0ed..3d274330dc7e1 100644 --- a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php +++ b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php @@ -26,6 +26,7 @@ namespace OCP\LanguageModel\Events; use OCP\EventDispatcher\Event; +use OCP\LanguageModel\AbstractLanguageModelTask; /** * @since 28.0.0 @@ -35,32 +36,16 @@ abstract class AbstractLanguageModelEvent extends Event { * @since 28.0.0 */ public function __construct( - private int $requestId, - private ?string $userId, - private string $appId, + private AbstractLanguageModelTask $task ) { parent::__construct(); } /** + * @return AbstractLanguageModelTask * @since 28.0.0 */ - public function getRequestId(): int { - return $this->requestId; - } - - - /** - * @since 28.0.0 - */ - public function getUserId(): ?string { - return $this->userId; - } - - /** - * @since 28.0.0 - */ - public function getAppId(): string { - return $this->appId; + public function getTask(): AbstractLanguageModelTask { + return $this->task; } } diff --git a/lib/public/LanguageModel/Events/PromptFailedEvent.php b/lib/public/LanguageModel/Events/PromptFailedEvent.php deleted file mode 100644 index 2ad8848d0845a..0000000000000 --- a/lib/public/LanguageModel/Events/PromptFailedEvent.php +++ /dev/null @@ -1,24 +0,0 @@ -errorMessage; - } -} diff --git a/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php b/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php deleted file mode 100644 index f9e3a6123183a..0000000000000 --- a/lib/public/LanguageModel/Events/PromptSuccessfulEvent.php +++ /dev/null @@ -1,23 +0,0 @@ -output; - } -} diff --git a/lib/public/LanguageModel/Events/SummaryFailedEvent.php b/lib/public/LanguageModel/Events/SummaryFailedEvent.php deleted file mode 100644 index 49070656ec4a6..0000000000000 --- a/lib/public/LanguageModel/Events/SummaryFailedEvent.php +++ /dev/null @@ -1,24 +0,0 @@ -errorMessage; - } -} diff --git a/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php b/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php deleted file mode 100644 index 353394438f0a3..0000000000000 --- a/lib/public/LanguageModel/Events/SummarySuccessfulEvent.php +++ /dev/null @@ -1,23 +0,0 @@ -output; - } -} diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php new file mode 100644 index 0000000000000..2b0dea9153fc3 --- /dev/null +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -0,0 +1,23 @@ +errorMessage; + } +} diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php new file mode 100644 index 0000000000000..6cdb57143f9b0 --- /dev/null +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -0,0 +1,23 @@ +output; + } +} diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php new file mode 100644 index 0000000000000..ff7fa7fffedb5 --- /dev/null +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -0,0 +1,25 @@ +setStatus(self::STATUS_RUNNING); + try { + $output = $provider->prompt($this->getInput()); + } catch (RuntimeException $e) { + $this->setStatus(self::STATUS_FAILED); + throw $e; + } + $this->setStatus(self::STATUS_SUCCESSFUL); + return $output; + } +} diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 6805309995502..e0d33777052a1 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -27,6 +27,7 @@ namespace OCP\LanguageModel; use InvalidArgumentException; +use OCP\LanguageModel\Events\AbstractLanguageModelEvent; use OCP\PreConditionNotMetException; use RuntimeException; @@ -37,53 +38,25 @@ interface ILanguageModelManager { public function hasProviders(): bool; /** + * @return string[] * @since 28.0.0 */ - public function hasSummaryProviders(): bool; + public function getAvailableTasks(): array; /** - * @param string $prompt The prompt to call the Language model with - * @returns string The output - * @throws PreConditionNotMetException If no provider was registered but this method was still called + * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @throws InvalidArgumentException If the file could not be found or is not of a supported type * @throws RuntimeException If the transcription failed for other reasons * @since 28.0.0 */ - public function prompt(string $prompt): string; + public function runTask(AbstractLanguageModelTask $task): AbstractLanguageModelEvent; /** * Will schedule an LLM inference process in the background. The result will become available - * with the \OCP\LanguageModel\Events\PromptFinishedEvent + * with the \OCP\LanguageModel\Events\TaskFinishedEvent * - * @param string $prompt The prompt to call the Language model with - * @param ?string $userId The user that triggered this request (only for convenience, will be available on the TranscriptEvents) - * @param string $appId The app that triggered this request (only for convenience, will be available on the TranscriptEvents) - * @returns int The id of the prompt request - * @throws PreConditionNotMetException If no provider was registered but this method was still called + * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @since 28.0.0 */ - public function schedulePrompt(string $prompt, ?string $userId, string $appId): int; - - /** - * Will schedule an LLM inference process in the background. The result will become available - * with the \OCP\LanguageModel\Events\PromptFinishedEvent - * - * @param string $text The text to summarize - * @param ?string $userId The user that triggered this request (only for convenience, will be available on the TranscriptEvents) - * @param string $appId The app that triggered this request (only for convenience, will be available on the TranscriptEvents) - * @returns int The id of the prompt request - * @throws PreConditionNotMetException If no summary provider was registered but this method was still called - * @since 28.0.0 - */ - public function scheduleSummary(string $text, ?string $userId, string $appId): int; - - /** - * @param string $text The text to summarize - * @returns string The output - * @throws PreConditionNotMetException If no summary provider was registered but this method was still called - * @throws InvalidArgumentException If the file could not be found or is not of a supported type - * @throws RuntimeException If the transcription failed for other reasons - * @since 28.0.0 - */ - public function summarize(string $text): string; + public function scheduleTask(AbstractLanguageModelTask $task) : void; } diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php index 125ef313f8094..fa4bf873e8fcc 100644 --- a/lib/public/LanguageModel/ISummaryProvider.php +++ b/lib/public/LanguageModel/ISummaryProvider.php @@ -31,7 +31,7 @@ /** * @since 28.0.0 */ -interface ISummaryProvider { +interface ISummaryProvider extends ILanguageModelProvider { /** * @param string $text The text to summarize diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php new file mode 100644 index 0000000000000..0037beb459309 --- /dev/null +++ b/lib/public/LanguageModel/SummaryTask.php @@ -0,0 +1,28 @@ +setStatus(self::STATUS_RUNNING); + try { + $output = $provider->summarize($this->getInput()); + } catch (RuntimeException $e) { + $this->setStatus(self::STATUS_FAILED); + throw $e; + } + $this->setStatus(self::STATUS_SUCCESSFUL); + return $output; + } +} From d20ee42580086a59c2166f00f571047911e9b87f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 16 Jun 2023 13:06:47 +0200 Subject: [PATCH 003/100] LLM OCP API: Implement private backend code + add ILanguageModelTask Signed-off-by: Marcel Klehr (cherry picked from commit 34138736538f604af2c6c52aa43662d1d66087d0) --- lib/private/LanguageModel/Db/Task.php | 58 +++++++ lib/private/LanguageModel/Db/TaskMapper.php | 34 ++++ .../LanguageModel/LanguageModelManager.php | 162 ++++++++++++++++++ .../LanguageModel/TaskBackgroundJob.php | 72 ++++++++ .../AbstractLanguageModelTask.php | 48 ++++-- .../Events/AbstractLanguageModelEvent.php | 8 +- .../LanguageModel/Events/TaskFailedEvent.php | 4 +- .../Events/TaskSuccessfulEvent.php | 4 +- lib/public/LanguageModel/FreePromptTask.php | 21 +-- .../LanguageModel/ILanguageModelManager.php | 10 +- .../LanguageModel/ILanguageModelTask.php | 56 ++++++ lib/public/LanguageModel/SummaryTask.php | 21 +-- 12 files changed, 452 insertions(+), 46 deletions(-) create mode 100644 lib/private/LanguageModel/Db/Task.php create mode 100644 lib/private/LanguageModel/Db/TaskMapper.php create mode 100644 lib/private/LanguageModel/LanguageModelManager.php create mode 100644 lib/private/LanguageModel/TaskBackgroundJob.php create mode 100644 lib/public/LanguageModel/ILanguageModelTask.php diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php new file mode 100644 index 0000000000000..cee6c2fd8b9c8 --- /dev/null +++ b/lib/private/LanguageModel/Db/Task.php @@ -0,0 +1,58 @@ +addType('id', 'integer'); + $this->addType('type', 'string'); + $this->addType('input', 'string'); + $this->addType('status', 'integer'); + $this->addType('userId', 'string'); + $this->addType('appId', 'string'); + } + + public static function fromLanguageModelTask(ILanguageModelTask $task): Task { + return Task::fromParams([ + 'type' => $task->getType(), + 'status' => ILanguageModelTask::STATUS_UNKNOWN, + 'input' => $task->getInput(), + 'userId' => $task->getUserId(), + 'appId' => $task->getAppId(), + ]); + } +} diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php new file mode 100644 index 0000000000000..0b9004c4d9607 --- /dev/null +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -0,0 +1,34 @@ + + */ +class TaskMapper extends QBMapper { + + public function __construct(IDBConnection $db) { + parent::__construct($db, 'oc_llm_tasks', Task::class); + } + + /** + * @param int $id + * @return Task + * @throws Exception + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function find(int $id): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); + return $this->findEntity($qb); + } +} diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php new file mode 100644 index 0000000000000..f9f13b15d6e25 --- /dev/null +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -0,0 +1,162 @@ +coordinator->getRegistrationContext(); + if ($context === null) { + return []; + } + + if ($this->providers !== null) { + return $this->providers; + } + + $this->providers = []; + + foreach ($context->getSpeechToTextProviders() as $providerServiceRegistration) { + $class = $providerServiceRegistration->getService(); + try { + $this->providers[$class] = $this->serverContainer->get($class); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable $e) { + $this->logger->error('Failed to load LanguageModel provider ' . $class, [ + 'exception' => $e, + ]); + } + } + + return $this->providers; + } + + public function hasProviders(): bool { + $context = $this->coordinator->getRegistrationContext(); + if ($context === null) { + return false; + } + return !empty($context->getSpeechToTextProviders()); + } + + /** + * @inheritDoc + */ + public function getAvailableTasks(): array { + $tasks = []; + foreach ($this->getProviders() as $provider) { + $tasks[FreePromptTask::class] = true; + if ($provider instanceof ISummaryProvider) { + $tasks[SummaryTask::class] = true; + } + } + return array_keys($tasks); + } + + public function canHandleTask(ILanguageModelTask $task): bool { + return !empty(array_filter($this->getAvailableTasks(), fn ($class) => $task instanceof $class)); + } + + /** + * @inheritDoc + */ + public function runTask(ILanguageModelTask $task): string { + if (!$this->canHandleTask($task)) { + throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); + } + foreach ($this->getProviders() as $provider) { + if (!$task->canUseProvider($provider)) { + continue; + } + try { + $task->setStatus(ILanguageModelTask::STATUS_RUNNING); + $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $output = $task->visitProvider($provider); + $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); + $this->taskMapper->update(Task::fromLanguageModelTask($task)); + return $output; + } catch (\RuntimeException $e) { + $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); + $task->setStatus(ILanguageModelTask::STATUS_FAILED); + $this->taskMapper->update(Task::fromLanguageModelTask($task)); + throw $e; + } catch (\Throwable $e) { + $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); + $task->setStatus(ILanguageModelTask::STATUS_FAILED); + $this->taskMapper->update(Task::fromLanguageModelTask($task)); + throw new RuntimeException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage()); + } + } + + throw new RuntimeException('Could not transcribe file'); + } + + /** + * @inheritDoc + * @throws Exception + */ + public function scheduleTask(ILanguageModelTask $task): void { + if (!$this->canHandleTask($task)) { + throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); + } + $taskEntity = Task::fromLanguageModelTask($task); + $this->taskMapper->insert($taskEntity); + $task->setId($taskEntity->getId()); + $task->setStatus(ILanguageModelTask::STATUS_SCHEDULED); + $this->jobList->add(TaskBackgroundJob::class, [ + 'taskId' => $task->getId() + ]); + } + + /** + * @param int $id The id of the task + * @return ILanguageModelTask + * @throws RuntimeException If the query failed + * @throws \ValueError If the task could not be found + */ + public function getTask(int $id): ILanguageModelTask { + try { + $taskEntity = $this->taskMapper->find($id); + return AbstractLanguageModelTask::fromTaskEntity($taskEntity); + } catch (DoesNotExistException $e) { + throw new \ValueError('Could not find task with the provided id'); + } catch (MultipleObjectsReturnedException $e) { + throw new RuntimeException('Could not uniquely identify task with given id'); + } catch (Exception $e) { + throw new RuntimeException('Failure while trying to find task by id: '.$e->getMessage()); + } + } +} diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php new file mode 100644 index 0000000000000..55413ba371471 --- /dev/null +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -0,0 +1,72 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OC\LanguageModel; + +use OC\User\NoUserException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\LanguageModel\Events\TaskFailedEvent; +use OCP\LanguageModel\Events\TaskSuccessfulEvent; +use OCP\LanguageModel\ILanguageModelManager; +use OCP\PreConditionNotMetException; +use OCP\SpeechToText\Events\TranscriptionFailedEvent; +use OCP\SpeechToText\Events\TranscriptionSuccessfulEvent; +use OCP\SpeechToText\ISpeechToTextManager; +use Psr\Log\LoggerInterface; + +class TaskBackgroundJob extends QueuedJob { + public function __construct( + ITimeFactory $timeFactory, + private ILanguageModelManager $languageModelManager, + private IEventDispatcher $eventDispatcher, + ) { + parent::__construct($timeFactory); + $this->setAllowParallelRuns(false); + } + + /** + * @param array{taskId: int} $argument + * @inheritDoc + */ + protected function run($argument) { + $taskId = $argument['taskId']; + $task = $this->languageModelManager->getTask($taskId); + try { + $output = $this->languageModelManager->runTask($task); + $event = new TaskSuccessfulEvent($task, $output); + + } catch (\RuntimeException|PreConditionNotMetException $e) { + $event = new TaskFailedEvent($task, $e->getMessage()); + } + $this->eventDispatcher->dispatchTyped($event); + } +} diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 50ae209523561..12aedc95fe56e 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -2,71 +2,91 @@ namespace OCP\LanguageModel; -abstract class AbstractLanguageModelTask { - public const STATUS_UNKNOWN = 0; - public const STATUS_RUNNING = 1; - public const STATUS_SUCCESSFUL = 2; - public const STATUS_FAILED = 4; +use OC\LanguageModel\Db\Task; +abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id; - protected int $status = self::STATUS_UNKNOWN; + protected int $status = ILanguageModelTask::STATUS_UNKNOWN; - public function __construct( + public final function __construct( protected string $input, protected string $appId, protected ?string $userId, ) { } + /** + * @param ILanguageModelProvider $provider + * @return string + * @throws \RuntimeException + */ abstract public function visitProvider(ILanguageModelProvider $provider): string; + abstract public function canUseProvider(ILanguageModelProvider $provider): bool; + + abstract public function getType(): string; + /** * @return int */ - public function getStatus(): int { + public final function getStatus(): int { return $this->status; } /** * @param int $status */ - public function setStatus(int $status): void { + public final function setStatus(int $status): void { $this->status = $status; } /** * @return int|null */ - public function getId(): ?int { + public final function getId(): ?int { return $this->id; } /** * @param int|null $id */ - public function setId(?int $id): void { + public final function setId(?int $id): void { $this->id = $id; } /** * @return string */ - public function getInput(): string { + public final function getInput(): string { return $this->input; } /** * @return string */ - public function getAppId(): string { + public final function getAppId(): string { return $this->appId; } /** * @return string|null */ - public function getUserId(): ?string { + public final function getUserId(): ?string { return $this->userId; } + + public final static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { + $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); + $task->setId($taskEntity->getId()); + $task->setStatus($taskEntity->getStatus()); + return $task; + } + + public final static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { + if (!in_array($type, self::TYPES)) { + throw new \InvalidArgumentException('Unknown task type'); + } + return new ILanguageModelTask::TYPES[$type]($input, $userId, $appId); + } } diff --git a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php index 3d274330dc7e1..218a448008187 100644 --- a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php +++ b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php @@ -26,7 +26,7 @@ namespace OCP\LanguageModel\Events; use OCP\EventDispatcher\Event; -use OCP\LanguageModel\AbstractLanguageModelTask; +use OCP\LanguageModel\ILanguageModelTask; /** * @since 28.0.0 @@ -36,16 +36,16 @@ abstract class AbstractLanguageModelEvent extends Event { * @since 28.0.0 */ public function __construct( - private AbstractLanguageModelTask $task + private ILanguageModelTask $task ) { parent::__construct(); } /** - * @return AbstractLanguageModelTask + * @return ILanguageModelTask * @since 28.0.0 */ - public function getTask(): AbstractLanguageModelTask { + public function getTask(): ILanguageModelTask { return $this->task; } } diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index 2b0dea9153fc3..5134c37476a16 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -2,14 +2,14 @@ namespace OCP\LanguageModel\Events; -use OCP\LanguageModel\AbstractLanguageModelTask; +use OCP\LanguageModel\ILanguageModelTask; /** * @since 28.0.0 */ class TaskFailedEvent extends AbstractLanguageModelEvent { - public function __construct(AbstractLanguageModelTask $task, + public function __construct(ILanguageModelTask $task, private string $errorMessage) { parent::__construct($task); } diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php index 6cdb57143f9b0..156c5679e0beb 100644 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -2,14 +2,14 @@ namespace OCP\LanguageModel\Events; -use OCP\LanguageModel\AbstractLanguageModelTask; +use OCP\LanguageModel\ILanguageModelTask; /** * @since 28.0.0 */ class TaskSuccessfulEvent extends AbstractLanguageModelEvent { - public function __construct(AbstractLanguageModelTask $task, + public function __construct(ILanguageModelTask $task, private string $output) { parent::__construct($task); } diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index ff7fa7fffedb5..a179048631c8c 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -4,7 +4,8 @@ use RuntimeException; -class FreePromptTask extends AbstractLanguageModelTask { +final class FreePromptTask extends AbstractLanguageModelTask { + public const TYPE = 'free_prompt'; /** * @param ILanguageModelProvider $provider @@ -12,14 +13,14 @@ class FreePromptTask extends AbstractLanguageModelTask { * @return string */ public function visitProvider(ILanguageModelProvider $provider): string { - $this->setStatus(self::STATUS_RUNNING); - try { - $output = $provider->prompt($this->getInput()); - } catch (RuntimeException $e) { - $this->setStatus(self::STATUS_FAILED); - throw $e; - } - $this->setStatus(self::STATUS_SUCCESSFUL); - return $output; + return $provider->prompt($this->getInput()); + } + + public function canUseProvider(ILanguageModelProvider $provider): bool { + return true; + } + + public function getType(): string { + return self::TYPE; } } diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index e0d33777052a1..a4d3079c180b3 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -27,6 +27,7 @@ namespace OCP\LanguageModel; use InvalidArgumentException; +use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\Events\AbstractLanguageModelEvent; use OCP\PreConditionNotMetException; use RuntimeException; @@ -45,11 +46,10 @@ public function getAvailableTasks(): array; /** * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called - * @throws InvalidArgumentException If the file could not be found or is not of a supported type - * @throws RuntimeException If the transcription failed for other reasons + * @throws RuntimeException If something else failed * @since 28.0.0 */ - public function runTask(AbstractLanguageModelTask $task): AbstractLanguageModelEvent; + public function runTask(ILanguageModelTask $task): string; /** * Will schedule an LLM inference process in the background. The result will become available @@ -58,5 +58,7 @@ public function runTask(AbstractLanguageModelTask $task): AbstractLanguageModelE * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @since 28.0.0 */ - public function scheduleTask(AbstractLanguageModelTask $task) : void; + public function scheduleTask(ILanguageModelTask $task) : void; + + public function getTask(int $id): ILanguageModelTask; } diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php new file mode 100644 index 0000000000000..478ee54e8a31f --- /dev/null +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -0,0 +1,56 @@ + SummaryTask::class, + FreePromptTask::TYPE => FreePromptTask::class, + ]; + + /** + * @return string + */ + public function getType(): string; + + /** + * @return int + */ + public function getStatus(): int; + + /** + * @param int $status + */ + public function setStatus(int $status): void; + + /** + * @param int|null $id + */ + public function setId(?int $id): void; + + /** + * @return int|null + */ + public function getId(): ?int; + + /** + * @return string + */ + public function getInput(): string; + + /** + * @return string + */ + public function getAppId(): string; + + /** + * @return string|null + */ + public function getUserId(): ?string; +} diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 0037beb459309..35f20cebfb633 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -4,7 +4,8 @@ use RuntimeException; -class SummaryTask extends AbstractLanguageModelTask { +final class SummaryTask extends AbstractLanguageModelTask { + public const TYPE = 'summarize'; /** * @param ILanguageModelProvider&ISummaryProvider $provider @@ -15,14 +16,14 @@ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof ISummaryProvider) { throw new \RuntimeException('SummaryTask#visitProvider expects ISummaryProvider'); } - $this->setStatus(self::STATUS_RUNNING); - try { - $output = $provider->summarize($this->getInput()); - } catch (RuntimeException $e) { - $this->setStatus(self::STATUS_FAILED); - throw $e; - } - $this->setStatus(self::STATUS_SUCCESSFUL); - return $output; + return $provider->summarize($this->getInput()); + } + + public function canUseProvider(ILanguageModelProvider $provider): bool { + return $provider instanceof ISummaryProvider; + } + + public function getType(): string { + return self::TYPE; } } From 7031072717f7cd121335777dee56ac6aa8c91da4 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 16 Jun 2023 13:22:14 +0200 Subject: [PATCH 004/100] LLM OCP API: Add db migration Signed-off-by: Marcel Klehr (cherry picked from commit 6fc4cb63efcb83b9c86814b9e2ead597be630b7e) --- .../Version28000Date20230616104802.php | 85 +++++++++++++++++++ lib/private/LanguageModel/Db/TaskMapper.php | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 core/Migrations/Version28000Date20230616104802.php diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php new file mode 100644 index 0000000000000..484267f9fab4a --- /dev/null +++ b/core/Migrations/Version28000Date20230616104802.php @@ -0,0 +1,85 @@ + + * + * @author Louis Chmn + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Introduce llm_tasks table + */ +class Version28000Date20230616104802 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('llm_tasks')) { + $table = $schema->createTable('llm_tasks'); + + $table->addColumn('id', Types::BIGINT, [ + 'notnull' => true, + 'length' => 64, + 'autoincrement' => true, + ]); + $table->addColumn('type', Types::STRING, [ + 'notnull' => true, + 'length' => 255, + ]); + $table->addColumn('input', Types::TEXT, [ + 'notnull' => true, + ]); + $table->addColumn('status', Types::INTEGER, [ + 'notnull' => false, + 'length' => 6, + 'default' => 0, + ]); + $table->addColumn('user_id', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('app_id', Types::STRING, [ + 'notnull' => true, + 'length' => 32, + 'default' => '', + ]); + + $table->setPrimaryKey(['id'], 'llm_tasks_id_index'); + $table->addUniqueIndex(['status', 'type'], 'llm_tasks_status_type'); + } + + return $schema; + } +} diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php index 0b9004c4d9607..46f0f4d21f616 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -14,7 +14,7 @@ class TaskMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'oc_llm_tasks', Task::class); + parent::__construct($db, 'llm_tasks', Task::class); } /** From ed3672a5c2abbeaa3976e4dcee8169b42d9c9783 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 16 Jun 2023 13:27:59 +0200 Subject: [PATCH 005/100] LLM OCP API: Add to RegistrationContext Signed-off-by: Marcel Klehr (cherry picked from commit 82d3b00ab1f5ed3206a986e4969778bff77ca560) --- .../Bootstrap/RegistrationContext.php | 21 +++++++++++++++++++ .../LanguageModel/LanguageModelManager.php | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 8fcafab2d87db..67e8b390c158b 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -33,6 +33,7 @@ use OCP\Calendar\Resource\IBackend as IResourceBackend; use OCP\Calendar\Room\IBackend as IRoomBackend; use OCP\Collaboration\Reference\IReferenceProvider; +use OCP\LanguageModel\ILanguageModelProvider; use OCP\SpeechToText\ISpeechToTextProvider; use OCP\Talk\ITalkBackend; use OCP\Translation\ITranslationProvider; @@ -115,6 +116,9 @@ class RegistrationContext { /** @var ServiceRegistration[] */ private $speechToTextProviders = []; + /** @var ServiceRegistration[] */ + private $languageModelProviders = []; + /** @var ServiceRegistration[] */ private $templateProviders = []; @@ -262,6 +266,12 @@ public function registerSpeechToTextProvider(string $providerClass): void { $providerClass ); } + public function registerLanguageModelProvider(string $providerClass): void { + $this->context->registerLanguageModelProvider( + $this->appId, + $providerClass + ); + } public function registerTemplateProvider(string $providerClass): void { $this->context->registerTemplateProvider( @@ -429,6 +439,10 @@ public function registerSpeechToTextProvider(string $appId, string $class): void $this->speechToTextProviders[] = new ServiceRegistration($appId, $class); } + public function registerLanguageModelProvider(string $appId, string $class): void { + $this->languageModelProviders[] = new ServiceRegistration($appId, $class); + } + public function registerTemplateProvider(string $appId, string $class): void { $this->templateProviders[] = new ServiceRegistration($appId, $class); } @@ -707,6 +721,13 @@ public function getSpeechToTextProviders(): array { return $this->speechToTextProviders; } + /** + * @return ServiceRegistration[] + */ + public function getLanguageModelProviders(): array { + return $this->languageModelProviders; + } + /** * @return ServiceRegistration[] */ diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index f9f13b15d6e25..b0e45f5812ab4 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -50,7 +50,7 @@ public function getProviders(): array { $this->providers = []; - foreach ($context->getSpeechToTextProviders() as $providerServiceRegistration) { + foreach ($context->getLanguageModelProviders() as $providerServiceRegistration) { $class = $providerServiceRegistration->getService(); try { $this->providers[$class] = $this->serverContainer->get($class); From 33f3379855e3a58023954433e7910161033736d4 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 19 Jun 2023 11:10:52 +0200 Subject: [PATCH 006/100] Fix Copyright Signed-off-by: Marcel Klehr (cherry picked from commit 9e9fc1d99bc8f058762d27d548303a62372771e7) --- core/Migrations/Version28000Date20230616104802.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php index 484267f9fab4a..ffb2b38f57e2f 100644 --- a/core/Migrations/Version28000Date20230616104802.php +++ b/core/Migrations/Version28000Date20230616104802.php @@ -3,9 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Louis Chmn + * @copyright Copyright (c) 2023 Marcel Klehr * - * @author Louis Chmn + * @author Marcel Klehr * * @license GNU AGPL version 3 or any later version * From 5807c431b854cca5e46fda4222782cd488137222 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 14:41:28 +0200 Subject: [PATCH 007/100] LLM OCP API: ADd topics and headline tasks Signed-off-by: Marcel Klehr (cherry picked from commit 9d5717d239d380273f49ea6542c7eee2f0353703) --- lib/public/LanguageModel/HeadlineTask.php | 29 +++++++++++++ .../LanguageModel/IHeadlineProvider.php | 43 +++++++++++++++++++ .../LanguageModel/ILanguageModelTask.php | 16 ++++++- lib/public/LanguageModel/ITopicsProvider.php | 43 +++++++++++++++++++ lib/public/LanguageModel/SummaryTask.php | 2 +- lib/public/LanguageModel/TopicsTask.php | 29 +++++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 lib/public/LanguageModel/HeadlineTask.php create mode 100644 lib/public/LanguageModel/IHeadlineProvider.php create mode 100644 lib/public/LanguageModel/ITopicsProvider.php create mode 100644 lib/public/LanguageModel/TopicsTask.php diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php new file mode 100644 index 0000000000000..003488b68419c --- /dev/null +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -0,0 +1,29 @@ +findHeadline($this->getInput()); + } + + public function canUseProvider(ILanguageModelProvider $provider): bool { + return $provider instanceof IHeadlineProvider; + } + + public function getType(): string { + return self::TYPE; + } +} diff --git a/lib/public/LanguageModel/IHeadlineProvider.php b/lib/public/LanguageModel/IHeadlineProvider.php new file mode 100644 index 0000000000000..2ade27d2f7463 --- /dev/null +++ b/lib/public/LanguageModel/IHeadlineProvider.php @@ -0,0 +1,43 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\LanguageModel; + +use RuntimeException; + +/** + * @since 28.0.0 + */ +interface IHeadlineProvider extends ILanguageModelProvider { + + /** + * @param string $text The text to find headline for + * @returns string the headline + * @since 28.0.0 + * @throws RuntimeException If the text could not be transcribed + */ + public function findHeadline(string $text): string; +} diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 478ee54e8a31f..3775546d4f0d3 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -2,7 +2,7 @@ namespace OCP\LanguageModel; -interface ILanguageModelTask { +interface ILanguageModelTask extends \JsonSerializable { public const STATUS_FAILED = 4; public const STATUS_SUCCESSFUL = 3; public const STATUS_RUNNING = 2; @@ -10,8 +10,10 @@ interface ILanguageModelTask { public const STATUS_UNKNOWN = 0; public const TYPES = [ - SummaryTask::TYPE => SummaryTask::class, FreePromptTask::TYPE => FreePromptTask::class, + SummaryTask::TYPE => SummaryTask::class, + HeadlineTask::TYPE => HeadlineTask::class, + TopicsTask::TYPE => TopicsTask::class, ]; /** @@ -44,6 +46,16 @@ public function getId(): ?int; */ public function getInput(): string; + /** + * @param string $output + */ + public function setOutput(string $output): void; + + /** + * @return string + */ + public function getOutput(): string; + /** * @return string */ diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/LanguageModel/ITopicsProvider.php new file mode 100644 index 0000000000000..a55df0d704446 --- /dev/null +++ b/lib/public/LanguageModel/ITopicsProvider.php @@ -0,0 +1,43 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\LanguageModel; + +use RuntimeException; + +/** + * @since 28.0.0 + */ +interface ITopicsProvider extends ILanguageModelProvider { + + /** + * @param string $text The text to find topics for + * @returns string the topics, comma separated + * @since 28.0.0 + * @throws RuntimeException If the text could not be transcribed + */ + public function findTopics(string $text): string; +} diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 35f20cebfb633..3c300246f03bc 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -8,7 +8,7 @@ final class SummaryTask extends AbstractLanguageModelTask { public const TYPE = 'summarize'; /** - * @param ILanguageModelProvider&ISummaryProvider $provider + * @param ILanguageModelProvider $provider * @throws RuntimeException * @return string */ diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php new file mode 100644 index 0000000000000..98ffabfc815ab --- /dev/null +++ b/lib/public/LanguageModel/TopicsTask.php @@ -0,0 +1,29 @@ +findTopics($this->getInput()); + } + + public function canUseProvider(ILanguageModelProvider $provider): bool { + return $provider instanceof ITopicsProvider; + } + + public function getType(): string { + return self::TYPE; + } +} From 236c32b13f190f49fa369e4eb20ec4a85a76ba04 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 14:41:58 +0200 Subject: [PATCH 008/100] LLM OCP API: Implement ocs API Signed-off-by: Marcel Klehr (cherry picked from commit 795b097122a8dd70b4d6b9ebe044440396be9104) --- .../Controller/LanguageModelApiController.php | 96 +++++++++++++++++++ .../Version28000Date20230616104802.php | 3 + core/routes.php | 4 + lib/private/LanguageModel/Db/Task.php | 7 +- .../LanguageModel/LanguageModelManager.php | 11 ++- .../LanguageModel/TaskBackgroundJob.php | 4 +- lib/private/Server.php | 4 + .../Bootstrap/IRegistrationContext.php | 11 +++ .../AbstractLanguageModelTask.php | 21 ++++ .../Events/TaskSuccessfulEvent.php | 10 +- .../LanguageModel/ILanguageModelManager.php | 12 +++ 11 files changed, 167 insertions(+), 16 deletions(-) create mode 100644 core/Controller/LanguageModelApiController.php diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php new file mode 100644 index 0000000000000..5699dd7552673 --- /dev/null +++ b/core/Controller/LanguageModelApiController.php @@ -0,0 +1,96 @@ + + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OC\Core\Controller; + +use InvalidArgumentException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; +use OCP\IRequest; +use OCP\LanguageModel\AbstractLanguageModelTask; +use OCP\LanguageModel\ILanguageModelManager; +use OCP\PreConditionNotMetException; + +class LanguageModelApiController extends \OCP\AppFramework\OCSController { + public function __construct( + string $appName, + IRequest $request, + private ILanguageModelManager $languageModelManager, + private IL10N $l, + private ?string $userId, + ) { + parent::__construct($appName, $request); + } + + /** + * @PublicPage + */ + public function tasks(): DataResponse { + return new DataResponse([ + 'tasks' => $this->languageModelManager->getAvailableTaskTypes(), + ]); + } + + /** + * @PublicPage + * @UserRateThrottle(limit=20, period=120) + * @AnonRateThrottle(limit=5, period=120) + */ + public function schedule(string $text, string $type, ?string $appId): DataResponse { + try { + $task = AbstractLanguageModelTask::factory($type, $text, $this->userId, $appId); + } catch (InvalidArgumentException $e) { + return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); + } + try { + $this->languageModelManager->scheduleTask($task); + + return new DataResponse([ + 'task' => $task, + ]); + } catch (PreConditionNotMetException) { + return new DataResponse(['message' => $this->l->t('Necessary language model provider is not available')], Http::STATUS_PRECONDITION_FAILED); + } + } + + /** + * @PublicPage + */ + public function getTask(int $id): DataResponse { + try { + $task = $this->languageModelManager->getTask($id); + + return new DataResponse([ + 'task' => $task, + ]); + } catch (\ValueError $e) { + return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); + } catch (\RuntimeException $e) { + return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php index ffb2b38f57e2f..76d8173861f41 100644 --- a/core/Migrations/Version28000Date20230616104802.php +++ b/core/Migrations/Version28000Date20230616104802.php @@ -61,6 +61,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addColumn('input', Types::TEXT, [ 'notnull' => true, ]); + $table->addColumn('output', Types::TEXT, [ + 'notnull' => false, + ]); $table->addColumn('status', Types::INTEGER, [ 'notnull' => false, 'length' => 6, diff --git a/core/routes.php b/core/routes.php index 0f9729e54eb09..20be6ef63f456 100644 --- a/core/routes.php +++ b/core/routes.php @@ -145,6 +145,10 @@ ['root' => '/translation', 'name' => 'TranslationApi#languages', 'url' => '/languages', 'verb' => 'GET'], ['root' => '/translation', 'name' => 'TranslationApi#translate', 'url' => '/translate', 'verb' => 'POST'], + + ['root' => '/languagemodel', 'name' => 'LanguageModelApi#tasks', 'url' => '/tasks', 'verb' => 'GET'], + ['root' => '/languagemodel', 'name' => 'LanguageModelApi#schedule', 'url' => '/schedule', 'verb' => 'POST'], + ['root' => '/languagemodel', 'name' => 'LanguageModelApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'], ], ]); diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index cee6c2fd8b9c8..e5d2f79e45383 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -28,12 +28,12 @@ class Task extends Entity { /** * @var string[] */ - public static array $columns = ['id', 'type', 'input', 'status', 'user_id', 'app_id']; + public static array $columns = ['id', 'type', 'input', 'output', 'status', 'user_id', 'app_id']; /** * @var string[] */ - public static array $fields = ['id', 'type', 'input', 'status', 'userId', 'appId']; + public static array $fields = ['id', 'type', 'input', 'output', 'status', 'userId', 'appId']; public function __construct() { @@ -49,8 +49,9 @@ public function __construct() { public static function fromLanguageModelTask(ILanguageModelTask $task): Task { return Task::fromParams([ 'type' => $task->getType(), - 'status' => ILanguageModelTask::STATUS_UNKNOWN, + 'status' => $task->getStatus(), 'input' => $task->getInput(), + 'output' => $task->getOutput(), 'userId' => $task->getUserId(), 'appId' => $task->getAppId(), ]); diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index b0e45f5812ab4..7db2e656a0a9b 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -86,6 +86,13 @@ public function getAvailableTasks(): array { return array_keys($tasks); } + /** + * @inheritDoc + */ + public function getAvailableTaskTypes(): array { + return array_map(fn ($taskClass) => $taskClass::TYPE, $this->getAvailableTasks()); + } + public function canHandleTask(ILanguageModelTask $task): bool { return !empty(array_filter($this->getAvailableTasks(), fn ($class) => $task instanceof $class)); } @@ -104,10 +111,10 @@ public function runTask(ILanguageModelTask $task): string { try { $task->setStatus(ILanguageModelTask::STATUS_RUNNING); $this->taskMapper->update(Task::fromLanguageModelTask($task)); - $output = $task->visitProvider($provider); + $task->setOutput($task->visitProvider($provider)); $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); $this->taskMapper->update(Task::fromLanguageModelTask($task)); - return $output; + return $task->getOutput(); } catch (\RuntimeException $e) { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); $task->setStatus(ILanguageModelTask::STATUS_FAILED); diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index 55413ba371471..3c18ff0310225 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -61,8 +61,8 @@ protected function run($argument) { $taskId = $argument['taskId']; $task = $this->languageModelManager->getTask($taskId); try { - $output = $this->languageModelManager->runTask($task); - $event = new TaskSuccessfulEvent($task, $output); + $this->languageModelManager->runTask($task); + $event = new TaskSuccessfulEvent($task); } catch (\RuntimeException|PreConditionNotMetException $e) { $event = new TaskFailedEvent($task, $e->getMessage()); diff --git a/lib/private/Server.php b/lib/private/Server.php index bb4e217efa3bb..d1f18a1235f03 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -110,6 +110,7 @@ use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; +use OC\LanguageModel\LanguageModelManager; use OC\LDAP\NullLDAPProviderFactory; use OC\KnownUser\KnownUserService; use OC\Lock\DBLockingProvider; @@ -228,6 +229,7 @@ use OCP\IUserManager; use OCP\IUserSession; use OCP\L10N\IFactory; +use OCP\LanguageModel\ILanguageModelManager; use OCP\LDAP\ILDAPProvider; use OCP\LDAP\ILDAPProviderFactory; use OCP\Lock\ILockingProvider; @@ -1461,6 +1463,8 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(ISpeechToTextManager::class, SpeechToTextManager::class); + $this->registerAlias(ILanguageModelManager::class, LanguageModelManager::class); + $this->connectDispatcher(); } diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 66cf1ef23063a..19ef6832a2c2c 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -37,6 +37,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\IContainer; +use OCP\LanguageModel\ILanguageModelProvider; use OCP\Notification\INotifier; use OCP\Preview\IProviderV2; use OCP\SpeechToText\ISpeechToTextProvider; @@ -219,6 +220,16 @@ public function registerWellKnownHandler(string $class): void; */ public function registerSpeechToTextProvider(string $providerClass): void; + /** + * Register a custom LanguageModel provider class that provides a promptable language model + * through the OCP\LanguageModel APIs + * + * @param string $providerClass + * @psalm-param class-string $providerClass + * @since 27.0.0 + */ + public function registerLanguageModelProvider(string $providerClass): void; + /** * Register a custom template provider class that is able to inject custom templates * in addition to the user defined ones diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 12aedc95fe56e..63b6396fb437a 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -76,6 +76,19 @@ public final function getUserId(): ?string { return $this->userId; } + public function jsonSerialize() { + return [ + 'id' => $this->getId(), + 'type' => $this->getType(), + 'status' => $this->getStatus(), + 'userId' => $this->getUserId(), + 'appId' => $this->getAppId(), + 'input' => $this->getInput(), + 'output' => $this->getOutput(), + ]; + } + + public final static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); $task->setId($taskEntity->getId()); @@ -83,6 +96,14 @@ public final static function fromTaskEntity(Task $taskEntity): ILanguageModelTas return $task; } + /** + * @param string $type + * @param string $input + * @param string|null $userId + * @param string $appId + * @return ILanguageModelTask + * @throws \InvalidArgumentException + */ public final static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { if (!in_array($type, self::TYPES)) { throw new \InvalidArgumentException('Unknown task type'); diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php index 156c5679e0beb..61be3a20cd172 100644 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -9,15 +9,7 @@ */ class TaskSuccessfulEvent extends AbstractLanguageModelEvent { - public function __construct(ILanguageModelTask $task, - private string $output) { + public function __construct(ILanguageModelTask $task) { parent::__construct($task); } - - /** - * @return string - */ - public function getErrorMessage(): string { - return $this->output; - } } diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index a4d3079c180b3..439cfb761764f 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -44,6 +44,12 @@ public function hasProviders(): bool; */ public function getAvailableTasks(): array; + /** + * @return string[] + * @since 28.0.0 + */ + public function getAvailableTaskTypes(): array; + /** * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @throws RuntimeException If something else failed @@ -60,5 +66,11 @@ public function runTask(ILanguageModelTask $task): string; */ public function scheduleTask(ILanguageModelTask $task) : void; + /** + * @param int $id The id of the task + * @return ILanguageModelTask + * @throws RuntimeException If the query failed + * @throws \ValueError If the task could not be found + */ public function getTask(int $id): ILanguageModelTask; } From 76a5db8867fe16e59636022a532d5ff173122b3d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 14:49:03 +0200 Subject: [PATCH 009/100] LLM OCP API: cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit b6941aa5c3d26b1c2d66837ee924e8e14e5e1232) --- lib/private/LanguageModel/Db/Task.php | 1 - lib/private/LanguageModel/Db/TaskMapper.php | 1 - lib/private/LanguageModel/LanguageModelManager.php | 7 +++---- lib/private/LanguageModel/TaskBackgroundJob.php | 10 ---------- lib/public/LanguageModel/Events/TaskFailedEvent.php | 3 +-- .../LanguageModel/Events/TaskSuccessfulEvent.php | 1 - lib/public/LanguageModel/IHeadlineProvider.php | 1 - lib/public/LanguageModel/ILanguageModelManager.php | 3 --- lib/public/LanguageModel/ISummaryProvider.php | 1 - lib/public/LanguageModel/ITopicsProvider.php | 1 - 10 files changed, 4 insertions(+), 25 deletions(-) diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index e5d2f79e45383..fe77d5595307a 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -18,7 +18,6 @@ * @method string getAppId() */ class Task extends Entity { - protected $type; protected $input; protected $status; diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php index 46f0f4d21f616..d7122ea794165 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -12,7 +12,6 @@ * @extends QBMapper */ class TaskMapper extends QBMapper { - public function __construct(IDBConnection $db) { parent::__construct($db, 'llm_tasks', Task::class); } diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 7db2e656a0a9b..14b14397c09f0 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -5,18 +5,18 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\LanguageModel\Db\Task; use OC\LanguageModel\Db\TaskMapper; -use OCP\LanguageModel\AbstractLanguageModelTask; -use OCP\LanguageModel\FreePromptTask; -use OCP\LanguageModel\SummaryTask; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\BackgroundJob\IJobList; use OCP\DB\Exception; use OCP\IServerContainer; +use OCP\LanguageModel\AbstractLanguageModelTask; +use OCP\LanguageModel\FreePromptTask; use OCP\LanguageModel\ILanguageModelManager; use OCP\LanguageModel\ILanguageModelProvider; use OCP\LanguageModel\ILanguageModelTask; use OCP\LanguageModel\ISummaryProvider; +use OCP\LanguageModel\SummaryTask; use OCP\PreConditionNotMetException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -25,7 +25,6 @@ use Throwable; class LanguageModelManager implements ILanguageModelManager { - /** @var ?ILanguageModelProvider[] */ private ?array $providers = null; diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index 3c18ff0310225..1c1291dc12699 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -26,22 +26,13 @@ namespace OC\LanguageModel; -use OC\User\NoUserException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\QueuedJob; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\File; -use OCP\Files\IRootFolder; -use OCP\Files\NotFoundException; -use OCP\Files\NotPermittedException; use OCP\LanguageModel\Events\TaskFailedEvent; use OCP\LanguageModel\Events\TaskSuccessfulEvent; use OCP\LanguageModel\ILanguageModelManager; use OCP\PreConditionNotMetException; -use OCP\SpeechToText\Events\TranscriptionFailedEvent; -use OCP\SpeechToText\Events\TranscriptionSuccessfulEvent; -use OCP\SpeechToText\ISpeechToTextManager; -use Psr\Log\LoggerInterface; class TaskBackgroundJob extends QueuedJob { public function __construct( @@ -63,7 +54,6 @@ protected function run($argument) { try { $this->languageModelManager->runTask($task); $event = new TaskSuccessfulEvent($task); - } catch (\RuntimeException|PreConditionNotMetException $e) { $event = new TaskFailedEvent($task, $e->getMessage()); } diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index 5134c37476a16..11e7fdfa23d21 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -8,9 +8,8 @@ * @since 28.0.0 */ class TaskFailedEvent extends AbstractLanguageModelEvent { - public function __construct(ILanguageModelTask $task, - private string $errorMessage) { + private string $errorMessage) { parent::__construct($task); } diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php index 61be3a20cd172..35b5a043215f2 100644 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -8,7 +8,6 @@ * @since 28.0.0 */ class TaskSuccessfulEvent extends AbstractLanguageModelEvent { - public function __construct(ILanguageModelTask $task) { parent::__construct($task); } diff --git a/lib/public/LanguageModel/IHeadlineProvider.php b/lib/public/LanguageModel/IHeadlineProvider.php index 2ade27d2f7463..b2e902a034df2 100644 --- a/lib/public/LanguageModel/IHeadlineProvider.php +++ b/lib/public/LanguageModel/IHeadlineProvider.php @@ -32,7 +32,6 @@ * @since 28.0.0 */ interface IHeadlineProvider extends ILanguageModelProvider { - /** * @param string $text The text to find headline for * @returns string the headline diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 439cfb761764f..f7864a68a8eda 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -26,9 +26,6 @@ namespace OCP\LanguageModel; -use InvalidArgumentException; -use OCP\LanguageModel\AbstractLanguageModelTask; -use OCP\LanguageModel\Events\AbstractLanguageModelEvent; use OCP\PreConditionNotMetException; use RuntimeException; diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php index fa4bf873e8fcc..af897e1a8c3bd 100644 --- a/lib/public/LanguageModel/ISummaryProvider.php +++ b/lib/public/LanguageModel/ISummaryProvider.php @@ -32,7 +32,6 @@ * @since 28.0.0 */ interface ISummaryProvider extends ILanguageModelProvider { - /** * @param string $text The text to summarize * @returns string the summary diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/LanguageModel/ITopicsProvider.php index a55df0d704446..2e3983819693f 100644 --- a/lib/public/LanguageModel/ITopicsProvider.php +++ b/lib/public/LanguageModel/ITopicsProvider.php @@ -32,7 +32,6 @@ * @since 28.0.0 */ interface ITopicsProvider extends ILanguageModelProvider { - /** * @param string $text The text to find topics for * @returns string the topics, comma separated From 01db2cff5a8df0909a229be77043428fcb4e3960 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 14:58:58 +0200 Subject: [PATCH 010/100] LLM OCP API: Make linters happy Signed-off-by: Marcel Klehr (cherry picked from commit b8a9f08d833ae2893eed9b4a19749822bf0441bc) --- .../AbstractLanguageModelTask.php | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 63b6396fb437a..f1834cdf05884 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -2,14 +2,13 @@ namespace OCP\LanguageModel; - use OC\LanguageModel\Db\Task; abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id; protected int $status = ILanguageModelTask::STATUS_UNKNOWN; - public final function __construct( + final public function __construct( protected string $input, protected string $appId, protected ?string $userId, @@ -30,49 +29,49 @@ abstract public function getType(): string; /** * @return int */ - public final function getStatus(): int { + final public function getStatus(): int { return $this->status; } /** * @param int $status */ - public final function setStatus(int $status): void { + final public function setStatus(int $status): void { $this->status = $status; } /** * @return int|null */ - public final function getId(): ?int { + final public function getId(): ?int { return $this->id; } /** * @param int|null $id */ - public final function setId(?int $id): void { + final public function setId(?int $id): void { $this->id = $id; } /** * @return string */ - public final function getInput(): string { + final public function getInput(): string { return $this->input; } /** * @return string */ - public final function getAppId(): string { + final public function getAppId(): string { return $this->appId; } /** * @return string|null */ - public final function getUserId(): ?string { + final public function getUserId(): ?string { return $this->userId; } @@ -89,7 +88,7 @@ public function jsonSerialize() { } - public final static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { + final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); $task->setId($taskEntity->getId()); $task->setStatus($taskEntity->getStatus()); @@ -104,10 +103,10 @@ public final static function fromTaskEntity(Task $taskEntity): ILanguageModelTas * @return ILanguageModelTask * @throws \InvalidArgumentException */ - public final static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { + final public static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { if (!in_array($type, self::TYPES)) { throw new \InvalidArgumentException('Unknown task type'); } - return new ILanguageModelTask::TYPES[$type]($input, $userId, $appId); + return new (ILanguageModelTask::TYPES[$type])($input, $userId, $appId); } } From dbd9420ebebef418bd985a7d325829ae8802b795 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 15:45:18 +0200 Subject: [PATCH 011/100] LLM OCP API: Fix type errors Signed-off-by: Marcel Klehr (cherry picked from commit b6a95e35b0583c2dfc87bf80067c1bdcd51431ca) --- .../LanguageModel/AbstractLanguageModelTask.php | 15 +++++++++++++++ lib/public/LanguageModel/ILanguageModelTask.php | 13 +++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index f1834cdf05884..ff54277c840c0 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -6,6 +6,7 @@ abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id; + protected ?string $output; protected int $status = ILanguageModelTask::STATUS_UNKNOWN; final public function __construct( @@ -26,6 +27,20 @@ abstract public function canUseProvider(ILanguageModelProvider $provider): bool; abstract public function getType(): string; + /** + * @return string|null + */ + final public function getOutput(): ?string { + return $this->output; + } + + /** + * @param string|null $output + */ + final public function setOutput(?string $output): void { + $this->output = $output; + } + /** * @return int */ diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 3775546d4f0d3..816dcf7dc1cd6 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -16,6 +16,11 @@ interface ILanguageModelTask extends \JsonSerializable { TopicsTask::TYPE => TopicsTask::class, ]; + public function visitProvider(ILanguageModelProvider $provider): string; + + public function canUseProvider(ILanguageModelProvider $provider): bool; + + /** * @return string */ @@ -47,14 +52,14 @@ public function getId(): ?int; public function getInput(): string; /** - * @param string $output + * @param string|null $output */ - public function setOutput(string $output): void; + public function setOutput(?string $output): void; /** - * @return string + * @return null|string */ - public function getOutput(): string; + public function getOutput(): ?string; /** * @return string From 0024f0d1cb12421ec6fd59b01875c8b2045ad657 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 17:30:07 +0200 Subject: [PATCH 012/100] LLM OCP API: Add @since Signed-off-by: Marcel Klehr (cherry picked from commit 9935034480eabc8d6ece586f7af0a11117f516f4) --- .../AbstractLanguageModelTask.php | 38 +++++++++++++++++ .../LanguageModel/Events/TaskFailedEvent.php | 1 + lib/public/LanguageModel/FreePromptTask.php | 10 +++-- lib/public/LanguageModel/HeadlineTask.php | 6 +++ .../LanguageModel/ILanguageModelManager.php | 1 + .../LanguageModel/ILanguageModelTask.php | 41 +++++++++++++++++++ 6 files changed, 94 insertions(+), 3 deletions(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index ff54277c840c0..05503f4d95cb3 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -4,11 +4,20 @@ use OC\LanguageModel\Db\Task; +/** + * @since 28.0.0 + */ abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id; protected ?string $output; protected int $status = ILanguageModelTask::STATUS_UNKNOWN; + /** + * @param string $input + * @param string $appId + * @param string|null $userId + * @since 28.0.0 + */ final public function __construct( protected string $input, protected string $appId, @@ -20,15 +29,26 @@ final public function __construct( * @param ILanguageModelProvider $provider * @return string * @throws \RuntimeException + * @since 28.0.0 */ abstract public function visitProvider(ILanguageModelProvider $provider): string; + /** + * @param ILanguageModelProvider $provider + * @return bool + * @since 28.0.0 + */ abstract public function canUseProvider(ILanguageModelProvider $provider): bool; + /** + * @return string + * @since 28.0.0 + */ abstract public function getType(): string; /** * @return string|null + * @since 28.0.0 */ final public function getOutput(): ?string { return $this->output; @@ -36,6 +56,7 @@ final public function getOutput(): ?string { /** * @param string|null $output + * @since 28.0.0 */ final public function setOutput(?string $output): void { $this->output = $output; @@ -43,6 +64,7 @@ final public function setOutput(?string $output): void { /** * @return int + * @since 28.0.0 */ final public function getStatus(): int { return $this->status; @@ -50,6 +72,7 @@ final public function getStatus(): int { /** * @param int $status + * @since 28.0.0 */ final public function setStatus(int $status): void { $this->status = $status; @@ -57,6 +80,7 @@ final public function setStatus(int $status): void { /** * @return int|null + * @since 28.0.0 */ final public function getId(): ?int { return $this->id; @@ -64,6 +88,7 @@ final public function getId(): ?int { /** * @param int|null $id + * @since 28.0.0 */ final public function setId(?int $id): void { $this->id = $id; @@ -71,6 +96,7 @@ final public function setId(?int $id): void { /** * @return string + * @since 28.0.0 */ final public function getInput(): string { return $this->input; @@ -78,6 +104,7 @@ final public function getInput(): string { /** * @return string + * @since 28.0.0 */ final public function getAppId(): string { return $this->appId; @@ -85,11 +112,16 @@ final public function getAppId(): string { /** * @return string|null + * @since 28.0.0 */ final public function getUserId(): ?string { return $this->userId; } + /** + * @return array + * @since 28.0.0 + */ public function jsonSerialize() { return [ 'id' => $this->getId(), @@ -103,6 +135,11 @@ public function jsonSerialize() { } + /** + * @param Task $taskEntity + * @return ILanguageModelTask + * @since 28.0.0 + */ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); $task->setId($taskEntity->getId()); @@ -117,6 +154,7 @@ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTas * @param string $appId * @return ILanguageModelTask * @throws \InvalidArgumentException + * @since 28.0.0 */ final public static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { if (!in_array($type, self::TYPES)) { diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index 11e7fdfa23d21..5425368d57d6d 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -15,6 +15,7 @@ public function __construct(ILanguageModelTask $task, /** * @return string + * @since 28.0.0 */ public function getErrorMessage(): string { return $this->errorMessage; diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index a179048631c8c..c577d9c653b96 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -4,13 +4,17 @@ use RuntimeException; +/** + * @since 28.0.0 + */ final class FreePromptTask extends AbstractLanguageModelTask { + /** + * @since 28.0.0 + */ public const TYPE = 'free_prompt'; /** - * @param ILanguageModelProvider $provider - * @throws RuntimeException - * @return string + * @inheritDoc */ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->prompt($this->getInput()); diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index 003488b68419c..283af6657e886 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -4,7 +4,13 @@ use RuntimeException; +/** + * @since 28.0.0 + */ final class HeadlineTask extends AbstractLanguageModelTask { + /** + * @since 28.0.0 + */ public const TYPE = 'headline'; /** diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index f7864a68a8eda..5e3f350df66b2 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -68,6 +68,7 @@ public function scheduleTask(ILanguageModelTask $task) : void; * @return ILanguageModelTask * @throws RuntimeException If the query failed * @throws \ValueError If the task could not be found + * @since 28.0.0 */ public function getTask(int $id): ILanguageModelTask; } diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 816dcf7dc1cd6..d0d7f825ffbb6 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -2,13 +2,34 @@ namespace OCP\LanguageModel; +/** + * @since 28.0.0 + */ interface ILanguageModelTask extends \JsonSerializable { + /** + * @since 28.0.0 + */ public const STATUS_FAILED = 4; + /** + * @since 28.0.0 + */ public const STATUS_SUCCESSFUL = 3; + /** + * @since 28.0.0 + */ public const STATUS_RUNNING = 2; + /** + * @since 28.0.0 + */ public const STATUS_SCHEDULED = 1; + /** + * @since 28.0.0 + */ public const STATUS_UNKNOWN = 0; + /** + * @since 28.0.0 + */ public const TYPES = [ FreePromptTask::TYPE => FreePromptTask::class, SummaryTask::TYPE => SummaryTask::class, @@ -16,58 +37,78 @@ interface ILanguageModelTask extends \JsonSerializable { TopicsTask::TYPE => TopicsTask::class, ]; + /** + * @param ILanguageModelProvider $provider + * @return string + * @since 28.0.0 + */ public function visitProvider(ILanguageModelProvider $provider): string; + /** + * @param ILanguageModelProvider $provider + * @return bool + * @since 28.0.0 + */ public function canUseProvider(ILanguageModelProvider $provider): bool; /** * @return string + * @since 28.0.0 */ public function getType(): string; /** * @return int + * @since 28.0.0 */ public function getStatus(): int; /** * @param int $status + * @since 28.0.0 */ public function setStatus(int $status): void; /** * @param int|null $id + * @since 28.0.0 */ public function setId(?int $id): void; /** * @return int|null + * @since 28.0.0 */ public function getId(): ?int; /** * @return string + * @since 28.0.0 */ public function getInput(): string; /** * @param string|null $output + * @since 28.0.0 */ public function setOutput(?string $output): void; /** * @return null|string + * @since 28.0.0 */ public function getOutput(): ?string; /** * @return string + * @since 28.0.0 */ public function getAppId(): string; /** * @return string|null + * @since 28.0.0 */ public function getUserId(): ?string; } From 02c78be7902bff1806816776edbbc59473b11b3e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 17:50:56 +0200 Subject: [PATCH 013/100] LLM OCP API: Fix static analysis Signed-off-by: Marcel Klehr (cherry picked from commit fac83ce4b66cde708c720284f7102090ac99d540) --- .../LanguageModel/LanguageModelManager.php | 5 +++-- lib/public/LanguageModel/FreePromptTask.php | 6 ++++++ lib/public/LanguageModel/SummaryTask.php | 16 +++++++++++++--- lib/public/LanguageModel/TopicsTask.php | 16 +++++++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 14b14397c09f0..09bf9ae5d154e 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -110,10 +110,11 @@ public function runTask(ILanguageModelTask $task): string { try { $task->setStatus(ILanguageModelTask::STATUS_RUNNING); $this->taskMapper->update(Task::fromLanguageModelTask($task)); - $task->setOutput($task->visitProvider($provider)); + $output = $task->visitProvider($provider); + $task->setOutput($output); $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); $this->taskMapper->update(Task::fromLanguageModelTask($task)); - return $task->getOutput(); + return $output; } catch (\RuntimeException $e) { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); $task->setStatus(ILanguageModelTask::STATUS_FAILED); diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index c577d9c653b96..75d8f1a0da205 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -20,10 +20,16 @@ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->prompt($this->getInput()); } + /** + * @inheritDoc + */ public function canUseProvider(ILanguageModelProvider $provider): bool { return true; } + /** + * @inheritDoc + */ public function getType(): string { return self::TYPE; } diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 3c300246f03bc..0f7a96287c3cc 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -4,13 +4,17 @@ use RuntimeException; +/** + * @since 28.0.0 + */ final class SummaryTask extends AbstractLanguageModelTask { + /** + * @since 28.0.0 + */ public const TYPE = 'summarize'; /** - * @param ILanguageModelProvider $provider - * @throws RuntimeException - * @return string + * @inheritDoc */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof ISummaryProvider) { @@ -19,10 +23,16 @@ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->summarize($this->getInput()); } + /** + * @inheritDoc + */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ISummaryProvider; } + /** + * @inheritDoc + */ public function getType(): string { return self::TYPE; } diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index 98ffabfc815ab..f704afdcfc0f7 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -4,13 +4,17 @@ use RuntimeException; +/** + * @since 28.0.0 + */ final class TopicsTask extends AbstractLanguageModelTask { + /** + * @since 28.0.0 + */ public const TYPE = 'topics'; /** - * @param ILanguageModelProvider $provider - * @throws RuntimeException - * @return string + * @inheritDoc */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof ITopicsProvider) { @@ -19,10 +23,16 @@ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->findTopics($this->getInput()); } + /** + * @inheritDoc + */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ITopicsProvider; } + /** + * @inheritDoc + */ public function getType(): string { return self::TYPE; } From 612af73d92a25c9fbc6a89681172c4ead5f37725 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 18:02:53 +0200 Subject: [PATCH 014/100] LLM OCP API: cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit 75d7af86acfbe283e99f6f690f6bf4c948afc6a5) --- lib/public/LanguageModel/FreePromptTask.php | 2 -- lib/public/LanguageModel/SummaryTask.php | 2 -- lib/public/LanguageModel/TopicsTask.php | 2 -- 3 files changed, 6 deletions(-) diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index 75d8f1a0da205..180d778b34cd0 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -2,8 +2,6 @@ namespace OCP\LanguageModel; -use RuntimeException; - /** * @since 28.0.0 */ diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 0f7a96287c3cc..e454404be9214 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -2,8 +2,6 @@ namespace OCP\LanguageModel; -use RuntimeException; - /** * @since 28.0.0 */ diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index f704afdcfc0f7..114c3c0ab0679 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -2,8 +2,6 @@ namespace OCP\LanguageModel; -use RuntimeException; - /** * @since 28.0.0 */ From dd7eafaa48842857b95edfb24f27f077bf58f200 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 20 Jun 2023 18:26:29 +0200 Subject: [PATCH 015/100] LLM OCP API: Fix coding style and psalm Signed-off-by: Marcel Klehr (cherry picked from commit 72ea76178a694b302ece8a0752bbf5a22afc637a) --- .../LanguageModel/Events/TaskFailedEvent.php | 5 +++++ .../LanguageModel/Events/TaskSuccessfulEvent.php | 4 ++++ lib/public/LanguageModel/FreePromptTask.php | 3 +++ lib/public/LanguageModel/HeadlineTask.php | 15 ++++++++++----- .../LanguageModel/ILanguageModelManager.php | 3 +++ lib/public/LanguageModel/SummaryTask.php | 3 +++ lib/public/LanguageModel/TopicsTask.php | 3 +++ 7 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index 5425368d57d6d..efc7d9043c8e3 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -8,6 +8,11 @@ * @since 28.0.0 */ class TaskFailedEvent extends AbstractLanguageModelEvent { + /** + * @param ILanguageModelTask $task + * @param string $errorMessage + * @since 28.0.0 + */ public function __construct(ILanguageModelTask $task, private string $errorMessage) { parent::__construct($task); diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php index 35b5a043215f2..ec8a8586a3ec1 100644 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -8,6 +8,10 @@ * @since 28.0.0 */ class TaskSuccessfulEvent extends AbstractLanguageModelEvent { + /** + * @param ILanguageModelTask $task + * @since 28.0.0 + */ public function __construct(ILanguageModelTask $task) { parent::__construct($task); } diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index 180d778b34cd0..02eb6803276ed 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -13,6 +13,7 @@ final class FreePromptTask extends AbstractLanguageModelTask { /** * @inheritDoc + * @since 28.0.0 */ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->prompt($this->getInput()); @@ -20,6 +21,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc + * @since 28.0.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return true; @@ -27,6 +29,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc + * @since 28.0.0 */ public function getType(): string { return self::TYPE; diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index 283af6657e886..fadaea0b92d4c 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -2,8 +2,6 @@ namespace OCP\LanguageModel; -use RuntimeException; - /** * @since 28.0.0 */ @@ -14,9 +12,8 @@ final class HeadlineTask extends AbstractLanguageModelTask { public const TYPE = 'headline'; /** - * @param ILanguageModelProvider $provider - * @throws RuntimeException - * @return string + * @inheritDoc + * @since 28.0.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof IHeadlineProvider) { @@ -25,10 +22,18 @@ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->findHeadline($this->getInput()); } + /** + * @inheritDoc + * @since 28.0.0 + */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof IHeadlineProvider; } + /** + * @inheritDoc + * @since 28.0.0 + */ public function getType(): string { return self::TYPE; } diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 5e3f350df66b2..80546149a6206 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -29,6 +29,9 @@ use OCP\PreConditionNotMetException; use RuntimeException; +/** + * @since 28.0.0 + */ interface ILanguageModelManager { /** * @since 28.0.0 diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index e454404be9214..b5a3bd25c4fc2 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -13,6 +13,7 @@ final class SummaryTask extends AbstractLanguageModelTask { /** * @inheritDoc + * @since 28.0.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof ISummaryProvider) { @@ -23,6 +24,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc + * @since 28.0.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ISummaryProvider; @@ -30,6 +32,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc + * @since 28.0.0 */ public function getType(): string { return self::TYPE; diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index 114c3c0ab0679..a7a296033622f 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -13,6 +13,7 @@ final class TopicsTask extends AbstractLanguageModelTask { /** * @inheritDoc + * @since 28.0.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$provider instanceof ITopicsProvider) { @@ -23,6 +24,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc + * @since 28.0.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ITopicsProvider; @@ -30,6 +32,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc + * @since 28.0.0 */ public function getType(): string { return self::TYPE; From 6320c68c3bea049fafc17cc3d5ee8532c8ca933e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 26 Jun 2023 11:39:48 +0200 Subject: [PATCH 016/100] Update lib/public/LanguageModel/ITopicsProvider.php Co-authored-by: Julien Veyssier Signed-off-by: Marcel Klehr (cherry picked from commit 249dd5cce44b7d226ebd94c58701034123e8473d) --- lib/public/LanguageModel/ITopicsProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/LanguageModel/ITopicsProvider.php index 2e3983819693f..bc5f7d8581149 100644 --- a/lib/public/LanguageModel/ITopicsProvider.php +++ b/lib/public/LanguageModel/ITopicsProvider.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Marcel Klehr + * @copyright Copyright (c) 2023 Marcel Klehr * * @author Marcel Klehr * From ecda18b51597be26c0d8fe83e9b3087577b0c990 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 26 Jun 2023 11:45:54 +0200 Subject: [PATCH 017/100] LLM OCP API: Add task definitions Signed-off-by: Marcel Klehr (cherry picked from commit fb4de16c113143022e195ee41f87c42c16aa09ea) --- lib/public/LanguageModel/HeadlineTask.php | 2 ++ lib/public/LanguageModel/IHeadlineProvider.php | 2 ++ lib/public/LanguageModel/ISummaryProvider.php | 2 ++ lib/public/LanguageModel/ITopicsProvider.php | 2 ++ lib/public/LanguageModel/SummaryTask.php | 2 ++ lib/public/LanguageModel/TopicsTask.php | 2 ++ 6 files changed, 12 insertions(+) diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index fadaea0b92d4c..92abc551cc6fb 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -3,6 +3,8 @@ namespace OCP\LanguageModel; /** + * This LanguageModel Task represents headline generation + * which generates a headline for the passed text * @since 28.0.0 */ final class HeadlineTask extends AbstractLanguageModelTask { diff --git a/lib/public/LanguageModel/IHeadlineProvider.php b/lib/public/LanguageModel/IHeadlineProvider.php index b2e902a034df2..d1fe7504fdfb7 100644 --- a/lib/public/LanguageModel/IHeadlineProvider.php +++ b/lib/public/LanguageModel/IHeadlineProvider.php @@ -29,6 +29,8 @@ use RuntimeException; /** + * This LanguageModel Provider represents headline generation + * which generates a headline for the passed text * @since 28.0.0 */ interface IHeadlineProvider extends ILanguageModelProvider { diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php index af897e1a8c3bd..0b49258964dc5 100644 --- a/lib/public/LanguageModel/ISummaryProvider.php +++ b/lib/public/LanguageModel/ISummaryProvider.php @@ -29,6 +29,8 @@ use RuntimeException; /** + * This LanguageModel Provider implements summarization + * which sums up the passed text. * @since 28.0.0 */ interface ISummaryProvider extends ILanguageModelProvider { diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/LanguageModel/ITopicsProvider.php index bc5f7d8581149..a15cfba698fd4 100644 --- a/lib/public/LanguageModel/ITopicsProvider.php +++ b/lib/public/LanguageModel/ITopicsProvider.php @@ -29,6 +29,8 @@ use RuntimeException; /** + * This LanguageModel Provider implements topics synthesis + * which outputs comma-separated topics for the passed text * @since 28.0.0 */ interface ITopicsProvider extends ILanguageModelProvider { diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index b5a3bd25c4fc2..ffc7aa01e7158 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -3,6 +3,8 @@ namespace OCP\LanguageModel; /** + * This LanguageModel Task represents summarization + * which sums up the passed text. * @since 28.0.0 */ final class SummaryTask extends AbstractLanguageModelTask { diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index a7a296033622f..a628695a59a36 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -3,6 +3,8 @@ namespace OCP\LanguageModel; /** + * This LanguageModel Task represents topics synthesis + * which outputs comma-separated topics for the passed text * @since 28.0.0 */ final class TopicsTask extends AbstractLanguageModelTask { From 1ac86d2c624153d67c28f4a541966f9ea9119cee Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 26 Jun 2023 11:51:33 +0200 Subject: [PATCH 018/100] LLM OCP API: Commit autoloaders Signed-off-by: Marcel Klehr (cherry picked from commit 5bc6180b38baca1c78eea250d77a65103983e30c) --- lib/composer/composer/autoload_classmap.php | 20 ++++++++++++++++++++ lib/composer/composer/autoload_static.php | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index ba3edcad4f237..f99bb5f440bf2 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -485,6 +485,20 @@ 'OCP\\LDAP\\IDeletionFlagSupport' => $baseDir . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => $baseDir . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => $baseDir . '/lib/public/LDAP/ILDAPProviderFactory.php', + 'OCP\\LanguageModel\\AbstractLanguageModelTask' => $baseDir . '/lib/public/LanguageModel/AbstractLanguageModelTask.php', + 'OCP\\LanguageModel\\Events\\AbstractLanguageModelEvent' => $baseDir . '/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php', + 'OCP\\LanguageModel\\Events\\TaskFailedEvent' => $baseDir . '/lib/public/LanguageModel/Events/TaskFailedEvent.php', + 'OCP\\LanguageModel\\Events\\TaskSuccessfulEvent' => $baseDir . '/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php', + 'OCP\\LanguageModel\\FreePromptTask' => $baseDir . '/lib/public/LanguageModel/FreePromptTask.php', + 'OCP\\LanguageModel\\HeadlineTask' => $baseDir . '/lib/public/LanguageModel/HeadlineTask.php', + 'OCP\\LanguageModel\\IHeadlineProvider' => $baseDir . '/lib/public/LanguageModel/IHeadlineProvider.php', + 'OCP\\LanguageModel\\ILanguageModelManager' => $baseDir . '/lib/public/LanguageModel/ILanguageModelManager.php', + 'OCP\\LanguageModel\\ILanguageModelProvider' => $baseDir . '/lib/public/LanguageModel/ILanguageModelProvider.php', + 'OCP\\LanguageModel\\ILanguageModelTask' => $baseDir . '/lib/public/LanguageModel/ILanguageModelTask.php', + 'OCP\\LanguageModel\\ISummaryProvider' => $baseDir . '/lib/public/LanguageModel/ISummaryProvider.php', + 'OCP\\LanguageModel\\ITopicsProvider' => $baseDir . '/lib/public/LanguageModel/ITopicsProvider.php', + 'OCP\\LanguageModel\\SummaryTask' => $baseDir . '/lib/public/LanguageModel/SummaryTask.php', + 'OCP\\LanguageModel\\TopicsTask' => $baseDir . '/lib/public/LanguageModel/TopicsTask.php', 'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => $baseDir . '/lib/public/Lock/ManuallyLockedException.php', @@ -1027,6 +1041,7 @@ 'OC\\Core\\Controller\\GuestAvatarController' => $baseDir . '/core/Controller/GuestAvatarController.php', 'OC\\Core\\Controller\\HoverCardController' => $baseDir . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php', + 'OC\\Core\\Controller\\LanguageModelApiController' => $baseDir . '/core/Controller/LanguageModelApiController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\NavigationController' => $baseDir . '/core/Controller/NavigationController.php', @@ -1118,6 +1133,7 @@ 'OC\\Core\\Migrations\\Version27000Date20220613163520' => $baseDir . '/core/Migrations/Version27000Date20220613163520.php', 'OC\\Core\\Migrations\\Version27000Date20230309104325' => $baseDir . '/core/Migrations/Version27000Date20230309104325.php', 'OC\\Core\\Migrations\\Version27000Date20230309104802' => $baseDir . '/core/Migrations/Version27000Date20230309104802.php', + 'OC\\Core\\Migrations\\Version28000Date20230616104802' => $baseDir . '/core/Migrations/Version28000Date20230616104802.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', @@ -1360,6 +1376,10 @@ 'OC\\L10N\\LanguageNotFoundException' => $baseDir . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\L10N\\LazyL10N' => $baseDir . '/lib/private/L10N/LazyL10N.php', 'OC\\LDAP\\NullLDAPProviderFactory' => $baseDir . '/lib/private/LDAP/NullLDAPProviderFactory.php', + 'OC\\LanguageModel\\Db\\Task' => $baseDir . '/lib/private/LanguageModel/Db/Task.php', + 'OC\\LanguageModel\\Db\\TaskMapper' => $baseDir . '/lib/private/LanguageModel/Db/TaskMapper.php', + 'OC\\LanguageModel\\LanguageModelManager' => $baseDir . '/lib/private/LanguageModel/LanguageModelManager.php', + 'OC\\LanguageModel\\TaskBackgroundJob' => $baseDir . '/lib/private/LanguageModel/TaskBackgroundJob.php', 'OC\\LargeFileHelper' => $baseDir . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => $baseDir . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => $baseDir . '/lib/private/Lock/DBLockingProvider.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index b28fd124109d1..2b13d4532c6c1 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -518,6 +518,20 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\LDAP\\IDeletionFlagSupport' => __DIR__ . '/../../..' . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProviderFactory.php', + 'OCP\\LanguageModel\\AbstractLanguageModelTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/AbstractLanguageModelTask.php', + 'OCP\\LanguageModel\\Events\\AbstractLanguageModelEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php', + 'OCP\\LanguageModel\\Events\\TaskFailedEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/TaskFailedEvent.php', + 'OCP\\LanguageModel\\Events\\TaskSuccessfulEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php', + 'OCP\\LanguageModel\\FreePromptTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/FreePromptTask.php', + 'OCP\\LanguageModel\\HeadlineTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/HeadlineTask.php', + 'OCP\\LanguageModel\\IHeadlineProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/IHeadlineProvider.php', + 'OCP\\LanguageModel\\ILanguageModelManager' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelManager.php', + 'OCP\\LanguageModel\\ILanguageModelProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelProvider.php', + 'OCP\\LanguageModel\\ILanguageModelTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelTask.php', + 'OCP\\LanguageModel\\ISummaryProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ISummaryProvider.php', + 'OCP\\LanguageModel\\ITopicsProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ITopicsProvider.php', + 'OCP\\LanguageModel\\SummaryTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/SummaryTask.php', + 'OCP\\LanguageModel\\TopicsTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/TopicsTask.php', 'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/ManuallyLockedException.php', @@ -1060,6 +1074,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Controller\\GuestAvatarController' => __DIR__ . '/../../..' . '/core/Controller/GuestAvatarController.php', 'OC\\Core\\Controller\\HoverCardController' => __DIR__ . '/../../..' . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php', + 'OC\\Core\\Controller\\LanguageModelApiController' => __DIR__ . '/../../..' . '/core/Controller/LanguageModelApiController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\NavigationController' => __DIR__ . '/../../..' . '/core/Controller/NavigationController.php', @@ -1151,6 +1166,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version27000Date20220613163520' => __DIR__ . '/../../..' . '/core/Migrations/Version27000Date20220613163520.php', 'OC\\Core\\Migrations\\Version27000Date20230309104325' => __DIR__ . '/../../..' . '/core/Migrations/Version27000Date20230309104325.php', 'OC\\Core\\Migrations\\Version27000Date20230309104802' => __DIR__ . '/../../..' . '/core/Migrations/Version27000Date20230309104802.php', + 'OC\\Core\\Migrations\\Version28000Date20230616104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230616104802.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', @@ -1393,6 +1409,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\L10N\\LanguageNotFoundException' => __DIR__ . '/../../..' . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\L10N\\LazyL10N' => __DIR__ . '/../../..' . '/lib/private/L10N/LazyL10N.php', 'OC\\LDAP\\NullLDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/private/LDAP/NullLDAPProviderFactory.php', + 'OC\\LanguageModel\\Db\\Task' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/Db/Task.php', + 'OC\\LanguageModel\\Db\\TaskMapper' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/Db/TaskMapper.php', + 'OC\\LanguageModel\\LanguageModelManager' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/LanguageModelManager.php', + 'OC\\LanguageModel\\TaskBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/TaskBackgroundJob.php', 'OC\\LargeFileHelper' => __DIR__ . '/../../..' . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/DBLockingProvider.php', From 4e3ca11fee166a4b6bc2704badac6e4d207f61ca Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 11:10:44 +0200 Subject: [PATCH 019/100] Update lib/public/AppFramework/Bootstrap/IRegistrationContext.php Co-authored-by: Daniel Signed-off-by: Marcel Klehr (cherry picked from commit fb55afc9ffea906432e52def8bacb6f902afe220) --- lib/public/AppFramework/Bootstrap/IRegistrationContext.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 19ef6832a2c2c..a4b874b754517 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -226,7 +226,7 @@ public function registerSpeechToTextProvider(string $providerClass): void; * * @param string $providerClass * @psalm-param class-string $providerClass - * @since 27.0.0 + * @since 28.0.0 */ public function registerLanguageModelProvider(string $providerClass): void; From 44050274715bc389ed5cde3d8ac58dd241da7865 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 14:08:05 +0200 Subject: [PATCH 020/100] Apply suggestions from code review Co-authored-by: Daniel Signed-off-by: Marcel Klehr (cherry picked from commit 8e4aa92167c4856dac42bae1ae4d8f5d653a2735) --- lib/private/LanguageModel/LanguageModelManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 09bf9ae5d154e..340116e7d719c 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -68,7 +68,7 @@ public function hasProviders(): bool { if ($context === null) { return false; } - return !empty($context->getSpeechToTextProviders()); + return count($context->getSpeechToTextProviders()) > 0; } /** @@ -93,7 +93,7 @@ public function getAvailableTaskTypes(): array { } public function canHandleTask(ILanguageModelTask $task): bool { - return !empty(array_filter($this->getAvailableTasks(), fn ($class) => $task instanceof $class)); + return count(array_filter($this->getAvailableTasks(), fn ($class) => $task instanceof $class)) > 0; } /** From c8bbe8fc75f24262573f9cafee533701680fb1e1 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:43:54 +0200 Subject: [PATCH 021/100] LLM OCP API: Type shenanigans for Visitor pattern Signed-off-by: Marcel Klehr (cherry picked from commit 906e9b7014203ac4e7608155287efe793a83aa8e) --- .../AbstractLanguageModelTask.php | 21 +++++-------------- lib/public/LanguageModel/FreePromptTask.php | 5 +++-- lib/public/LanguageModel/HeadlineTask.php | 9 ++++---- .../LanguageModel/ILanguageModelProvider.php | 2 ++ .../LanguageModel/ILanguageModelTask.php | 9 ++++---- lib/public/LanguageModel/SummaryTask.php | 9 ++++---- lib/public/LanguageModel/TopicsTask.php | 9 ++++---- 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 05503f4d95cb3..9621b3d490555 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -5,7 +5,11 @@ use OC\LanguageModel\Db\Task; /** - * @since 28.0.0 + * This is an abstract LanguageModel task that implements basic + * goodies for downstream tasks + * @since 28.0. + * @template T of ILanguageModelProvider + * @template-implements ILanguageModelTask */ abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id; @@ -25,21 +29,6 @@ final public function __construct( ) { } - /** - * @param ILanguageModelProvider $provider - * @return string - * @throws \RuntimeException - * @since 28.0.0 - */ - abstract public function visitProvider(ILanguageModelProvider $provider): string; - - /** - * @param ILanguageModelProvider $provider - * @return bool - * @since 28.0.0 - */ - abstract public function canUseProvider(ILanguageModelProvider $provider): bool; - /** * @return string * @since 28.0.0 diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index 02eb6803276ed..1471e13eae20e 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -4,6 +4,7 @@ /** * @since 28.0.0 + * @template-extends AbstractLanguageModelTask */ final class FreePromptTask extends AbstractLanguageModelTask { /** @@ -15,7 +16,7 @@ final class FreePromptTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider(ILanguageModelProvider $provider): string { + public function visitProvider($provider): string { return $provider->prompt($this->getInput()); } @@ -23,7 +24,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool { + public function canUseProvider($provider): bool { return true; } diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index 92abc551cc6fb..1f3680b311661 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -6,6 +6,7 @@ * This LanguageModel Task represents headline generation * which generates a headline for the passed text * @since 28.0.0 + * @template-extends AbstractLanguageModelTask */ final class HeadlineTask extends AbstractLanguageModelTask { /** @@ -17,9 +18,9 @@ final class HeadlineTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$provider instanceof IHeadlineProvider) { - throw new \RuntimeException('SummaryTask#visitProvider expects IHeadlineProvider'); + public function visitProvider($provider): string { + if (!$this->canUseProvider($provider)) { + throw new \RuntimeException('HeadlineTask#visitProvider expects IHeadlineProvider'); } return $provider->findHeadline($this->getInput()); } @@ -28,7 +29,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool { + public function canUseProvider($provider): bool { return $provider instanceof IHeadlineProvider; } diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/LanguageModel/ILanguageModelProvider.php index f44ee64f15169..56c20d1e7cca8 100644 --- a/lib/public/LanguageModel/ILanguageModelProvider.php +++ b/lib/public/LanguageModel/ILanguageModelProvider.php @@ -29,6 +29,8 @@ use RuntimeException; /** + * This is the minimum interface is implemented by apps that + * implement a LanguageModel provider * @since 28.0.0 */ interface ILanguageModelProvider { diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index d0d7f825ffbb6..1bb06c259422c 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -4,6 +4,7 @@ /** * @since 28.0.0 + * @template T of ILanguageModelProvider */ interface ILanguageModelTask extends \JsonSerializable { /** @@ -38,18 +39,18 @@ interface ILanguageModelTask extends \JsonSerializable { ]; /** - * @param ILanguageModelProvider $provider + * @param T $provider * @return string * @since 28.0.0 */ - public function visitProvider(ILanguageModelProvider $provider): string; + public function visitProvider($provider): string; /** - * @param ILanguageModelProvider $provider + * @param T $provider * @return bool * @since 28.0.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool; + public function canUseProvider($provider): bool; /** diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index ffc7aa01e7158..5bb158f00ddb9 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -3,9 +3,10 @@ namespace OCP\LanguageModel; /** - * This LanguageModel Task represents summarization + * This is an absctract LanguageModel Task represents summarization * which sums up the passed text. * @since 28.0.0 + * @template-extends AbstractLanguageModelTask */ final class SummaryTask extends AbstractLanguageModelTask { /** @@ -17,8 +18,8 @@ final class SummaryTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$provider instanceof ISummaryProvider) { + public function visitProvider($provider): string { + if (!$this->canUseProvider($provider)) { throw new \RuntimeException('SummaryTask#visitProvider expects ISummaryProvider'); } return $provider->summarize($this->getInput()); @@ -28,7 +29,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool { + public function canUseProvider($provider): bool { return $provider instanceof ISummaryProvider; } diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index a628695a59a36..41571fb7e2119 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -6,6 +6,7 @@ * This LanguageModel Task represents topics synthesis * which outputs comma-separated topics for the passed text * @since 28.0.0 + * @template-extends AbstractLanguageModelTask */ final class TopicsTask extends AbstractLanguageModelTask { /** @@ -17,9 +18,9 @@ final class TopicsTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$provider instanceof ITopicsProvider) { - throw new \RuntimeException('SummaryTask#visitProvider expects IHeadlineProvider'); + public function visitProvider($provider): string { + if (!$this->canUseProvider($provider)) { + throw new \RuntimeException('TopicsTask#visitProvider expects ITopicsProvider'); } return $provider->findTopics($this->getInput()); } @@ -28,7 +29,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool { + public function canUseProvider($provider): bool { return $provider instanceof ITopicsProvider; } From ddeaaeace08e866ddaa5c1ab57aac75adc4d9ff4 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:44:15 +0200 Subject: [PATCH 022/100] LLM OCP API: strict types and copyright Signed-off-by: Marcel Klehr (cherry picked from commit 83db23ea4852dc86f6d6bcbb0ad1771665875528) --- .../AbstractLanguageModelTask.php | 22 +++++++++++++++++++ lib/public/LanguageModel/FreePromptTask.php | 22 +++++++++++++++++++ lib/public/LanguageModel/HeadlineTask.php | 22 +++++++++++++++++++ .../LanguageModel/ILanguageModelProvider.php | 2 +- .../LanguageModel/ILanguageModelTask.php | 22 +++++++++++++++++++ lib/public/LanguageModel/SummaryTask.php | 22 +++++++++++++++++++ lib/public/LanguageModel/TopicsTask.php | 22 +++++++++++++++++++ 7 files changed, 133 insertions(+), 1 deletion(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 9621b3d490555..b7edcf33be858 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index 1471e13eae20e..6261e21eef7ab 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index 1f3680b311661..c702f3a713079 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/LanguageModel/ILanguageModelProvider.php index 56c20d1e7cca8..512fd192aa3df 100644 --- a/lib/public/LanguageModel/ILanguageModelProvider.php +++ b/lib/public/LanguageModel/ILanguageModelProvider.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Marcel Klehr + * @copyright Copyright (c) 2023 Marcel Klehr * * @author Marcel Klehr * diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 1bb06c259422c..a7a275682e18b 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 5bb158f00ddb9..a7cb04cbadb22 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index 41571fb7e2119..833afda3fa34d 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -1,4 +1,26 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ namespace OCP\LanguageModel; From d203e3ad8db168c705dfee9b6bc46e0ff9c12d34 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:51:30 +0200 Subject: [PATCH 023/100] OCP: Introduce OCP\Common\Exception\NotFoundException Signed-off-by: Marcel Klehr (cherry picked from commit a7cd6bf5b80d1974d8e680fd5da9b7f3af28ffa6) --- .../Common/Exception/NotFoundException.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 lib/public/Common/Exception/NotFoundException.php diff --git a/lib/public/Common/Exception/NotFoundException.php b/lib/public/Common/Exception/NotFoundException.php new file mode 100644 index 0000000000000..309efe3e22062 --- /dev/null +++ b/lib/public/Common/Exception/NotFoundException.php @@ -0,0 +1,41 @@ + + * + * @author Marcel Klehr + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCP\Common\Exception; + +/** + * This is thrown whenever something was expected to exist but doesn't + * + * @since 28.0.0 + */ +class NotFoundException extends \Exception { + /** + * Constructor + * @param string $msg the error message + * @since 28.0.0 + */ + public function __construct($msg) { + parent::__construct($msg); + } +} From fb9262e8644e68f6cb4df9be95f0306ab5f43672 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:51:41 +0200 Subject: [PATCH 024/100] LLM OCP API: Use OCP\Common\Exception\NotFoundException Signed-off-by: Marcel Klehr (cherry picked from commit b00a9a6eaeeafce11e0141199f37d8a105050cce) --- core/Controller/LanguageModelApiController.php | 3 ++- lib/private/LanguageModel/LanguageModelManager.php | 5 +++-- lib/public/LanguageModel/ILanguageModelManager.php | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 5699dd7552673..a4f83cc1b1151 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -29,6 +29,7 @@ use InvalidArgumentException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\Common\Exception\NotFoundException; use OCP\IL10N; use OCP\IRequest; use OCP\LanguageModel\AbstractLanguageModelTask; @@ -87,7 +88,7 @@ public function getTask(int $id): DataResponse { return new DataResponse([ 'task' => $task, ]); - } catch (\ValueError $e) { + } catch (NotFoundException $e) { return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); } catch (\RuntimeException $e) { return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 340116e7d719c..4a29e8d8b18c6 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -8,6 +8,7 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\BackgroundJob\IJobList; +use OCP\Common\Exception\NotFoundException; use OCP\DB\Exception; use OCP\IServerContainer; use OCP\LanguageModel\AbstractLanguageModelTask; @@ -152,14 +153,14 @@ public function scheduleTask(ILanguageModelTask $task): void { * @param int $id The id of the task * @return ILanguageModelTask * @throws RuntimeException If the query failed - * @throws \ValueError If the task could not be found + * @throws NotFoundException If the task could not be found */ public function getTask(int $id): ILanguageModelTask { try { $taskEntity = $this->taskMapper->find($id); return AbstractLanguageModelTask::fromTaskEntity($taskEntity); } catch (DoesNotExistException $e) { - throw new \ValueError('Could not find task with the provided id'); + throw new NotFoundException('Could not find task with the provided id'); } catch (MultipleObjectsReturnedException $e) { throw new RuntimeException('Could not uniquely identify task with given id'); } catch (Exception $e) { diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 80546149a6206..4c28ca86d7257 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -26,6 +26,7 @@ namespace OCP\LanguageModel; +use OCP\Common\Exception\NotFoundException; use OCP\PreConditionNotMetException; use RuntimeException; @@ -70,7 +71,7 @@ public function scheduleTask(ILanguageModelTask $task) : void; * @param int $id The id of the task * @return ILanguageModelTask * @throws RuntimeException If the query failed - * @throws \ValueError If the task could not be found + * @throws NotFoundException If the task could not be found * @since 28.0.0 */ public function getTask(int $id): ILanguageModelTask; From e90dd4a0000eb66fb7ecf5d82f86f6a4be90b7c3 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:52:50 +0200 Subject: [PATCH 025/100] LLM OCP API: Fix copyright Signed-off-by: Marcel Klehr (cherry picked from commit 94fcf888923d39b904129b19a1cc20d85f09e8ef) --- core/Controller/LanguageModelApiController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index a4f83cc1b1151..b31b8f66b4a67 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -3,9 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julius Härtl + * @copyright Copyright (c) 2023 Marcel Klehr * - * @author Julius Härtl + * @author Marcel Klehr * * @license GNU AGPL version 3 or any later version * From 44ce2854697c61d10f00abc86493d49cfec8d34b Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 27 Jun 2023 16:58:17 +0200 Subject: [PATCH 026/100] cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit 1d3661ded9c4ca80c30a1ae4eb08a2d4967da8cf) --- lib/public/LanguageModel/AbstractLanguageModelTask.php | 1 + lib/public/LanguageModel/FreePromptTask.php | 1 + lib/public/LanguageModel/HeadlineTask.php | 1 + lib/public/LanguageModel/ILanguageModelTask.php | 1 + lib/public/LanguageModel/SummaryTask.php | 1 + lib/public/LanguageModel/TopicsTask.php | 1 + 6 files changed, 6 insertions(+) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index b7edcf33be858..67c341f9b3e37 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -1,4 +1,5 @@ Date: Wed, 28 Jun 2023 14:59:20 +0200 Subject: [PATCH 027/100] Add tasks::last_updated column and vacate tasks after a week Signed-off-by: Marcel Klehr (cherry picked from commit cb0f918d2101bf7644d7d84f811b37e3e672f091) --- .../Version28000Date20230616104802.php | 7 +++ lib/private/LanguageModel/Db/Task.php | 10 +++- lib/private/LanguageModel/Db/TaskMapper.php | 18 ++++++ .../RemoveOldTasksBackgroundJob.php | 59 +++++++++++++++++++ lib/private/Repair.php | 2 + .../Repair/AddRemoveOldTasksBackgroundJob.php | 47 +++++++++++++++ lib/private/Setup.php | 2 + 7 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php create mode 100644 lib/private/Repair/AddRemoveOldTasksBackgroundJob.php diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php index 76d8173861f41..ab7347608e200 100644 --- a/core/Migrations/Version28000Date20230616104802.php +++ b/core/Migrations/Version28000Date20230616104802.php @@ -78,9 +78,16 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt 'length' => 32, 'default' => '', ]); + $table->addColumn('last_updated', 'integer', [ + 'notnull' => false, + 'length' => 4, + 'default' => 0, + 'unsigned' => true, + ]); $table->setPrimaryKey(['id'], 'llm_tasks_id_index'); $table->addUniqueIndex(['status', 'type'], 'llm_tasks_status_type'); + $table->addIndex(['last_updated'], 'llm_tasks_updated'); } return $schema; diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index fe77d5595307a..895969e08bb91 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -8,6 +8,8 @@ /** * @method setType(string $type) * @method string getType() + * @method setLastUpdated(int $lastUpdated) + * @method int getLastUpdated() * @method setInput(string $type) * @method string getInput() * @method setStatus(int $type) @@ -18,6 +20,8 @@ * @method string getAppId() */ class Task extends Entity { + protected $lastUpdated; + protected $type; protected $input; protected $status; @@ -27,17 +31,18 @@ class Task extends Entity { /** * @var string[] */ - public static array $columns = ['id', 'type', 'input', 'output', 'status', 'user_id', 'app_id']; + public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id']; /** * @var string[] */ - public static array $fields = ['id', 'type', 'input', 'output', 'status', 'userId', 'appId']; + public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId']; public function __construct() { // add types in constructor $this->addType('id', 'integer'); + $this->addType('lastUpdated', 'integer'); $this->addType('type', 'string'); $this->addType('input', 'string'); $this->addType('status', 'integer'); @@ -48,6 +53,7 @@ public function __construct() { public static function fromLanguageModelTask(ILanguageModelTask $task): Task { return Task::fromParams([ 'type' => $task->getType(), + 'lastUpdated' => time(), 'status' => $task->getStatus(), 'input' => $task->getInput(), 'output' => $task->getOutput(), diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php index d7122ea794165..e0b06a1b62b3f 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -3,6 +3,7 @@ namespace OC\LanguageModel\Db; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; @@ -30,4 +31,21 @@ public function find(int $id): Task { ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); return $this->findEntity($qb); } + + /** + * @param int $timeout + * @return int the number of deleted tasks + * @throws Exception + */ + public function deleteOlderThan(int $timeout): int { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->tableName) + ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter(time() - $timeout))); + return $qb->executeStatement(); + } + + public function update(Entity $entity): Entity { + $entity->setLastUpdated(time()); + return parent::update($entity); + } } diff --git a/lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php b/lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php new file mode 100644 index 0000000000000..fa3a716a2c608 --- /dev/null +++ b/lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php @@ -0,0 +1,59 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OC\LanguageModel; + +use OC\LanguageModel\Db\TaskMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\TimedJob; +use OCP\DB\Exception; +use Psr\Log\LoggerInterface; + +class RemoveOldTasksBackgroundJob extends TimedJob { + public const MAX_TASK_AGE_SECONDS = 60 * 50 * 24 * 7; // 1 week + + public function __construct( + ITimeFactory $timeFactory, + private TaskMapper $taskMapper, + private LoggerInterface $logger, + + ) { + parent::__construct($timeFactory); + $this->setInterval(60 * 60 * 24); + } + + /** + * @param mixed $argument + * @inheritDoc + */ + protected function run($argument) { + try { + $this->taskMapper->deleteOlderThan(self::MAX_TASK_AGE_SECONDS); + } catch (Exception $e) { + $this->logger->warning('Failed to delete stale language model tasks', ['exception' => $e]); + } + } +} diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 330fa241b1e09..00cc40a00b355 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -34,6 +34,7 @@ */ namespace OC; +use OC\Repair\AddRemoveOldTasksBackgroundJob; use OC\Repair\CleanUpAbandonedApps; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; @@ -210,6 +211,7 @@ public static function getRepairSteps(): array { \OCP\Server::get(AddTokenCleanupJob::class), \OCP\Server::get(CleanUpAbandonedApps::class), \OCP\Server::get(AddMissingSecretJob::class), + \OCP\Server::get(AddRemoveOldTasksBackgroundJob::class), ]; } diff --git a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php new file mode 100644 index 0000000000000..713192b06f957 --- /dev/null +++ b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php @@ -0,0 +1,47 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OC\Repair; + +use OC\LanguageModel\RemoveOldTasksBackgroundJob; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddRemoveOldTasksBackgroundJob implements IRepairStep { + private IJobList $jobList; + + public function __construct(IJobList $jobList) { + $this->jobList = $jobList; + } + + public function getName(): string { + return 'Add language model tasks cleanup job'; + } + + public function run(IOutput $output) { + $this->jobList->add(RemoveOldTasksBackgroundJob::class); + } +} diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 3a2e7614dd92e..a80afee6d86c2 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -53,6 +53,7 @@ use InvalidArgumentException; use OC\Authentication\Token\PublicKeyTokenProvider; use OC\Authentication\Token\TokenCleanupJob; +use OC\LanguageModel\RemoveOldTasksBackgroundJob; use OC\Log\Rotate; use OC\Preview\BackgroundCleanupJob; use OCP\AppFramework\Utility\ITimeFactory; @@ -453,6 +454,7 @@ public static function installBackgroundJobs() { $jobList->add(TokenCleanupJob::class); $jobList->add(Rotate::class); $jobList->add(BackgroundCleanupJob::class); + $jobList->add(RemoveOldTasksBackgroundJob::class); } /** From 2234497c49b3e529721852313ff7005949bc01d1 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 28 Jun 2023 15:14:41 +0200 Subject: [PATCH 028/100] TaskMapper#update: Use time factory Signed-off-by: Marcel Klehr (cherry picked from commit 1623ad9eda5fed298b798d5cac0012a3a0af8bda) --- lib/private/LanguageModel/Db/TaskMapper.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php index e0b06a1b62b3f..f15187d61d523 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -6,6 +6,7 @@ use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\QBMapper; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\Exception; use OCP\IDBConnection; @@ -13,7 +14,10 @@ * @extends QBMapper */ class TaskMapper extends QBMapper { - public function __construct(IDBConnection $db) { + public function __construct( + IDBConnection $db, + private ITimeFactory $timeFactory, + ) { parent::__construct($db, 'llm_tasks', Task::class); } @@ -45,7 +49,7 @@ public function deleteOlderThan(int $timeout): int { } public function update(Entity $entity): Entity { - $entity->setLastUpdated(time()); + $entity->setLastUpdated($this->timeFactory->now()->getTimestamp()); return parent::update($entity); } } From 970d6886e6b5cb46310659f843f8ec2e6b8c5168 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 28 Jun 2023 15:16:21 +0200 Subject: [PATCH 029/100] ILanguageModelManager: Add docblock description Signed-off-by: Marcel Klehr (cherry picked from commit 0a94525042b754b19664292f3134cc8c98dcd2a7) --- lib/public/LanguageModel/ILanguageModelManager.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 4c28ca86d7257..055fb69820f0d 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -31,6 +31,8 @@ use RuntimeException; /** + * API surface for apps interacting with and making use of LanguageModel providers + * without known which providers are installed * @since 28.0.0 */ interface ILanguageModelManager { From c1085bf1d7653d4b279179affec299757b82a743 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 28 Jun 2023 15:19:57 +0200 Subject: [PATCH 030/100] ILanguageModelTask: Use php type checking along with psalm parameterized types Signed-off-by: Marcel Klehr (cherry picked from commit 27e1c86652d73b27f48756b350bb4578cdeeee0b) --- lib/public/LanguageModel/FreePromptTask.php | 4 ++-- lib/public/LanguageModel/HeadlineTask.php | 4 ++-- lib/public/LanguageModel/ILanguageModelTask.php | 10 ++++++---- lib/public/LanguageModel/SummaryTask.php | 4 ++-- lib/public/LanguageModel/TopicsTask.php | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index ac8ba6638a193..3de215784ab70 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -39,7 +39,7 @@ final class FreePromptTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider($provider): string { + public function visitProvider(ILanguageModelProvider $provider): string { return $provider->prompt($this->getInput()); } @@ -47,7 +47,7 @@ public function visitProvider($provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider($provider): bool { + public function canUseProvider(ILanguageModelProvider $provider): bool { return true; } diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index 789ad22665635..e66c8893d8301 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -41,7 +41,7 @@ final class HeadlineTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider($provider): string { + public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { throw new \RuntimeException('HeadlineTask#visitProvider expects IHeadlineProvider'); } @@ -52,7 +52,7 @@ public function visitProvider($provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider($provider): bool { + public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof IHeadlineProvider; } diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index f5793632f582c..09d7507662261 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -62,18 +62,20 @@ interface ILanguageModelTask extends \JsonSerializable { ]; /** - * @param T $provider + * @psalm-param T $provider + * @param ILanguageModelProvider $provider * @return string * @since 28.0.0 */ - public function visitProvider($provider): string; + public function visitProvider(ILanguageModelProvider $provider): string; /** - * @param T $provider + * @psalm-param T $provider + * @param ILanguageModelProvider $provider * @return bool * @since 28.0.0 */ - public function canUseProvider($provider): bool; + public function canUseProvider(ILanguageModelProvider $provider): bool; /** diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 4d7f9e0813f57..4504cdff7503c 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -41,7 +41,7 @@ final class SummaryTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider($provider): string { + public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { throw new \RuntimeException('SummaryTask#visitProvider expects ISummaryProvider'); } @@ -52,7 +52,7 @@ public function visitProvider($provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider($provider): bool { + public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ISummaryProvider; } diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index 513241240a223..ccf5d512a8a97 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -41,7 +41,7 @@ final class TopicsTask extends AbstractLanguageModelTask { * @inheritDoc * @since 28.0.0 */ - public function visitProvider($provider): string { + public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { throw new \RuntimeException('TopicsTask#visitProvider expects ITopicsProvider'); } @@ -52,7 +52,7 @@ public function visitProvider($provider): string { * @inheritDoc * @since 28.0.0 */ - public function canUseProvider($provider): bool { + public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ITopicsProvider; } From a4627508e87a70a23459d86bb703dc6f8b401650 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 28 Jun 2023 16:18:59 +0200 Subject: [PATCH 031/100] Add preliminary tests Signed-off-by: Marcel Klehr (cherry picked from commit ebc76315441d75c3c7659c8a3fd0a285bdcd8cb2) --- .../LanguageModelManagerTest.php | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 tests/lib/LanguageModel/LanguageModelManagerTest.php diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/LanguageModel/LanguageModelManagerTest.php new file mode 100644 index 0000000000000..80cf4348a0118 --- /dev/null +++ b/tests/lib/LanguageModel/LanguageModelManagerTest.php @@ -0,0 +1,269 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\LanguageModel; + +use OC\AppFramework\Bootstrap\Coordinator; +use OC\LanguageModel\Db\TaskMapper; +use OC\LanguageModel\LanguageModelManager; +use OC\LanguageModel\TaskBackgroundJob; +use OCP\BackgroundJob\IJobList; +use OCP\Common\Exception\NotFoundException; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IServerContainer; +use OCP\LanguageModel\Events\TaskFailedEvent; +use OCP\LanguageModel\Events\TaskSuccessfulEvent; +use OCP\LanguageModel\FreePromptTask; +use OCP\LanguageModel\HeadlineTask; +use OCP\LanguageModel\IHeadlineProvider; +use OCP\LanguageModel\ILanguageModelManager; +use OCP\LanguageModel\ILanguageModelProvider; +use OCP\LanguageModel\ILanguageModelTask; +use OCP\LanguageModel\ISummaryProvider; +use OCP\LanguageModel\SummaryTask; +use OCP\LanguageModel\TopicsTask; +use OCP\PreConditionNotMetException; +use Psr\Log\LoggerInterface; +use Test\BackgroundJob\DummyJobList; + +class TestVanillaLanguageModelProvider implements ILanguageModelProvider { + public bool $ran = false; + + public function getName(): string { + return 'TEST Vanilla LLM Provider'; + } + + public function prompt(string $prompt): string { + $this->ran = true; + return $prompt . ' Free Prompt'; + } +} + +class TestFailingLanguageModelProvider implements ILanguageModelProvider { + public bool $ran = false; + + public function getName(): string { + return 'TEST Vanilla LLM Provider'; + } + + public function prompt(string $prompt): string { + $this->ran = true; + throw new \Exception('ERROR'); + } +} + +class TestFullLanguageModelProvider implements ILanguageModelProvider, ISummaryProvider, IHeadlineProvider { + public function getName(): string { + return 'TEST Full LLM Provider'; + } + + public function prompt(string $prompt): string { + return $prompt . ' Free Prompt'; + } + + public function findHeadline(string $text): string { + return $text . ' Headline'; + } + + public function summarize(string $text): string { + return $text. ' Summarize'; + } +} + +class LanguageModelManagerTest extends \Test\TestCase { + private ILanguageModelManager $languageModelManager; + private Coordinator $coordinator; + + protected function setUp(): void { + parent::setUp(); + + $this->languageModelManager = new LanguageModelManager( + \OC::$server->get(IServerContainer::class), + $this->coordinator = \OC::$server->get(Coordinator::class), + \OC::$server->get(LoggerInterface::class), + \OC::$server->get(IJobList::class), + \OC::$server->get(TaskMapper::class), + ); + } + + public function testShouldNotHaveAnyProviders() { + $this->assertCount(0, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(0, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertFalse($this->languageModelManager->hasProviders()); + $this->expectException(PreConditionNotMetException::class); + $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null)); + } + + public function testProviderShouldBeRegisteredAndRun() { + $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); + $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertTrue($this->languageModelManager->hasProviders()); + $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null))); + + // Summaries are not implemented by the vanilla provider, only free prompt + $this->expectException(PreConditionNotMetException::class); + $this->languageModelManager->runTask(new SummaryTask('Hello', 'test', null)); + } + + public function testProviderShouldBeRegisteredAndScheduled() { + // register provider + $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); + $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertTrue($this->languageModelManager->hasProviders()); + + // create task object + $task = new FreePromptTask('Hello', 'test', null); + $this->assertNull($task->getId()); + $this->assertNull($task->getOutput()); + + // schedule works + $this->assertEquals(ILanguageModelTask::STATUS_UNKNOWN, $task->getStatus()); + $this->languageModelManager->scheduleTask($task); + + // Task object is up-to-date + $this->assertNotNull($task->getId()); + $this->assertNull($task->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task->getStatus()); + + // Task object retrieved from db is up-to-date + $task2 = $this->languageModelManager->getTask($task->getId()); + $this->assertEquals($task->getId(), $task2->getId()); + $this->assertEquals('Hello', $task2->getInput()); + $this->assertNull($task2->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); + + /** @var IEventDispatcher $eventDispatcher */ + $eventDispatcher = \OC::$server->get(IEventDispatcher::class); + $successfulEventFired = false; + $eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { + $successfulEventFired = true; + $t = $event->getTask(); + $this->assertEquals($task->getId(), $t->getId()); + $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $t->getStatus()); + $this->assertEquals('Hello Free Prompt', $t->getOutput()); + }); + $failedEventFired = false; + $eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { + $failedEventFired = true; + $t = $event->getTask(); + $this->assertEquals($task->getId(), $t->getId()); + $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $t->getStatus()); + $this->assertEquals('ERROR', $event->getErrorMessage()); + }); + + // run background job + /** @var TaskBackgroundJob $bgJob */ + $bgJob = \OC::$server->get(TaskBackgroundJob::class); + $bgJob->setArgument(['taskId' => $task->getId()]); + $bgJob->start(new DummyJobList()); + $provider = \OC::$server->get(TestVanillaLanguageModelProvider::class); + $this->assertTrue($provider->ran); + $this->assertTrue($successfulEventFired); + $this->assertFalse($failedEventFired); + + // Task object retrieved from db is up-to-date + $task3 = $this->languageModelManager->getTask($task->getId()); + $this->assertEquals($task->getId(), $task3->getId()); + $this->assertEquals('Hello', $task3->getInput()); + $this->assertEquals('Hello Free Prompt', $task3->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $task2->getStatus()); + } + + public function testMultipleProvidersShouldBeRegisteredAndRunCorrectly() { + $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); + $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestFullLanguageModelProvider::class); + $this->assertCount(3, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(3, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertTrue($this->languageModelManager->hasProviders()); + + // Try free prompt again + $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null))); + + // Try headline task + $this->assertEquals('Hello Headline', $this->languageModelManager->runTask(new HeadlineTask('Hello', 'test', null))); + + // Try summary task + $this->assertEquals('Hello Summarize', $this->languageModelManager->runTask(new SummaryTask('Hello', 'test', null))); + + // Topics are not implemented by both the vanilla provider and the full provider + $this->expectException(PreConditionNotMetException::class); + $this->languageModelManager->runTask(new TopicsTask('Hello', 'test', null)); + } + + public function testNonexistentTask() { + $this->expectException(NotFoundException::class); + $this->languageModelManager->getTask(98765432456); + } + + public function testTaskFailure() { + // register provider + $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestFailingLanguageModelProvider::class); + $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertTrue($this->languageModelManager->hasProviders()); + + // create task object + $task = new FreePromptTask('Hello', 'test', null); + $this->assertNull($task->getId()); + $this->assertNull($task->getOutput()); + + // schedule works + $this->assertEquals(ILanguageModelTask::STATUS_UNKNOWN, $task->getStatus()); + $this->languageModelManager->scheduleTask($task); + + // Task object is up-to-date + $this->assertNotNull($task->getId()); + $this->assertNull($task->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task->getStatus()); + + // Task object retrieved from db is up-to-date + $task2 = $this->languageModelManager->getTask($task->getId()); + $this->assertEquals($task->getId(), $task2->getId()); + $this->assertEquals('Hello', $task2->getInput()); + $this->assertNull($task2->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); + + /** @var IEventDispatcher $eventDispatcher */ + $eventDispatcher = \OC::$server->get(IEventDispatcher::class); + $successfulEventFired = false; + $eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { + $successfulEventFired = true; + $t = $event->getTask(); + $this->assertEquals($task->getId(), $t->getId()); + $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $t->getStatus()); + $this->assertEquals('Hello Free Prompt', $t->getOutput()); + }); + $failedEventFired = false; + $eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { + $failedEventFired = true; + $t = $event->getTask(); + $this->assertEquals($task->getId(), $t->getId()); + $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $t->getStatus()); + $this->assertEquals('ERROR', $event->getErrorMessage()); + }); + + // run background job + /** @var TaskBackgroundJob $bgJob */ + $bgJob = \OC::$server->get(TaskBackgroundJob::class); + $bgJob->setArgument(['taskId' => $task->getId()]); + $bgJob->start(new DummyJobList()); + $provider = \OC::$server->get(TestFailingLanguageModelProvider::class); + $this->assertTrue($provider->ran); + $this->assertTrue($failedEventFired); + $this->assertFalse($successfulEventFired); + + // Task object retrieved from db is up-to-date + $task3 = $this->languageModelManager->getTask($task->getId()); + $this->assertEquals($task->getId(), $task3->getId()); + $this->assertEquals('Hello', $task3->getInput()); + $this->assertNull($task3->getOutput()); + $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $task2->getStatus()); + } +} From d21f7bf1fb2b420e7f5049983398d67f36fe044a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 29 Jun 2023 17:07:31 +0200 Subject: [PATCH 032/100] Fix tests Signed-off-by: Marcel Klehr (cherry picked from commit 20cb9935ca80c32665b131315078661064037795) --- tests/lib/BackgroundJob/DummyJobList.php | 2 +- .../LanguageModelManagerTest.php | 126 ++++++++++++++---- 2 files changed, 99 insertions(+), 29 deletions(-) diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index 42b69cfbe418c..8574f462ca76e 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -141,7 +141,7 @@ public function setLastRun(IJob $job): void { } public function hasReservedJob(?string $className = null): bool { - return $this->reserved[$className ?? '']; + return isset($this->reserved[$className ?? '']) && $this->reserved[$className ?? '']; } public function setHasReservedJob(?string $className, bool $hasReserved): void { diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/LanguageModel/LanguageModelManagerTest.php index 80cf4348a0118..39580fa3cb829 100644 --- a/tests/lib/LanguageModel/LanguageModelManagerTest.php +++ b/tests/lib/LanguageModel/LanguageModelManagerTest.php @@ -9,10 +9,15 @@ namespace Test\LanguageModel; use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\Bootstrap\RegistrationContext; +use OC\AppFramework\Bootstrap\ServiceRegistration; +use OC\EventDispatcher\EventDispatcher; +use OC\LanguageModel\Db\Task; use OC\LanguageModel\Db\TaskMapper; use OC\LanguageModel\LanguageModelManager; use OC\LanguageModel\TaskBackgroundJob; -use OCP\BackgroundJob\IJobList; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Common\Exception\NotFoundException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IServerContainer; @@ -82,16 +87,69 @@ class LanguageModelManagerTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); + $this->providers = [ + TestVanillaLanguageModelProvider::class => new TestVanillaLanguageModelProvider(), + TestFullLanguageModelProvider::class => new TestFullLanguageModelProvider(), + TestFailingLanguageModelProvider::class => new TestFailingLanguageModelProvider(), + ]; + + $this->serverContainer = $this->createMock(IServerContainer::class); + $this->serverContainer->expects($this->any())->method('get')->willReturnCallback(function ($class) { + return $this->providers[$class]; + }); + + $this->eventDispatcher = new EventDispatcher( + new \Symfony\Component\EventDispatcher\EventDispatcher(), + $this->serverContainer, + \OC::$server->get(LoggerInterface::class), + ); + + $this->registrationContext = $this->createMock(RegistrationContext::class); + $this->coordinator = $this->createMock(Coordinator::class); + $this->coordinator->expects($this->any())->method('getRegistrationContext')->willReturn($this->registrationContext); + + $this->taskMapper = $this->createMock(TaskMapper::class); + $this->tasksDb = []; + $this->taskMapper + ->expects($this->any()) + ->method('insert') + ->willReturnCallback(function (Task $task) { + $task->setId(count($this->tasksDb) ? max(array_keys($this->tasksDb)) : 1); + $this->tasksDb[$task->getId()] = $task->toRow(); + return $task; + }); + $this->taskMapper + ->expects($this->any()) + ->method('update') + ->willReturnCallback(function (Task $task) { + $this->tasksDb[$task->getId()] = $task->toRow(); + return $task; + }); + $this->taskMapper + ->expects($this->any()) + ->method('find') + ->willReturnCallback(function (int $id) { + if (!isset($this->tasksDb[$id])) { + throw new DoesNotExistException('Could not find it'); + } + return Task::fromRow($this->tasksDb[$id]); + }); + + $this->jobList = $this->createPartialMock(DummyJobList::class, ['add']); + $this->jobList->expects($this->any())->method('add')->willReturnCallback(function () { + }); + $this->languageModelManager = new LanguageModelManager( - \OC::$server->get(IServerContainer::class), - $this->coordinator = \OC::$server->get(Coordinator::class), + $this->serverContainer, + $this->coordinator, \OC::$server->get(LoggerInterface::class), - \OC::$server->get(IJobList::class), - \OC::$server->get(TaskMapper::class), + $this->jobList, + $this->taskMapper, ); } public function testShouldNotHaveAnyProviders() { + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([]); $this->assertCount(0, $this->languageModelManager->getAvailableTasks()); $this->assertCount(0, $this->languageModelManager->getAvailableTaskTypes()); $this->assertFalse($this->languageModelManager->hasProviders()); @@ -100,7 +158,9 @@ public function testShouldNotHaveAnyProviders() { } public function testProviderShouldBeRegisteredAndRun() { - $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ + new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + ]); $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -113,7 +173,9 @@ public function testProviderShouldBeRegisteredAndRun() { public function testProviderShouldBeRegisteredAndScheduled() { // register provider - $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ + new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + ]); $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -139,10 +201,10 @@ public function testProviderShouldBeRegisteredAndScheduled() { $this->assertNull($task2->getOutput()); $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); - /** @var IEventDispatcher $eventDispatcher */ - $eventDispatcher = \OC::$server->get(IEventDispatcher::class); + /** @var IEventDispatcher $this->eventDispatcher */ + $this->eventDispatcher = \OC::$server->get(IEventDispatcher::class); $successfulEventFired = false; - $eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { + $this->eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { $successfulEventFired = true; $t = $event->getTask(); $this->assertEquals($task->getId(), $t->getId()); @@ -150,7 +212,7 @@ public function testProviderShouldBeRegisteredAndScheduled() { $this->assertEquals('Hello Free Prompt', $t->getOutput()); }); $failedEventFired = false; - $eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { + $this->eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { $failedEventFired = true; $t = $event->getTask(); $this->assertEquals($task->getId(), $t->getId()); @@ -159,11 +221,14 @@ public function testProviderShouldBeRegisteredAndScheduled() { }); // run background job - /** @var TaskBackgroundJob $bgJob */ - $bgJob = \OC::$server->get(TaskBackgroundJob::class); + $bgJob = new TaskBackgroundJob( + \OC::$server->get(ITimeFactory::class), + $this->languageModelManager, + $this->eventDispatcher, + ); $bgJob->setArgument(['taskId' => $task->getId()]); - $bgJob->start(new DummyJobList()); - $provider = \OC::$server->get(TestVanillaLanguageModelProvider::class); + $bgJob->start($this->jobList); + $provider = $this->providers[TestVanillaLanguageModelProvider::class]; $this->assertTrue($provider->ran); $this->assertTrue($successfulEventFired); $this->assertFalse($failedEventFired); @@ -173,12 +238,14 @@ public function testProviderShouldBeRegisteredAndScheduled() { $this->assertEquals($task->getId(), $task3->getId()); $this->assertEquals('Hello', $task3->getInput()); $this->assertEquals('Hello Free Prompt', $task3->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $task2->getStatus()); + $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $task3->getStatus()); } public function testMultipleProvidersShouldBeRegisteredAndRunCorrectly() { - $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestVanillaLanguageModelProvider::class); - $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestFullLanguageModelProvider::class); + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ + new ServiceRegistration('test', TestVanillaLanguageModelProvider::class), + new ServiceRegistration('test', TestFullLanguageModelProvider::class), + ]); $this->assertCount(3, $this->languageModelManager->getAvailableTasks()); $this->assertCount(3, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -204,7 +271,9 @@ public function testNonexistentTask() { public function testTaskFailure() { // register provider - $this->coordinator->getRegistrationContext()->registerLanguageModelProvider('test', TestFailingLanguageModelProvider::class); + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ + new ServiceRegistration('test', TestFailingLanguageModelProvider::class), + ]); $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -230,10 +299,8 @@ public function testTaskFailure() { $this->assertNull($task2->getOutput()); $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); - /** @var IEventDispatcher $eventDispatcher */ - $eventDispatcher = \OC::$server->get(IEventDispatcher::class); $successfulEventFired = false; - $eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { + $this->eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { $successfulEventFired = true; $t = $event->getTask(); $this->assertEquals($task->getId(), $t->getId()); @@ -241,7 +308,7 @@ public function testTaskFailure() { $this->assertEquals('Hello Free Prompt', $t->getOutput()); }); $failedEventFired = false; - $eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { + $this->eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { $failedEventFired = true; $t = $event->getTask(); $this->assertEquals($task->getId(), $t->getId()); @@ -250,11 +317,14 @@ public function testTaskFailure() { }); // run background job - /** @var TaskBackgroundJob $bgJob */ - $bgJob = \OC::$server->get(TaskBackgroundJob::class); + $bgJob = new TaskBackgroundJob( + \OC::$server->get(ITimeFactory::class), + $this->languageModelManager, + $this->eventDispatcher, + ); $bgJob->setArgument(['taskId' => $task->getId()]); - $bgJob->start(new DummyJobList()); - $provider = \OC::$server->get(TestFailingLanguageModelProvider::class); + $bgJob->start($this->jobList); + $provider = $this->providers[TestFailingLanguageModelProvider::class]; $this->assertTrue($provider->ran); $this->assertTrue($failedEventFired); $this->assertFalse($successfulEventFired); @@ -264,6 +334,6 @@ public function testTaskFailure() { $this->assertEquals($task->getId(), $task3->getId()); $this->assertEquals('Hello', $task3->getInput()); $this->assertNull($task3->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $task2->getStatus()); + $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $task3->getStatus()); } } From e7179598c7193ef0437811bdcad6af61980b9c0f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 29 Jun 2023 17:08:23 +0200 Subject: [PATCH 033/100] Make tests pass Signed-off-by: Marcel Klehr (cherry picked from commit 66c0e6b9f79d1e82f55af2acd9d2b500c8128614) --- lib/private/LanguageModel/Db/Task.php | 12 +++++++++++- .../LanguageModel/LanguageModelManager.php | 17 ++++++++++++++--- lib/private/LanguageModel/TaskBackgroundJob.php | 2 +- .../LanguageModel/AbstractLanguageModelTask.php | 9 +++++---- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index 895969e08bb91..bbafd7583c8b6 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -12,6 +12,8 @@ * @method int getLastUpdated() * @method setInput(string $type) * @method string getInput() + * @method setOutput(string $type) + * @method string getOutput() * @method setStatus(int $type) * @method int getStatus() * @method setUserId(string $type) @@ -21,9 +23,9 @@ */ class Task extends Entity { protected $lastUpdated; - protected $type; protected $input; + protected $output; protected $status; protected $userId; protected $appId; @@ -45,13 +47,21 @@ public function __construct() { $this->addType('lastUpdated', 'integer'); $this->addType('type', 'string'); $this->addType('input', 'string'); + $this->addType('output', 'string'); $this->addType('status', 'integer'); $this->addType('userId', 'string'); $this->addType('appId', 'string'); } + public function toRow(): array { + return array_combine(self::$columns, array_map(function ($field) { + return $this->{'get'.ucfirst($field)}(); + }, self::$fields)); + } + public static function fromLanguageModelTask(ILanguageModelTask $task): Task { return Task::fromParams([ + 'id' => $task->getId(), 'type' => $task->getType(), 'lastUpdated' => time(), 'status' => $task->getStatus(), diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 4a29e8d8b18c6..9117b131578ab 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -13,11 +13,15 @@ use OCP\IServerContainer; use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\FreePromptTask; +use OCP\LanguageModel\HeadlineTask; +use OCP\LanguageModel\IHeadlineProvider; use OCP\LanguageModel\ILanguageModelManager; use OCP\LanguageModel\ILanguageModelProvider; use OCP\LanguageModel\ILanguageModelTask; use OCP\LanguageModel\ISummaryProvider; +use OCP\LanguageModel\ITopicsProvider; use OCP\LanguageModel\SummaryTask; +use OCP\LanguageModel\TopicsTask; use OCP\PreConditionNotMetException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -69,7 +73,7 @@ public function hasProviders(): bool { if ($context === null) { return false; } - return count($context->getSpeechToTextProviders()) > 0; + return count($context->getLanguageModelProviders()) > 0; } /** @@ -82,6 +86,12 @@ public function getAvailableTasks(): array { if ($provider instanceof ISummaryProvider) { $tasks[SummaryTask::class] = true; } + if ($provider instanceof IHeadlineProvider) { + $tasks[HeadlineTask::class] = true; + } + if ($provider instanceof ITopicsProvider) { + $tasks[TopicsTask::class] = true; + } } return array_keys($tasks); } @@ -110,7 +120,8 @@ public function runTask(ILanguageModelTask $task): string { } try { $task->setStatus(ILanguageModelTask::STATUS_RUNNING); - $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $taskEntity = $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $task->setId($taskEntity->getId()); $output = $task->visitProvider($provider); $task->setOutput($output); $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); @@ -140,10 +151,10 @@ public function scheduleTask(ILanguageModelTask $task): void { if (!$this->canHandleTask($task)) { throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); } + $task->setStatus(ILanguageModelTask::STATUS_SCHEDULED); $taskEntity = Task::fromLanguageModelTask($task); $this->taskMapper->insert($taskEntity); $task->setId($taskEntity->getId()); - $task->setStatus(ILanguageModelTask::STATUS_SCHEDULED); $this->jobList->add(TaskBackgroundJob::class, [ 'taskId' => $task->getId() ]); diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index 1c1291dc12699..f58510a9e3f2c 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -54,7 +54,7 @@ protected function run($argument) { try { $this->languageModelManager->runTask($task); $event = new TaskSuccessfulEvent($task); - } catch (\RuntimeException|PreConditionNotMetException $e) { + } catch (\RuntimeException|PreConditionNotMetException|\Throwable $e) { $event = new TaskFailedEvent($task, $e->getMessage()); } $this->eventDispatcher->dispatchTyped($event); diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 67c341f9b3e37..a6b091dc14fa5 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -35,8 +35,8 @@ * @template-implements ILanguageModelTask */ abstract class AbstractLanguageModelTask implements ILanguageModelTask { - protected ?int $id; - protected ?string $output; + protected ?int $id = null; + protected ?string $output = null; protected int $status = ILanguageModelTask::STATUS_UNKNOWN; /** @@ -156,6 +156,7 @@ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTas $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); $task->setId($taskEntity->getId()); $task->setStatus($taskEntity->getStatus()); + $task->setOutput($taskEntity->getOutput()); return $task; } @@ -169,9 +170,9 @@ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTas * @since 28.0.0 */ final public static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { - if (!in_array($type, self::TYPES)) { + if (!in_array($type, array_keys(self::TYPES))) { throw new \InvalidArgumentException('Unknown task type'); } - return new (ILanguageModelTask::TYPES[$type])($input, $userId, $appId); + return new (ILanguageModelTask::TYPES[$type])($input, $appId, $userId); } } From 09a8541d7438840aa70c5b4208206368bf4c2dc0 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 30 Jun 2023 13:19:38 +0200 Subject: [PATCH 034/100] Add test for task cleanup Signed-off-by: Marcel Klehr (cherry picked from commit c568c4a4640e79134eaa59625a546915bb706f28) --- .../LanguageModelManagerTest.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/LanguageModel/LanguageModelManagerTest.php index 39580fa3cb829..423eb525ba270 100644 --- a/tests/lib/LanguageModel/LanguageModelManagerTest.php +++ b/tests/lib/LanguageModel/LanguageModelManagerTest.php @@ -15,6 +15,7 @@ use OC\LanguageModel\Db\Task; use OC\LanguageModel\Db\TaskMapper; use OC\LanguageModel\LanguageModelManager; +use OC\LanguageModel\RemoveOldTasksBackgroundJob; use OC\LanguageModel\TaskBackgroundJob; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; @@ -108,6 +109,8 @@ protected function setUp(): void { $this->coordinator = $this->createMock(Coordinator::class); $this->coordinator->expects($this->any())->method('getRegistrationContext')->willReturn($this->registrationContext); + $this->currentTime = new \DateTimeImmutable('now'); + $this->taskMapper = $this->createMock(TaskMapper::class); $this->tasksDb = []; $this->taskMapper @@ -115,6 +118,7 @@ protected function setUp(): void { ->method('insert') ->willReturnCallback(function (Task $task) { $task->setId(count($this->tasksDb) ? max(array_keys($this->tasksDb)) : 1); + $task->setLastUpdated($this->currentTime->getTimestamp()); $this->tasksDb[$task->getId()] = $task->toRow(); return $task; }); @@ -122,6 +126,7 @@ protected function setUp(): void { ->expects($this->any()) ->method('update') ->willReturnCallback(function (Task $task) { + $task->setLastUpdated($this->currentTime->getTimestamp()); $this->tasksDb[$task->getId()] = $task->toRow(); return $task; }); @@ -134,6 +139,14 @@ protected function setUp(): void { } return Task::fromRow($this->tasksDb[$id]); }); + $this->taskMapper + ->expects($this->any()) + ->method('deleteOlderThan') + ->willReturnCallback(function (int $timeout) { + $this->tasksDb = array_filter($this->tasksDb, function (array $task) use ($timeout) { + return $task['last_updated'] >= $this->currentTime->getTimestamp() - $timeout; + }); + }); $this->jobList = $this->createPartialMock(DummyJobList::class, ['add']); $this->jobList->expects($this->any())->method('add')->willReturnCallback(function () { @@ -336,4 +349,28 @@ public function testTaskFailure() { $this->assertNull($task3->getOutput()); $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $task3->getStatus()); } + + public function testOldTasksShouldBeCleanedUp() { + $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ + new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + ]); + $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); + $this->assertTrue($this->languageModelManager->hasProviders()); + $task = new FreePromptTask('Hello', 'test', null); + $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask($task)); + + $this->currentTime = $this->currentTime->add(new \DateInterval('P1Y')); + // run background job + $bgJob = new RemoveOldTasksBackgroundJob( + \OC::$server->get(ITimeFactory::class), + $this->taskMapper, + \OC::$server->get(LoggerInterface::class), + ); + $bgJob->setArgument([]); + $bgJob->start($this->jobList); + + $this->expectException(NotFoundException::class); + $this->languageModelManager->getTask($task->getId()); + } } From 8197403de12f3f7bf6406f21b16b161cdfb2536d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 30 Jun 2023 13:20:15 +0200 Subject: [PATCH 035/100] Fix LanguageModelManager#runTask: Insert task into db if it doesn't exist Signed-off-by: Marcel Klehr (cherry picked from commit 1747068e518240edec22ff3c4f6946ac925918fe) --- lib/private/LanguageModel/LanguageModelManager.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 9117b131578ab..e07762cf8cc9a 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -120,8 +120,12 @@ public function runTask(ILanguageModelTask $task): string { } try { $task->setStatus(ILanguageModelTask::STATUS_RUNNING); - $taskEntity = $this->taskMapper->update(Task::fromLanguageModelTask($task)); - $task->setId($taskEntity->getId()); + if ($task->getId() === null) { + $taskEntity = $this->taskMapper->insert(Task::fromLanguageModelTask($task)); + $task->setId($taskEntity->getId()); + }else { + $this->taskMapper->update(Task::fromLanguageModelTask($task)); + } $output = $task->visitProvider($provider); $task->setOutput($output); $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); From 4447d59d177ce6160727de0f8307558238693253 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 30 Jun 2023 13:23:12 +0200 Subject: [PATCH 036/100] cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit fb657bfff8c23dea7fea5ab6c9415332677ec81a) --- lib/private/LanguageModel/LanguageModelManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index e07762cf8cc9a..4dff88b26a365 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -123,7 +123,7 @@ public function runTask(ILanguageModelTask $task): string { if ($task->getId() === null) { $taskEntity = $this->taskMapper->insert(Task::fromLanguageModelTask($task)); $task->setId($taskEntity->getId()); - }else { + } else { $this->taskMapper->update(Task::fromLanguageModelTask($task)); } $output = $task->visitProvider($provider); From 97a4a063f4c4edf5cf4389fd92b8e7ccbedc0c82 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 09:47:26 +0200 Subject: [PATCH 037/100] Apply suggestions from code review Co-authored-by: Christoph Wurst Signed-off-by: Marcel Klehr (cherry picked from commit 5b772468ad12f6f7e0d1077c05011912275cc4e7) --- lib/public/LanguageModel/ILanguageModelTask.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 09d7507662261..356cb53914ed8 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -85,13 +85,13 @@ public function canUseProvider(ILanguageModelProvider $provider): bool; public function getType(): string; /** - * @return int + * @return ILanguageModelTask::STATUS_* * @since 28.0.0 */ public function getStatus(): int; /** - * @param int $status + * @param ILanguageModelTask::STATUS_* $status * @since 28.0.0 */ public function setStatus(int $status): void; From ffedcebc500a131db5a9b65a7490bbae8170ce37 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 10:24:28 +0200 Subject: [PATCH 038/100] LLM OCP API: Add identifier param Signed-off-by: Marcel Klehr (cherry picked from commit f6f8cb43313deb3dfd366bc9e2128d787c833405) --- .../Version28000Date20230616104802.php | 5 +++++ lib/private/LanguageModel/Db/Task.php | 9 +++++++-- .../AbstractLanguageModelTask.php | 18 +++++++++++++++--- .../LanguageModel/ILanguageModelTask.php | 6 ++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php index ab7347608e200..f13f2ef03a6ea 100644 --- a/core/Migrations/Version28000Date20230616104802.php +++ b/core/Migrations/Version28000Date20230616104802.php @@ -78,6 +78,11 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt 'length' => 32, 'default' => '', ]); + $table->addColumn('identifier', Types::STRING, [ + 'notnull' => true, + 'length' => 255, + 'default' => '', + ]); $table->addColumn('last_updated', 'integer', [ 'notnull' => false, 'length' => 4, diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index bbafd7583c8b6..1e8d57d39e8ad 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -20,6 +20,8 @@ * @method string getuserId() * @method setAppId(string $type) * @method string getAppId() + * @method setIdentifier(string $type) + * @method string getIdentifier() */ class Task extends Entity { protected $lastUpdated; @@ -29,16 +31,17 @@ class Task extends Entity { protected $status; protected $userId; protected $appId; + protected $identifier; /** * @var string[] */ - public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id']; + public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier']; /** * @var string[] */ - public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId']; + public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier']; public function __construct() { @@ -51,6 +54,7 @@ public function __construct() { $this->addType('status', 'integer'); $this->addType('userId', 'string'); $this->addType('appId', 'string'); + $this->addType('identifier', 'string'); } public function toRow(): array { @@ -69,6 +73,7 @@ public static function fromLanguageModelTask(ILanguageModelTask $task): Task { 'output' => $task->getOutput(), 'userId' => $task->getUserId(), 'appId' => $task->getAppId(), + 'identifier' => $task->getIdentifier(), ]); } } diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index a6b091dc14fa5..afa23dd36c834 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -43,12 +43,14 @@ abstract class AbstractLanguageModelTask implements ILanguageModelTask { * @param string $input * @param string $appId * @param string|null $userId + * @param string $identifier An arbitrary identifier for this task. max length: 255 chars * @since 28.0.0 */ final public function __construct( protected string $input, protected string $appId, protected ?string $userId, + protected string $identifier = '', ) { } @@ -122,6 +124,14 @@ final public function getAppId(): string { return $this->appId; } + /** + * @return string + * @since 28.0.0 + */ + final public function getIdentifier(): string { + return $this->identifier; + } + /** * @return string|null * @since 28.0.0 @@ -143,6 +153,7 @@ public function jsonSerialize() { 'appId' => $this->getAppId(), 'input' => $this->getInput(), 'output' => $this->getOutput(), + 'identifier' => $this->getIdentifier(), ]; } @@ -153,7 +164,7 @@ public function jsonSerialize() { * @since 28.0.0 */ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { - $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId()); + $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId(), $taskEntity->getIdentifier()); $task->setId($taskEntity->getId()); $task->setStatus($taskEntity->getStatus()); $task->setOutput($taskEntity->getOutput()); @@ -165,14 +176,15 @@ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTas * @param string $input * @param string|null $userId * @param string $appId + * @param string $identifier * @return ILanguageModelTask * @throws \InvalidArgumentException * @since 28.0.0 */ - final public static function factory(string $type, string $input, ?string $userId, string $appId): ILanguageModelTask { + final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier): ILanguageModelTask { if (!in_array($type, array_keys(self::TYPES))) { throw new \InvalidArgumentException('Unknown task type'); } - return new (ILanguageModelTask::TYPES[$type])($input, $appId, $userId); + return new (ILanguageModelTask::TYPES[$type])($input, $appId, $userId, $identifier); } } diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index 356cb53914ed8..b8f0f96695d72 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -132,6 +132,12 @@ public function getOutput(): ?string; */ public function getAppId(): string; + /** + * @return string + * @since 28.0.0 + */ + public function getIdentifier(): string; + /** * @return string|null * @since 28.0.0 From 8f0618de9fdfcbd0199f0895edca5edc0f2d8d7e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 11:31:04 +0200 Subject: [PATCH 039/100] LLM OCP API: Fix psalm error Signed-off-by: Marcel Klehr (cherry picked from commit 62b19e0675681d251c210f4231823e3304bd6cd3) --- lib/public/LanguageModel/AbstractLanguageModelTask.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index afa23dd36c834..90e9c42de0957 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -77,7 +77,7 @@ final public function setOutput(?string $output): void { } /** - * @return int + * @return ILanguageModelTask::STATUS_* * @since 28.0.0 */ final public function getStatus(): int { @@ -85,7 +85,7 @@ final public function getStatus(): int { } /** - * @param int $status + * @psalm-param ILanguageModelTask::STATUS_* $status * @since 28.0.0 */ final public function setStatus(int $status): void { From 445b72a93c11fe16f82e5693395ba37084430c35 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 12:41:42 +0200 Subject: [PATCH 040/100] LLM OCP API: Fix security issue Signed-off-by: Marcel Klehr (cherry picked from commit f7e1e79880261e62daad800c42c0b65ca593a223) --- core/Controller/LanguageModelApiController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index b31b8f66b4a67..21954e7f1c753 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -85,6 +85,10 @@ public function getTask(int $id): DataResponse { try { $task = $this->languageModelManager->getTask($id); + if ($this->userId !== $task->getUserId()) { + return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); + } + return new DataResponse([ 'task' => $task, ]); From 7320011948fb5c59da4f4a56de90180fc7c138f6 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 12:48:40 +0200 Subject: [PATCH 041/100] LLM OCP API: Fix psam errors Signed-off-by: Marcel Klehr (cherry picked from commit d6d4e0ffe367a89dda1105fed3df15bc66bff11a) --- core/Controller/LanguageModelApiController.php | 4 ++-- lib/public/LanguageModel/AbstractLanguageModelTask.php | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 21954e7f1c753..9763e5878a820 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -61,9 +61,9 @@ public function tasks(): DataResponse { * @UserRateThrottle(limit=20, period=120) * @AnonRateThrottle(limit=5, period=120) */ - public function schedule(string $text, string $type, ?string $appId): DataResponse { + public function schedule(string $text, string $type, ?string $appId, string $identifier = ''): DataResponse { try { - $task = AbstractLanguageModelTask::factory($type, $text, $this->userId, $appId); + $task = AbstractLanguageModelTask::factory($type, $text, $this->userId, $appId, $identifier); } catch (InvalidArgumentException $e) { return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); } diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 90e9c42de0957..884f26e14574a 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -37,6 +37,10 @@ abstract class AbstractLanguageModelTask implements ILanguageModelTask { protected ?int $id = null; protected ?string $output = null; + + /** + * @psalm-var ILanguageModelTask::STATUS_* + */ protected int $status = ILanguageModelTask::STATUS_UNKNOWN; /** @@ -77,7 +81,7 @@ final public function setOutput(?string $output): void { } /** - * @return ILanguageModelTask::STATUS_* + * @psalm-return ILanguageModelTask::STATUS_* * @since 28.0.0 */ final public function getStatus(): int { @@ -181,7 +185,7 @@ final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTas * @throws \InvalidArgumentException * @since 28.0.0 */ - final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier): ILanguageModelTask { + final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier = ''): ILanguageModelTask { if (!in_array($type, array_keys(self::TYPES))) { throw new \InvalidArgumentException('Unknown task type'); } From d9f2aaf869184b4d582020ecb1249a51dbfa05d8 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 6 Jul 2023 15:24:54 +0200 Subject: [PATCH 042/100] LLM OCP API: Fix psam error Signed-off-by: Marcel Klehr (cherry picked from commit 0a0e8124537d91373870b81ba1f6142f90f26fa0) --- lib/private/LanguageModel/Db/Task.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index 1e8d57d39e8ad..d4e82024da127 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -64,7 +64,8 @@ public function toRow(): array { } public static function fromLanguageModelTask(ILanguageModelTask $task): Task { - return Task::fromParams([ + /** @var Task $task */ + $task = Task::fromParams([ 'id' => $task->getId(), 'type' => $task->getType(), 'lastUpdated' => time(), @@ -75,5 +76,6 @@ public static function fromLanguageModelTask(ILanguageModelTask $task): Task { 'appId' => $task->getAppId(), 'identifier' => $task->getIdentifier(), ]); + return $task; } } From 8567315c934d6c5eb1b226404ca9c40b4ca694f5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 10:51:07 +0200 Subject: [PATCH 043/100] LLM OCS API: s/tasks/tasktypes/ Signed-off-by: Marcel Klehr (cherry picked from commit d33b7a8da421fbcdcd958579cc913ab0c25888bc) --- core/Controller/LanguageModelApiController.php | 2 +- core/routes.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 9763e5878a820..e66bdf9d1247e 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -50,7 +50,7 @@ public function __construct( /** * @PublicPage */ - public function tasks(): DataResponse { + public function taskTypes(): DataResponse { return new DataResponse([ 'tasks' => $this->languageModelManager->getAvailableTaskTypes(), ]); diff --git a/core/routes.php b/core/routes.php index 20be6ef63f456..24af95cd7af3a 100644 --- a/core/routes.php +++ b/core/routes.php @@ -146,7 +146,7 @@ ['root' => '/translation', 'name' => 'TranslationApi#languages', 'url' => '/languages', 'verb' => 'GET'], ['root' => '/translation', 'name' => 'TranslationApi#translate', 'url' => '/translate', 'verb' => 'POST'], - ['root' => '/languagemodel', 'name' => 'LanguageModelApi#tasks', 'url' => '/tasks', 'verb' => 'GET'], + ['root' => '/languagemodel', 'name' => 'LanguageModelApi#taskTypes', 'url' => '/tasktypes', 'verb' => 'GET'], ['root' => '/languagemodel', 'name' => 'LanguageModelApi#schedule', 'url' => '/schedule', 'verb' => 'POST'], ['root' => '/languagemodel', 'name' => 'LanguageModelApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'], ], From e312b2bdd2eb73af3e2172bd32b4b9f1021b826d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:33:25 +0200 Subject: [PATCH 044/100] LLM OCS API: Add OpenAPI docs Signed-off-by: Marcel Klehr (cherry picked from commit 61b9b4f47413dc69cc029a808ec1ace8cceff1da) --- .../Controller/LanguageModelApiController.php | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index e66bdf9d1247e..335f16c2c0524 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -34,7 +34,9 @@ use OCP\IRequest; use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\ILanguageModelManager; +use OCP\LanguageModel\ILanguageModelTask; use OCP\PreConditionNotMetException; +use Punic\Data; class LanguageModelApiController extends \OCP\AppFramework\OCSController { public function __construct( @@ -48,22 +50,38 @@ public function __construct( } /** + * This endpoint returns all available LanguageModel task types + * * @PublicPage + * @return DataResponse + * + * 200: Task types returned */ public function taskTypes(): DataResponse { return new DataResponse([ - 'tasks' => $this->languageModelManager->getAvailableTaskTypes(), + 'types' => $this->languageModelManager->getAvailableTaskTypes(), ]); } /** + * This endpoint allows scheduling a language model task + * * @PublicPage * @UserRateThrottle(limit=20, period=120) * @AnonRateThrottle(limit=5, period=120) + * @param string $input The input for the language model task + * @param string $type The task type + * @param string $appId The originating app ID + * @param string $identifier An identifier to identify this task + * @return DataResponse|DataResponse + * + * 200: Task scheduled + * 400: Task type does not exist + * 412: Task type not available */ - public function schedule(string $text, string $type, ?string $appId, string $identifier = ''): DataResponse { + public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { try { - $task = AbstractLanguageModelTask::factory($type, $text, $this->userId, $appId, $identifier); + $task = AbstractLanguageModelTask::factory($type, $input, $this->userId, $appId, $identifier); } catch (InvalidArgumentException $e) { return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); } @@ -79,7 +97,16 @@ public function schedule(string $text, string $type, ?string $appId, string $ide } /** + * This endpoint allows checking the status and results of a task. + * Tasks are removed 1 week after receiving their last update. + * * @PublicPage + * @param int $id The id of the task + * @return DataResponse|DataResponse + * + * 200: Task returned + * 404: Task not found + * 500: Internal error */ public function getTask(int $id): DataResponse { try { From 22a93bfc3a8d2583089e6851c98a17009ffb1a6d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:50:19 +0200 Subject: [PATCH 045/100] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit 95d2bd50da721353f0bc84d93cf63d65852b0f23) --- lib/private/LanguageModel/LanguageModelManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 4dff88b26a365..e331da87b9051 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -58,7 +58,7 @@ public function getProviders(): array { $class = $providerServiceRegistration->getService(); try { $this->providers[$class] = $this->serverContainer->get($class); - } catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable $e) { + } catch (Throwable $e) { $this->logger->error('Failed to load LanguageModel provider ' . $class, [ 'exception' => $e, ]); From 7a423e416f2a84ec4852ead59c835213ae3fb624 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:51:16 +0200 Subject: [PATCH 046/100] Update lib/public/LanguageModel/ILanguageModelProvider.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit ba950f7e34185139f92e6e81394ab4449d76d105) --- lib/public/LanguageModel/ILanguageModelProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/LanguageModel/ILanguageModelProvider.php index 512fd192aa3df..b3e76ff4d820a 100644 --- a/lib/public/LanguageModel/ILanguageModelProvider.php +++ b/lib/public/LanguageModel/ILanguageModelProvider.php @@ -29,7 +29,7 @@ use RuntimeException; /** - * This is the minimum interface is implemented by apps that + * This is the minimum interface that is implemented by apps that * implement a LanguageModel provider * @since 28.0.0 */ From 9a36bb4f91cf7889541754408bbca584f1d7efd9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:54:23 +0200 Subject: [PATCH 047/100] Update lib/public/LanguageModel/Events/TaskFailedEvent.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit bad124c07bb3ef541c31cfa94131df730c1ac30f) --- lib/public/LanguageModel/Events/TaskFailedEvent.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index efc7d9043c8e3..2737e625e0dcd 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -13,8 +13,10 @@ class TaskFailedEvent extends AbstractLanguageModelEvent { * @param string $errorMessage * @since 28.0.0 */ - public function __construct(ILanguageModelTask $task, - private string $errorMessage) { + public function __construct( + ILanguageModelTask $task, + private string $errorMessage, + ) { parent::__construct($task); } From b0cb5a43c7d2da31b0ca3c8b905bde62d83c2eb0 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:35:38 +0200 Subject: [PATCH 048/100] LLM Migration: Return null if nothing changed Signed-off-by: Marcel Klehr (cherry picked from commit 0909657ea03bd195ba2e0acb1c0a3b63aec79894) --- core/Migrations/Version28000Date20230616104802.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/Migrations/Version28000Date20230616104802.php b/core/Migrations/Version28000Date20230616104802.php index f13f2ef03a6ea..a4520b3c5bcea 100644 --- a/core/Migrations/Version28000Date20230616104802.php +++ b/core/Migrations/Version28000Date20230616104802.php @@ -93,8 +93,10 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->setPrimaryKey(['id'], 'llm_tasks_id_index'); $table->addUniqueIndex(['status', 'type'], 'llm_tasks_status_type'); $table->addIndex(['last_updated'], 'llm_tasks_updated'); + + return $schema; } - return $schema; + return null; } } From 3350811ce390b83841258c0be7ebbd15e33383ff Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:36:26 +0200 Subject: [PATCH 049/100] LLM OCP API: Add missing copyright and strict types Signed-off-by: Marcel Klehr (cherry picked from commit a28d8fac2fb282b752e960e3329acffd1302da20) --- lib/private/LanguageModel/Db/Task.php | 23 +++++++++++++++++++ lib/private/LanguageModel/Db/TaskMapper.php | 23 +++++++++++++++++++ .../LanguageModel/LanguageModelManager.php | 23 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index d4e82024da127..8eb71b0ea5710 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -1,5 +1,28 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + namespace OC\LanguageModel\Db; use OCP\AppFramework\Db\Entity; diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/LanguageModel/Db/TaskMapper.php index f15187d61d523..9b93ea1990f26 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/LanguageModel/Db/TaskMapper.php @@ -1,5 +1,28 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + namespace OC\LanguageModel\Db; use OCP\AppFramework\Db\DoesNotExistException; diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index e331da87b9051..3b27fbae5ac37 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -1,5 +1,28 @@ + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + namespace OC\LanguageModel; use OC\AppFramework\Bootstrap\Coordinator; From 70e65ff7a3cb159b8d96255ea274e93d0b20a804 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:40:19 +0200 Subject: [PATCH 050/100] LLM OCP API: s/getAvailableTasks/getAvailableTaskClasses/ Signed-off-by: Marcel Klehr (cherry picked from commit d56286b8acb319e05eaa8b8a920c76e76da336cf) --- lib/private/LanguageModel/LanguageModelManager.php | 6 +++--- lib/public/LanguageModel/ILanguageModelManager.php | 2 +- tests/lib/LanguageModel/LanguageModelManagerTest.php | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 3b27fbae5ac37..11154849c42aa 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -102,7 +102,7 @@ public function hasProviders(): bool { /** * @inheritDoc */ - public function getAvailableTasks(): array { + public function getAvailableTaskClasses(): array { $tasks = []; foreach ($this->getProviders() as $provider) { $tasks[FreePromptTask::class] = true; @@ -123,11 +123,11 @@ public function getAvailableTasks(): array { * @inheritDoc */ public function getAvailableTaskTypes(): array { - return array_map(fn ($taskClass) => $taskClass::TYPE, $this->getAvailableTasks()); + return array_map(fn ($taskClass) => $taskClass::TYPE, $this->getAvailableTaskClasses()); } public function canHandleTask(ILanguageModelTask $task): bool { - return count(array_filter($this->getAvailableTasks(), fn ($class) => $task instanceof $class)) > 0; + return count(array_filter($this->getAvailableTaskClasses(), fn ($class) => $task instanceof $class)) > 0; } /** diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 055fb69820f0d..438ed1ceb00ed 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -45,7 +45,7 @@ public function hasProviders(): bool; * @return string[] * @since 28.0.0 */ - public function getAvailableTasks(): array; + public function getAvailableTaskClasses(): array; /** * @return string[] diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/LanguageModel/LanguageModelManagerTest.php index 423eb525ba270..1eea6bf2074de 100644 --- a/tests/lib/LanguageModel/LanguageModelManagerTest.php +++ b/tests/lib/LanguageModel/LanguageModelManagerTest.php @@ -163,7 +163,7 @@ protected function setUp(): void { public function testShouldNotHaveAnyProviders() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([]); - $this->assertCount(0, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(0, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(0, $this->languageModelManager->getAvailableTaskTypes()); $this->assertFalse($this->languageModelManager->hasProviders()); $this->expectException(PreConditionNotMetException::class); @@ -174,7 +174,7 @@ public function testProviderShouldBeRegisteredAndRun() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null))); @@ -189,7 +189,7 @@ public function testProviderShouldBeRegisteredAndScheduled() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -259,7 +259,7 @@ public function testMultipleProvidersShouldBeRegisteredAndRunCorrectly() { new ServiceRegistration('test', TestVanillaLanguageModelProvider::class), new ServiceRegistration('test', TestFullLanguageModelProvider::class), ]); - $this->assertCount(3, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(3, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(3, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -287,7 +287,7 @@ public function testTaskFailure() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ new ServiceRegistration('test', TestFailingLanguageModelProvider::class), ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); @@ -354,7 +354,7 @@ public function testOldTasksShouldBeCleanedUp() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTasks()); + $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); $this->assertTrue($this->languageModelManager->hasProviders()); $task = new FreePromptTask('Hello', 'test', null); From 39bc710d17d9057ea6bc8feee2c4ebb9c3284cc9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:42:19 +0200 Subject: [PATCH 051/100] LLM OCP API: Simplify LanguageModelManager#canHandleTask Signed-off-by: Marcel Klehr (cherry picked from commit 945522768e1e0df6684bb15d6dc5ef92d0cf6be6) --- lib/private/LanguageModel/LanguageModelManager.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index 11154849c42aa..c6f1a17229440 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -127,7 +127,12 @@ public function getAvailableTaskTypes(): array { } public function canHandleTask(ILanguageModelTask $task): bool { - return count(array_filter($this->getAvailableTaskClasses(), fn ($class) => $task instanceof $class)) > 0; + foreach ($this->getAvailableTaskClasses() as $class) { + if ($task instanceof $class) { + return true; + } + } + return false; } /** From ddb2726140cebdb7b0bb002c016cee02bafab64d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:44:35 +0200 Subject: [PATCH 052/100] LLM OCP API: Don't lose trace of wrapped exceptions Signed-off-by: Marcel Klehr (cherry picked from commit 05fcf319872988c4cf24e8a98c7611f0356565d5) --- lib/private/LanguageModel/LanguageModelManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index c6f1a17229440..c195669acb433 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -168,11 +168,11 @@ public function runTask(ILanguageModelTask $task): string { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); $task->setStatus(ILanguageModelTask::STATUS_FAILED); $this->taskMapper->update(Task::fromLanguageModelTask($task)); - throw new RuntimeException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage()); + throw new RuntimeException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage(), 0, $e); } } - throw new RuntimeException('Could not transcribe file'); + throw new RuntimeException('Could not run task'); } /** @@ -205,9 +205,9 @@ public function getTask(int $id): ILanguageModelTask { } catch (DoesNotExistException $e) { throw new NotFoundException('Could not find task with the provided id'); } catch (MultipleObjectsReturnedException $e) { - throw new RuntimeException('Could not uniquely identify task with given id'); + throw new RuntimeException('Could not uniquely identify task with given id', 0, $e); } catch (Exception $e) { - throw new RuntimeException('Failure while trying to find task by id: '.$e->getMessage()); + throw new RuntimeException('Failure while trying to find task by id: '.$e->getMessage(), 0, $e); } } } From 4ce77b9d56d83ec01b56aad97879bff7f2131635 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:45:46 +0200 Subject: [PATCH 053/100] LLM OCP API: Explain TaskBackgroundJob#setAllowParallelRuns Signed-off-by: Marcel Klehr (cherry picked from commit e810a8b66b6b39e13dbafba9138cec776609af65) --- lib/private/LanguageModel/TaskBackgroundJob.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index f58510a9e3f2c..0a33f54e89e4e 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -41,6 +41,8 @@ public function __construct( private IEventDispatcher $eventDispatcher, ) { parent::__construct($timeFactory); + // We want to avoid overloading the machine with these jobs + // so we only allow running one job at a time $this->setAllowParallelRuns(false); } From d578974b8165cb8b5ec9095887134359b7e8bc5a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:46:31 +0200 Subject: [PATCH 054/100] LLM OCP API: Simplify TaskBackgroundJob#run catch block Signed-off-by: Marcel Klehr (cherry picked from commit 9ccc65d68c025eb371b1e1877e05593c7a808fea) --- lib/private/LanguageModel/TaskBackgroundJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index 0a33f54e89e4e..9198f40032550 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -56,7 +56,7 @@ protected function run($argument) { try { $this->languageModelManager->runTask($task); $event = new TaskSuccessfulEvent($task); - } catch (\RuntimeException|PreConditionNotMetException|\Throwable $e) { + } catch (\Throwable $e) { $event = new TaskFailedEvent($task, $e->getMessage()); } $this->eventDispatcher->dispatchTyped($event); From 413a4f63ee69bb6419076a702c7187fbc44a9bf1 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:47:27 +0200 Subject: [PATCH 055/100] OCP\Common\NotFoundException: Add param type Signed-off-by: Marcel Klehr (cherry picked from commit 397495346470bd78ff02d05e34c72ac2f2cbcb82) --- lib/public/Common/Exception/NotFoundException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/Common/Exception/NotFoundException.php b/lib/public/Common/Exception/NotFoundException.php index 309efe3e22062..a2cd4db86347b 100644 --- a/lib/public/Common/Exception/NotFoundException.php +++ b/lib/public/Common/Exception/NotFoundException.php @@ -35,7 +35,7 @@ class NotFoundException extends \Exception { * @param string $msg the error message * @since 28.0.0 */ - public function __construct($msg) { + public function __construct(string $msg) { parent::__construct($msg); } } From 5e8605485861081d9cc8e2ebe2f8a0b53c8e6730 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 11:50:01 +0200 Subject: [PATCH 056/100] LLM OCP API: Avoid using OC in OCP Signed-off-by: Marcel Klehr (cherry picked from commit 8f1a4f42557302c7d1c97be0439c47e59133955a) --- lib/private/LanguageModel/Db/Task.php | 9 +++++++++ .../LanguageModel/LanguageModelManager.php | 2 +- .../LanguageModel/AbstractLanguageModelTask.php | 16 ---------------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/LanguageModel/Db/Task.php index 8eb71b0ea5710..4e46f19e8a91f 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/LanguageModel/Db/Task.php @@ -26,6 +26,7 @@ namespace OC\LanguageModel\Db; use OCP\AppFramework\Db\Entity; +use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\ILanguageModelTask; /** @@ -101,4 +102,12 @@ public static function fromLanguageModelTask(ILanguageModelTask $task): Task { ]); return $task; } + + public function toLanguageModelTask(): ILanguageModelTask { + $task = AbstractLanguageModelTask::factory($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); + $task->setId($this->getId()); + $task->setStatus($this->getStatus()); + $task->setOutput($this->getOutput()); + return $task; + } } diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index c195669acb433..c956dfc64abaf 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -201,7 +201,7 @@ public function scheduleTask(ILanguageModelTask $task): void { public function getTask(int $id): ILanguageModelTask { try { $taskEntity = $this->taskMapper->find($id); - return AbstractLanguageModelTask::fromTaskEntity($taskEntity); + return $taskEntity->toLanguageModelTask(); } catch (DoesNotExistException $e) { throw new NotFoundException('Could not find task with the provided id'); } catch (MultipleObjectsReturnedException $e) { diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 884f26e14574a..2b92e6f115d1d 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -25,8 +25,6 @@ namespace OCP\LanguageModel; -use OC\LanguageModel\Db\Task; - /** * This is an abstract LanguageModel task that implements basic * goodies for downstream tasks @@ -161,20 +159,6 @@ public function jsonSerialize() { ]; } - - /** - * @param Task $taskEntity - * @return ILanguageModelTask - * @since 28.0.0 - */ - final public static function fromTaskEntity(Task $taskEntity): ILanguageModelTask { - $task = self::factory($taskEntity->getType(), $taskEntity->getInput(), $taskEntity->getuserId(), $taskEntity->getAppId(), $taskEntity->getIdentifier()); - $task->setId($taskEntity->getId()); - $task->setStatus($taskEntity->getStatus()); - $task->setOutput($taskEntity->getOutput()); - return $task; - } - /** * @param string $type * @param string $input From 17351b64ecd13cae785af1814b0b1228e5439a45 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 12:00:12 +0200 Subject: [PATCH 057/100] LLM OCP API: Fix psalm issues Signed-off-by: Marcel Klehr (cherry picked from commit de1cfaae9c4ff76baaf7185c79821e497099ea54) --- core/Controller/LanguageModelApiController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 335f16c2c0524..a01fe268ab24d 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -73,7 +73,7 @@ public function taskTypes(): DataResponse { * @param string $type The task type * @param string $appId The originating app ID * @param string $identifier An identifier to identify this task - * @return DataResponse|DataResponse + * @return DataResponse|DataResponse * * 200: Task scheduled * 400: Task type does not exist @@ -89,7 +89,7 @@ public function schedule(string $input, string $type, string $appId, string $ide $this->languageModelManager->scheduleTask($task); return new DataResponse([ - 'task' => $task, + 'task' => $task->jsonSerialize(), ]); } catch (PreConditionNotMetException) { return new DataResponse(['message' => $this->l->t('Necessary language model provider is not available')], Http::STATUS_PRECONDITION_FAILED); @@ -102,7 +102,7 @@ public function schedule(string $input, string $type, string $appId, string $ide * * @PublicPage * @param int $id The id of the task - * @return DataResponse|DataResponse + * @return DataResponse|DataResponse * * 200: Task returned * 404: Task not found From 39d59a10b1f658a8739ef71e2c3032499dca8cc9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 12:11:46 +0200 Subject: [PATCH 058/100] LLM OCP API: Fix OpenApi docs Signed-off-by: Marcel Klehr (cherry picked from commit ea4dc4cba8c433f7cf3436794706c0d5b1c57bd9) --- core/Controller/LanguageModelApiController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index a01fe268ab24d..d82d8d0c8b3a3 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -53,7 +53,7 @@ public function __construct( * This endpoint returns all available LanguageModel task types * * @PublicPage - * @return DataResponse + * @return DataResponse * * 200: Task types returned */ @@ -73,7 +73,7 @@ public function taskTypes(): DataResponse { * @param string $type The task type * @param string $appId The originating app ID * @param string $identifier An identifier to identify this task - * @return DataResponse|DataResponse + * @return DataResponse | DataResponse * * 200: Task scheduled * 400: Task type does not exist @@ -102,7 +102,7 @@ public function schedule(string $input, string $type, string $appId, string $ide * * @PublicPage * @param int $id The id of the task - * @return DataResponse|DataResponse + * @return DataResponse | DataResponse * * 200: Task returned * 404: Task not found From 15f08a7487abbdf021a7bd377694e0262de4b4a5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 12:37:01 +0200 Subject: [PATCH 059/100] LLM OCP API: Improve scheduleTask docblock Signed-off-by: Marcel Klehr (cherry picked from commit 9f405a19f782c88310d8cbe1f02eb4e1e8157115) --- lib/public/LanguageModel/ILanguageModelManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 438ed1ceb00ed..32cc4b788bf39 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -62,7 +62,8 @@ public function runTask(ILanguageModelTask $task): string; /** * Will schedule an LLM inference process in the background. The result will become available - * with the \OCP\LanguageModel\Events\TaskFinishedEvent + * with the \OCP\LanguageModel\Events\TaskSuccessfulEvent + * If inference fails a \OCP\LanguageModel\Events\TaskFailedEvent will be dispatched instead * * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @since 28.0.0 From 6b2687eb112d4775b7d09848a6df47daafaf74a9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 12:56:17 +0200 Subject: [PATCH 060/100] LLM OCP API: Change Tests to use EventDispatcher mock Signed-off-by: Marcel Klehr (cherry picked from commit bf2dcd67f3cb83e3bebc472d65fac3bc8755e25d) --- .../LanguageModelManagerTest.php | 49 +++---------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/LanguageModel/LanguageModelManagerTest.php index 1eea6bf2074de..6f8d6cd868d56 100644 --- a/tests/lib/LanguageModel/LanguageModelManagerTest.php +++ b/tests/lib/LanguageModel/LanguageModelManagerTest.php @@ -34,6 +34,7 @@ use OCP\LanguageModel\SummaryTask; use OCP\LanguageModel\TopicsTask; use OCP\PreConditionNotMetException; +use PHPUnit\Framework\Constraint\IsInstanceOf; use Psr\Log\LoggerInterface; use Test\BackgroundJob\DummyJobList; @@ -63,7 +64,7 @@ public function prompt(string $prompt): string { } } -class TestFullLanguageModelProvider implements ILanguageModelProvider, ISummaryProvider, IHeadlineProvider { +class TestAdvancedLanguageModelProvider implements ILanguageModelProvider, ISummaryProvider, IHeadlineProvider { public function getName(): string { return 'TEST Full LLM Provider'; } @@ -90,7 +91,7 @@ protected function setUp(): void { $this->providers = [ TestVanillaLanguageModelProvider::class => new TestVanillaLanguageModelProvider(), - TestFullLanguageModelProvider::class => new TestFullLanguageModelProvider(), + TestAdvancedLanguageModelProvider::class => new TestAdvancedLanguageModelProvider(), TestFailingLanguageModelProvider::class => new TestFailingLanguageModelProvider(), ]; @@ -214,24 +215,8 @@ public function testProviderShouldBeRegisteredAndScheduled() { $this->assertNull($task2->getOutput()); $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); - /** @var IEventDispatcher $this->eventDispatcher */ - $this->eventDispatcher = \OC::$server->get(IEventDispatcher::class); - $successfulEventFired = false; - $this->eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { - $successfulEventFired = true; - $t = $event->getTask(); - $this->assertEquals($task->getId(), $t->getId()); - $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $t->getStatus()); - $this->assertEquals('Hello Free Prompt', $t->getOutput()); - }); - $failedEventFired = false; - $this->eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { - $failedEventFired = true; - $t = $event->getTask(); - $this->assertEquals($task->getId(), $t->getId()); - $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $t->getStatus()); - $this->assertEquals('ERROR', $event->getErrorMessage()); - }); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class)); // run background job $bgJob = new TaskBackgroundJob( @@ -243,8 +228,6 @@ public function testProviderShouldBeRegisteredAndScheduled() { $bgJob->start($this->jobList); $provider = $this->providers[TestVanillaLanguageModelProvider::class]; $this->assertTrue($provider->ran); - $this->assertTrue($successfulEventFired); - $this->assertFalse($failedEventFired); // Task object retrieved from db is up-to-date $task3 = $this->languageModelManager->getTask($task->getId()); @@ -257,7 +240,7 @@ public function testProviderShouldBeRegisteredAndScheduled() { public function testMultipleProvidersShouldBeRegisteredAndRunCorrectly() { $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ new ServiceRegistration('test', TestVanillaLanguageModelProvider::class), - new ServiceRegistration('test', TestFullLanguageModelProvider::class), + new ServiceRegistration('test', TestAdvancedLanguageModelProvider::class), ]); $this->assertCount(3, $this->languageModelManager->getAvailableTaskClasses()); $this->assertCount(3, $this->languageModelManager->getAvailableTaskTypes()); @@ -312,22 +295,8 @@ public function testTaskFailure() { $this->assertNull($task2->getOutput()); $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); - $successfulEventFired = false; - $this->eventDispatcher->addListener(TaskSuccessfulEvent::class, function (TaskSuccessfulEvent $event) use (&$successfulEventFired, $task) { - $successfulEventFired = true; - $t = $event->getTask(); - $this->assertEquals($task->getId(), $t->getId()); - $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $t->getStatus()); - $this->assertEquals('Hello Free Prompt', $t->getOutput()); - }); - $failedEventFired = false; - $this->eventDispatcher->addListener(TaskFailedEvent::class, function (TaskFailedEvent $event) use (&$failedEventFired, $task) { - $failedEventFired = true; - $t = $event->getTask(); - $this->assertEquals($task->getId(), $t->getId()); - $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $t->getStatus()); - $this->assertEquals('ERROR', $event->getErrorMessage()); - }); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class)); // run background job $bgJob = new TaskBackgroundJob( @@ -339,8 +308,6 @@ public function testTaskFailure() { $bgJob->start($this->jobList); $provider = $this->providers[TestFailingLanguageModelProvider::class]; $this->assertTrue($provider->ran); - $this->assertTrue($failedEventFired); - $this->assertFalse($successfulEventFired); // Task object retrieved from db is up-to-date $task3 = $this->languageModelManager->getTask($task->getId()); From a39e3c90e92ec9dca72129d85757af013ab8471d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 13:34:16 +0200 Subject: [PATCH 061/100] cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit a4578cd99519a63f1b0b5292ec369c0f2dada91f) --- core/Controller/LanguageModelApiController.php | 2 -- lib/private/LanguageModel/LanguageModelManager.php | 3 --- lib/private/LanguageModel/TaskBackgroundJob.php | 1 - 3 files changed, 6 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index d82d8d0c8b3a3..5bb7b31c3c79f 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -34,9 +34,7 @@ use OCP\IRequest; use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\ILanguageModelManager; -use OCP\LanguageModel\ILanguageModelTask; use OCP\PreConditionNotMetException; -use Punic\Data; class LanguageModelApiController extends \OCP\AppFramework\OCSController { public function __construct( diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/LanguageModel/LanguageModelManager.php index c956dfc64abaf..970d968c8839f 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/LanguageModel/LanguageModelManager.php @@ -34,7 +34,6 @@ use OCP\Common\Exception\NotFoundException; use OCP\DB\Exception; use OCP\IServerContainer; -use OCP\LanguageModel\AbstractLanguageModelTask; use OCP\LanguageModel\FreePromptTask; use OCP\LanguageModel\HeadlineTask; use OCP\LanguageModel\IHeadlineProvider; @@ -46,8 +45,6 @@ use OCP\LanguageModel\SummaryTask; use OCP\LanguageModel\TopicsTask; use OCP\PreConditionNotMetException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; use RuntimeException; use Throwable; diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/LanguageModel/TaskBackgroundJob.php index 9198f40032550..5ac37baf3326e 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/LanguageModel/TaskBackgroundJob.php @@ -32,7 +32,6 @@ use OCP\LanguageModel\Events\TaskFailedEvent; use OCP\LanguageModel\Events\TaskSuccessfulEvent; use OCP\LanguageModel\ILanguageModelManager; -use OCP\PreConditionNotMetException; class TaskBackgroundJob extends QueuedJob { public function __construct( From 1ad0a2960e7d568fb40dc36430444f2ef70eaace Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 13:36:27 +0200 Subject: [PATCH 062/100] LanguageModelApiController: Use jsonSerialize method to help psalm Signed-off-by: Marcel Klehr (cherry picked from commit 49ea56b306d0675958ece8bf043dcc06cee74ef9) --- core/Controller/LanguageModelApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 5bb7b31c3c79f..dcc8e59e2cc23 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -115,7 +115,7 @@ public function getTask(int $id): DataResponse { } return new DataResponse([ - 'task' => $task, + 'task' => $task->jsonSerialize(), ]); } catch (NotFoundException $e) { return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); From 8acb1dc47511a99d7923968be9dde986490d622d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 7 Jul 2023 13:46:03 +0200 Subject: [PATCH 063/100] Since 27.1.0 Signed-off-by: Marcel Klehr (cherry picked from commit 069962d04fb51fd52f891297b9096dd7143eaa18) --- .../Bootstrap/IRegistrationContext.php | 2 +- .../Common/Exception/NotFoundException.php | 4 +- .../AbstractLanguageModelTask.php | 28 ++++++------- .../Events/AbstractLanguageModelEvent.php | 6 +-- .../LanguageModel/Events/TaskFailedEvent.php | 6 +-- .../Events/TaskSuccessfulEvent.php | 4 +- lib/public/LanguageModel/FreePromptTask.php | 10 ++--- lib/public/LanguageModel/HeadlineTask.php | 10 ++--- .../LanguageModel/IHeadlineProvider.php | 4 +- .../LanguageModel/ILanguageModelManager.php | 14 +++---- .../LanguageModel/ILanguageModelProvider.php | 6 +-- .../LanguageModel/ILanguageModelTask.php | 40 +++++++++---------- lib/public/LanguageModel/ISummaryProvider.php | 4 +- lib/public/LanguageModel/ITopicsProvider.php | 4 +- lib/public/LanguageModel/SummaryTask.php | 10 ++--- lib/public/LanguageModel/TopicsTask.php | 10 ++--- 16 files changed, 81 insertions(+), 81 deletions(-) diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index a4b874b754517..66435d4593466 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -226,7 +226,7 @@ public function registerSpeechToTextProvider(string $providerClass): void; * * @param string $providerClass * @psalm-param class-string $providerClass - * @since 28.0.0 + * @since 27.1.0 */ public function registerLanguageModelProvider(string $providerClass): void; diff --git a/lib/public/Common/Exception/NotFoundException.php b/lib/public/Common/Exception/NotFoundException.php index a2cd4db86347b..a30e1c42b8b5e 100644 --- a/lib/public/Common/Exception/NotFoundException.php +++ b/lib/public/Common/Exception/NotFoundException.php @@ -27,13 +27,13 @@ /** * This is thrown whenever something was expected to exist but doesn't * - * @since 28.0.0 + * @since 27.1.0 */ class NotFoundException extends \Exception { /** * Constructor * @param string $msg the error message - * @since 28.0.0 + * @since 27.1.0 */ public function __construct(string $msg) { parent::__construct($msg); diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/LanguageModel/AbstractLanguageModelTask.php index 2b92e6f115d1d..91b81b9615b84 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/LanguageModel/AbstractLanguageModelTask.php @@ -46,7 +46,7 @@ abstract class AbstractLanguageModelTask implements ILanguageModelTask { * @param string $appId * @param string|null $userId * @param string $identifier An arbitrary identifier for this task. max length: 255 chars - * @since 28.0.0 + * @since 27.1.0 */ final public function __construct( protected string $input, @@ -58,13 +58,13 @@ final public function __construct( /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ abstract public function getType(): string; /** * @return string|null - * @since 28.0.0 + * @since 27.1.0 */ final public function getOutput(): ?string { return $this->output; @@ -72,7 +72,7 @@ final public function getOutput(): ?string { /** * @param string|null $output - * @since 28.0.0 + * @since 27.1.0 */ final public function setOutput(?string $output): void { $this->output = $output; @@ -80,7 +80,7 @@ final public function setOutput(?string $output): void { /** * @psalm-return ILanguageModelTask::STATUS_* - * @since 28.0.0 + * @since 27.1.0 */ final public function getStatus(): int { return $this->status; @@ -88,7 +88,7 @@ final public function getStatus(): int { /** * @psalm-param ILanguageModelTask::STATUS_* $status - * @since 28.0.0 + * @since 27.1.0 */ final public function setStatus(int $status): void { $this->status = $status; @@ -96,7 +96,7 @@ final public function setStatus(int $status): void { /** * @return int|null - * @since 28.0.0 + * @since 27.1.0 */ final public function getId(): ?int { return $this->id; @@ -104,7 +104,7 @@ final public function getId(): ?int { /** * @param int|null $id - * @since 28.0.0 + * @since 27.1.0 */ final public function setId(?int $id): void { $this->id = $id; @@ -112,7 +112,7 @@ final public function setId(?int $id): void { /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ final public function getInput(): string { return $this->input; @@ -120,7 +120,7 @@ final public function getInput(): string { /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ final public function getAppId(): string { return $this->appId; @@ -128,7 +128,7 @@ final public function getAppId(): string { /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ final public function getIdentifier(): string { return $this->identifier; @@ -136,7 +136,7 @@ final public function getIdentifier(): string { /** * @return string|null - * @since 28.0.0 + * @since 27.1.0 */ final public function getUserId(): ?string { return $this->userId; @@ -144,7 +144,7 @@ final public function getUserId(): ?string { /** * @return array - * @since 28.0.0 + * @since 27.1.0 */ public function jsonSerialize() { return [ @@ -167,7 +167,7 @@ public function jsonSerialize() { * @param string $identifier * @return ILanguageModelTask * @throws \InvalidArgumentException - * @since 28.0.0 + * @since 27.1.0 */ final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier = ''): ILanguageModelTask { if (!in_array($type, array_keys(self::TYPES))) { diff --git a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php index 218a448008187..c8abc7373ebb0 100644 --- a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php +++ b/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php @@ -29,11 +29,11 @@ use OCP\LanguageModel\ILanguageModelTask; /** - * @since 28.0.0 + * @since 27.1.0 */ abstract class AbstractLanguageModelEvent extends Event { /** - * @since 28.0.0 + * @since 27.1.0 */ public function __construct( private ILanguageModelTask $task @@ -43,7 +43,7 @@ public function __construct( /** * @return ILanguageModelTask - * @since 28.0.0 + * @since 27.1.0 */ public function getTask(): ILanguageModelTask { return $this->task; diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/LanguageModel/Events/TaskFailedEvent.php index 2737e625e0dcd..f42203a6e48de 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/LanguageModel/Events/TaskFailedEvent.php @@ -5,13 +5,13 @@ use OCP\LanguageModel\ILanguageModelTask; /** - * @since 28.0.0 + * @since 27.1.0 */ class TaskFailedEvent extends AbstractLanguageModelEvent { /** * @param ILanguageModelTask $task * @param string $errorMessage - * @since 28.0.0 + * @since 27.1.0 */ public function __construct( ILanguageModelTask $task, @@ -22,7 +22,7 @@ public function __construct( /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function getErrorMessage(): string { return $this->errorMessage; diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php index ec8a8586a3ec1..77a61ac5c6e08 100644 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php @@ -5,12 +5,12 @@ use OCP\LanguageModel\ILanguageModelTask; /** - * @since 28.0.0 + * @since 27.1.0 */ class TaskSuccessfulEvent extends AbstractLanguageModelEvent { /** * @param ILanguageModelTask $task - * @since 28.0.0 + * @since 27.1.0 */ public function __construct(ILanguageModelTask $task) { parent::__construct($task); diff --git a/lib/public/LanguageModel/FreePromptTask.php b/lib/public/LanguageModel/FreePromptTask.php index 3de215784ab70..560d6e7d1fbb8 100644 --- a/lib/public/LanguageModel/FreePromptTask.php +++ b/lib/public/LanguageModel/FreePromptTask.php @@ -26,18 +26,18 @@ namespace OCP\LanguageModel; /** - * @since 28.0.0 + * @since 27.1.0 * @template-extends AbstractLanguageModelTask */ final class FreePromptTask extends AbstractLanguageModelTask { /** - * @since 28.0.0 + * @since 27.1.0 */ public const TYPE = 'free_prompt'; /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function visitProvider(ILanguageModelProvider $provider): string { return $provider->prompt($this->getInput()); @@ -45,7 +45,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return true; @@ -53,7 +53,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function getType(): string { return self::TYPE; diff --git a/lib/public/LanguageModel/HeadlineTask.php b/lib/public/LanguageModel/HeadlineTask.php index e66c8893d8301..4c62b9722a992 100644 --- a/lib/public/LanguageModel/HeadlineTask.php +++ b/lib/public/LanguageModel/HeadlineTask.php @@ -28,18 +28,18 @@ /** * This LanguageModel Task represents headline generation * which generates a headline for the passed text - * @since 28.0.0 + * @since 27.1.0 * @template-extends AbstractLanguageModelTask */ final class HeadlineTask extends AbstractLanguageModelTask { /** - * @since 28.0.0 + * @since 27.1.0 */ public const TYPE = 'headline'; /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { @@ -50,7 +50,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof IHeadlineProvider; @@ -58,7 +58,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function getType(): string { return self::TYPE; diff --git a/lib/public/LanguageModel/IHeadlineProvider.php b/lib/public/LanguageModel/IHeadlineProvider.php index d1fe7504fdfb7..30185f4d4b3d9 100644 --- a/lib/public/LanguageModel/IHeadlineProvider.php +++ b/lib/public/LanguageModel/IHeadlineProvider.php @@ -31,13 +31,13 @@ /** * This LanguageModel Provider represents headline generation * which generates a headline for the passed text - * @since 28.0.0 + * @since 27.1.0 */ interface IHeadlineProvider extends ILanguageModelProvider { /** * @param string $text The text to find headline for * @returns string the headline - * @since 28.0.0 + * @since 27.1.0 * @throws RuntimeException If the text could not be transcribed */ public function findHeadline(string $text): string; diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/LanguageModel/ILanguageModelManager.php index 32cc4b788bf39..0afc99b91ab66 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/LanguageModel/ILanguageModelManager.php @@ -33,30 +33,30 @@ /** * API surface for apps interacting with and making use of LanguageModel providers * without known which providers are installed - * @since 28.0.0 + * @since 27.1.0 */ interface ILanguageModelManager { /** - * @since 28.0.0 + * @since 27.1.0 */ public function hasProviders(): bool; /** * @return string[] - * @since 28.0.0 + * @since 27.1.0 */ public function getAvailableTaskClasses(): array; /** * @return string[] - * @since 28.0.0 + * @since 27.1.0 */ public function getAvailableTaskTypes(): array; /** * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @throws RuntimeException If something else failed - * @since 28.0.0 + * @since 27.1.0 */ public function runTask(ILanguageModelTask $task): string; @@ -66,7 +66,7 @@ public function runTask(ILanguageModelTask $task): string; * If inference fails a \OCP\LanguageModel\Events\TaskFailedEvent will be dispatched instead * * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called - * @since 28.0.0 + * @since 27.1.0 */ public function scheduleTask(ILanguageModelTask $task) : void; @@ -75,7 +75,7 @@ public function scheduleTask(ILanguageModelTask $task) : void; * @return ILanguageModelTask * @throws RuntimeException If the query failed * @throws NotFoundException If the task could not be found - * @since 28.0.0 + * @since 27.1.0 */ public function getTask(int $id): ILanguageModelTask; } diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/LanguageModel/ILanguageModelProvider.php index b3e76ff4d820a..34e7eb6c4e59d 100644 --- a/lib/public/LanguageModel/ILanguageModelProvider.php +++ b/lib/public/LanguageModel/ILanguageModelProvider.php @@ -31,18 +31,18 @@ /** * This is the minimum interface that is implemented by apps that * implement a LanguageModel provider - * @since 28.0.0 + * @since 27.1.0 */ interface ILanguageModelProvider { /** - * @since 28.0.0 + * @since 27.1.0 */ public function getName(): string; /** * @param string $prompt The prompt to call the model with * @return string the output - * @since 28.0.0 + * @since 27.1.0 * @throws RuntimeException If the text could not be transcribed */ public function prompt(string $prompt): string; diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php index b8f0f96695d72..0f552c8de54d8 100644 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ b/lib/public/LanguageModel/ILanguageModelTask.php @@ -26,33 +26,33 @@ namespace OCP\LanguageModel; /** - * @since 28.0.0 + * @since 27.1.0 * @template T of ILanguageModelProvider */ interface ILanguageModelTask extends \JsonSerializable { /** - * @since 28.0.0 + * @since 27.1.0 */ public const STATUS_FAILED = 4; /** - * @since 28.0.0 + * @since 27.1.0 */ public const STATUS_SUCCESSFUL = 3; /** - * @since 28.0.0 + * @since 27.1.0 */ public const STATUS_RUNNING = 2; /** - * @since 28.0.0 + * @since 27.1.0 */ public const STATUS_SCHEDULED = 1; /** - * @since 28.0.0 + * @since 27.1.0 */ public const STATUS_UNKNOWN = 0; /** - * @since 28.0.0 + * @since 27.1.0 */ public const TYPES = [ FreePromptTask::TYPE => FreePromptTask::class, @@ -65,7 +65,7 @@ interface ILanguageModelTask extends \JsonSerializable { * @psalm-param T $provider * @param ILanguageModelProvider $provider * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function visitProvider(ILanguageModelProvider $provider): string; @@ -73,74 +73,74 @@ public function visitProvider(ILanguageModelProvider $provider): string; * @psalm-param T $provider * @param ILanguageModelProvider $provider * @return bool - * @since 28.0.0 + * @since 27.1.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool; /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function getType(): string; /** * @return ILanguageModelTask::STATUS_* - * @since 28.0.0 + * @since 27.1.0 */ public function getStatus(): int; /** * @param ILanguageModelTask::STATUS_* $status - * @since 28.0.0 + * @since 27.1.0 */ public function setStatus(int $status): void; /** * @param int|null $id - * @since 28.0.0 + * @since 27.1.0 */ public function setId(?int $id): void; /** * @return int|null - * @since 28.0.0 + * @since 27.1.0 */ public function getId(): ?int; /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function getInput(): string; /** * @param string|null $output - * @since 28.0.0 + * @since 27.1.0 */ public function setOutput(?string $output): void; /** * @return null|string - * @since 28.0.0 + * @since 27.1.0 */ public function getOutput(): ?string; /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function getAppId(): string; /** * @return string - * @since 28.0.0 + * @since 27.1.0 */ public function getIdentifier(): string; /** * @return string|null - * @since 28.0.0 + * @since 27.1.0 */ public function getUserId(): ?string; } diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php index 0b49258964dc5..c286e74eb1949 100644 --- a/lib/public/LanguageModel/ISummaryProvider.php +++ b/lib/public/LanguageModel/ISummaryProvider.php @@ -31,13 +31,13 @@ /** * This LanguageModel Provider implements summarization * which sums up the passed text. - * @since 28.0.0 + * @since 27.1.0 */ interface ISummaryProvider extends ILanguageModelProvider { /** * @param string $text The text to summarize * @returns string the summary - * @since 28.0.0 + * @since 27.1.0 * @throws RuntimeException If the text could not be transcribed */ public function summarize(string $text): string; diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/LanguageModel/ITopicsProvider.php index a15cfba698fd4..f061976a3ba43 100644 --- a/lib/public/LanguageModel/ITopicsProvider.php +++ b/lib/public/LanguageModel/ITopicsProvider.php @@ -31,13 +31,13 @@ /** * This LanguageModel Provider implements topics synthesis * which outputs comma-separated topics for the passed text - * @since 28.0.0 + * @since 27.1.0 */ interface ITopicsProvider extends ILanguageModelProvider { /** * @param string $text The text to find topics for * @returns string the topics, comma separated - * @since 28.0.0 + * @since 27.1.0 * @throws RuntimeException If the text could not be transcribed */ public function findTopics(string $text): string; diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php index 4504cdff7503c..47864532ae309 100644 --- a/lib/public/LanguageModel/SummaryTask.php +++ b/lib/public/LanguageModel/SummaryTask.php @@ -28,18 +28,18 @@ /** * This is an absctract LanguageModel Task represents summarization * which sums up the passed text. - * @since 28.0.0 + * @since 27.1.0 * @template-extends AbstractLanguageModelTask */ final class SummaryTask extends AbstractLanguageModelTask { /** - * @since 28.0.0 + * @since 27.1.0 */ public const TYPE = 'summarize'; /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { @@ -50,7 +50,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ISummaryProvider; @@ -58,7 +58,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function getType(): string { return self::TYPE; diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php index ccf5d512a8a97..ab2c5916061c3 100644 --- a/lib/public/LanguageModel/TopicsTask.php +++ b/lib/public/LanguageModel/TopicsTask.php @@ -28,18 +28,18 @@ /** * This LanguageModel Task represents topics synthesis * which outputs comma-separated topics for the passed text - * @since 28.0.0 + * @since 27.1.0 * @template-extends AbstractLanguageModelTask */ final class TopicsTask extends AbstractLanguageModelTask { /** - * @since 28.0.0 + * @since 27.1.0 */ public const TYPE = 'topics'; /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function visitProvider(ILanguageModelProvider $provider): string { if (!$this->canUseProvider($provider)) { @@ -50,7 +50,7 @@ public function visitProvider(ILanguageModelProvider $provider): string { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function canUseProvider(ILanguageModelProvider $provider): bool { return $provider instanceof ITopicsProvider; @@ -58,7 +58,7 @@ public function canUseProvider(ILanguageModelProvider $provider): bool { /** * @inheritDoc - * @since 28.0.0 + * @since 27.1.0 */ public function getType(): string { return self::TYPE; From fbd178ab8bd3b67fde8546ac4b8854d63bf326d6 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 11 Jul 2023 12:41:31 +0200 Subject: [PATCH 064/100] Update core/Controller/LanguageModelApiController.php Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit b7c3b50e411bf949a73f551af6d8582bdf390b08) --- core/Controller/LanguageModelApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index dcc8e59e2cc23..b343cb4315bdf 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -71,7 +71,7 @@ public function taskTypes(): DataResponse { * @param string $type The task type * @param string $appId The originating app ID * @param string $identifier An identifier to identify this task - * @return DataResponse | DataResponse + * @return DataResponse| DataResponse * * 200: Task scheduled * 400: Task type does not exist From 6f5119c0938d949fceb1e0f17508a2a96effdc7f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 11 Jul 2023 14:37:14 +0200 Subject: [PATCH 065/100] Fix openapi docs Signed-off-by: Marcel Klehr (cherry picked from commit 48c820653840a50f7c67801418222b718e9be40b) --- core/Controller/LanguageModelApiController.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index b343cb4315bdf..f2a510bec802f 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -71,7 +71,7 @@ public function taskTypes(): DataResponse { * @param string $type The task type * @param string $appId The originating app ID * @param string $identifier An identifier to identify this task - * @return DataResponse| DataResponse + * @return DataResponse| DataResponse * * 200: Task scheduled * 400: Task type does not exist @@ -86,8 +86,11 @@ public function schedule(string $input, string $type, string $appId, string $ide try { $this->languageModelManager->scheduleTask($task); + /** @var array{id: int|null, type: string, status: int, userId: string|null, appId: string, input: string, output: string|null, identifier: string} $json */ + $json = $task->jsonSerialize(); + return new DataResponse([ - 'task' => $task->jsonSerialize(), + 'task' => $json, ]); } catch (PreConditionNotMetException) { return new DataResponse(['message' => $this->l->t('Necessary language model provider is not available')], Http::STATUS_PRECONDITION_FAILED); @@ -100,7 +103,7 @@ public function schedule(string $input, string $type, string $appId, string $ide * * @PublicPage * @param int $id The id of the task - * @return DataResponse | DataResponse + * @return DataResponse | DataResponse * * 200: Task returned * 404: Task not found @@ -114,8 +117,11 @@ public function getTask(int $id): DataResponse { return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); } + /** @var array{id: int|null, type: string, status: int, userId: string|null, appId: string, input: string, output: string|null, identifier: string} $json */ + $json = $task->jsonSerialize(); + return new DataResponse([ - 'task' => $task->jsonSerialize(), + 'task' => $json, ]); } catch (NotFoundException $e) { return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); From 5d975b96e77f55b06836c217346a4c642e14acd5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 11 Jul 2023 15:28:22 +0200 Subject: [PATCH 066/100] Update core/Controller/LanguageModelApiController.php Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit d430cbbfca86c9e21df9cc4013f7f9b9ee86fe59) --- core/Controller/LanguageModelApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index f2a510bec802f..86023bb3fa4c6 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -71,7 +71,7 @@ public function taskTypes(): DataResponse { * @param string $type The task type * @param string $appId The originating app ID * @param string $identifier An identifier to identify this task - * @return DataResponse| DataResponse + * @return DataResponse|DataResponse * * 200: Task scheduled * 400: Task type does not exist From 696a45ddf1d460de7ffa6f252912375efd7e190e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 11 Jul 2023 15:28:37 +0200 Subject: [PATCH 067/100] Update core/Controller/LanguageModelApiController.php Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit bd45c436ebc710da2309dca232173d8133e9e06a) --- core/Controller/LanguageModelApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/LanguageModelApiController.php index 86023bb3fa4c6..74ed26e604a41 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/LanguageModelApiController.php @@ -103,7 +103,7 @@ public function schedule(string $input, string $type, string $appId, string $ide * * @PublicPage * @param int $id The id of the task - * @return DataResponse | DataResponse + * @return DataResponse|DataResponse * * 200: Task returned * 404: Task not found From cf2c42ae36a3c7280887bd3f15329739f9a6d221 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 14 Jul 2023 15:59:50 +0200 Subject: [PATCH 068/100] Massive refactoring: Turn LanguageModel OCP API into TextProcessing API Signed-off-by: Marcel Klehr (cherry picked from commit ffe27ce14ca74b509c8721c9fba7c759498fa471) --- ...er.php => TextProcessingApiController.php} | 51 +++-- core/routes.php | 6 +- .../Bootstrap/RegistrationContext.php | 20 +- .../Repair/AddRemoveOldTasksBackgroundJob.php | 2 +- lib/private/Server.php | 4 +- lib/private/Setup.php | 2 +- .../Db/Task.php | 11 +- .../Db/TaskMapper.php | 2 +- .../Manager.php} | 92 +++----- .../RemoveOldTasksBackgroundJob.php | 4 +- .../TaskBackgroundJob.php | 16 +- .../Bootstrap/IRegistrationContext.php | 10 +- .../Events/TaskSuccessfulEvent.php | 18 -- lib/public/LanguageModel/HeadlineTask.php | 66 ------ .../LanguageModel/IHeadlineProvider.php | 44 ---- .../LanguageModel/ILanguageModelTask.php | 146 ------------- lib/public/LanguageModel/ISummaryProvider.php | 44 ---- lib/public/LanguageModel/SummaryTask.php | 66 ------ lib/public/LanguageModel/TopicsTask.php | 66 ------ .../Events/AbstractTextProcessingEvent.php} | 13 +- .../Events/TaskFailedEvent.php | 10 +- .../Events/TaskSuccessfulEvent.php | 18 ++ .../FreePromptTaskType.php} | 33 ++- .../HeadlineTaskType.php} | 36 +++- .../IManager.php} | 22 +- lib/public/TextProcessing/IProvider.php | 61 ++++++ .../ITaskType.php} | 22 +- lib/public/TextProcessing/SummaryTaskType.php | 60 ++++++ .../Task.php} | 93 ++++++-- lib/public/TextProcessing/TopicsTaskType.php | 60 ++++++ .../TextProcessingTest.php} | 203 +++++++++--------- 31 files changed, 553 insertions(+), 748 deletions(-) rename core/Controller/{LanguageModelApiController.php => TextProcessingApiController.php} (73%) rename lib/private/{LanguageModel => TextProcessing}/Db/Task.php (88%) rename lib/private/{LanguageModel => TextProcessing}/Db/TaskMapper.php (98%) rename lib/private/{LanguageModel/LanguageModelManager.php => TextProcessing/Manager.php} (61%) rename lib/private/{LanguageModel => TextProcessing}/RemoveOldTasksBackgroundJob.php (96%) rename lib/private/{LanguageModel => TextProcessing}/TaskBackgroundJob.php (82%) delete mode 100644 lib/public/LanguageModel/Events/TaskSuccessfulEvent.php delete mode 100644 lib/public/LanguageModel/HeadlineTask.php delete mode 100644 lib/public/LanguageModel/IHeadlineProvider.php delete mode 100644 lib/public/LanguageModel/ILanguageModelTask.php delete mode 100644 lib/public/LanguageModel/ISummaryProvider.php delete mode 100644 lib/public/LanguageModel/SummaryTask.php delete mode 100644 lib/public/LanguageModel/TopicsTask.php rename lib/public/{LanguageModel/Events/AbstractLanguageModelEvent.php => TextProcessing/Events/AbstractTextProcessingEvent.php} (81%) rename lib/public/{LanguageModel => TextProcessing}/Events/TaskFailedEvent.php (61%) create mode 100644 lib/public/TextProcessing/Events/TaskSuccessfulEvent.php rename lib/public/{LanguageModel/FreePromptTask.php => TextProcessing/FreePromptTaskType.php} (64%) rename lib/public/{LanguageModel/ITopicsProvider.php => TextProcessing/HeadlineTaskType.php} (64%) rename lib/public/{LanguageModel/ILanguageModelManager.php => TextProcessing/IManager.php} (83%) create mode 100644 lib/public/TextProcessing/IProvider.php rename lib/public/{LanguageModel/ILanguageModelProvider.php => TextProcessing/ITaskType.php} (71%) create mode 100644 lib/public/TextProcessing/SummaryTaskType.php rename lib/public/{LanguageModel/AbstractLanguageModelTask.php => TextProcessing/Task.php} (62%) create mode 100644 lib/public/TextProcessing/TopicsTaskType.php rename tests/lib/{LanguageModel/LanguageModelManagerTest.php => TextProcessing/TextProcessingTest.php} (53%) diff --git a/core/Controller/LanguageModelApiController.php b/core/Controller/TextProcessingApiController.php similarity index 73% rename from core/Controller/LanguageModelApiController.php rename to core/Controller/TextProcessingApiController.php index 74ed26e604a41..7cc7199dfbdc8 100644 --- a/core/Controller/LanguageModelApiController.php +++ b/core/Controller/TextProcessingApiController.php @@ -32,17 +32,24 @@ use OCP\Common\Exception\NotFoundException; use OCP\IL10N; use OCP\IRequest; -use OCP\LanguageModel\AbstractLanguageModelTask; -use OCP\LanguageModel\ILanguageModelManager; +use OCP\TextProcessing\ITaskType; +use OCP\TextProcessing\Task; +use OCP\TextProcessing\IManager; use OCP\PreConditionNotMetException; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; -class LanguageModelApiController extends \OCP\AppFramework\OCSController { +class TextProcessingApiController extends \OCP\AppFramework\OCSController { public function __construct( - string $appName, - IRequest $request, - private ILanguageModelManager $languageModelManager, - private IL10N $l, - private ?string $userId, + string $appName, + IRequest $request, + private IManager $languageModelManager, + private IL10N $l, + private ?string $userId, + private ContainerInterface $container, + private LoggerInterface $logger, ) { parent::__construct($appName, $request); } @@ -51,13 +58,31 @@ public function __construct( * This endpoint returns all available LanguageModel task types * * @PublicPage - * @return DataResponse + * @return DataResponse}, array{}> * * 200: Task types returned */ public function taskTypes(): DataResponse { + $typeClasses = $this->languageModelManager->getAvailableTaskTypes(); + /** @var list $types */ + $types = []; + foreach ($typeClasses as $typeClass) { + /** @var ITaskType $object */ + try { + $object = $this->container->get($typeClass); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) { + $this->logger->warning('Could not find ' . $typeClass, ['exception' => $e]); + continue; + } + $types[] = [ + 'id' => $typeClass, + 'name' => $object->getName(), + 'description' => $object->getDescription(), + ]; + } + return new DataResponse([ - 'types' => $this->languageModelManager->getAvailableTaskTypes(), + 'types' => $types, ]); } @@ -79,14 +104,13 @@ public function taskTypes(): DataResponse { */ public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { try { - $task = AbstractLanguageModelTask::factory($type, $input, $this->userId, $appId, $identifier); - } catch (InvalidArgumentException $e) { + $task = Task::factory($type, $input, $this->userId, $appId, $identifier); + } catch (InvalidArgumentException) { return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); } try { $this->languageModelManager->scheduleTask($task); - /** @var array{id: int|null, type: string, status: int, userId: string|null, appId: string, input: string, output: string|null, identifier: string} $json */ $json = $task->jsonSerialize(); return new DataResponse([ @@ -117,7 +141,6 @@ public function getTask(int $id): DataResponse { return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); } - /** @var array{id: int|null, type: string, status: int, userId: string|null, appId: string, input: string, output: string|null, identifier: string} $json */ $json = $task->jsonSerialize(); return new DataResponse([ diff --git a/core/routes.php b/core/routes.php index 24af95cd7af3a..4790f32af3230 100644 --- a/core/routes.php +++ b/core/routes.php @@ -146,9 +146,9 @@ ['root' => '/translation', 'name' => 'TranslationApi#languages', 'url' => '/languages', 'verb' => 'GET'], ['root' => '/translation', 'name' => 'TranslationApi#translate', 'url' => '/translate', 'verb' => 'POST'], - ['root' => '/languagemodel', 'name' => 'LanguageModelApi#taskTypes', 'url' => '/tasktypes', 'verb' => 'GET'], - ['root' => '/languagemodel', 'name' => 'LanguageModelApi#schedule', 'url' => '/schedule', 'verb' => 'POST'], - ['root' => '/languagemodel', 'name' => 'LanguageModelApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'], + ['root' => '/textprocessing', 'name' => 'TextProcessingApi#taskTypes', 'url' => '/tasktypes', 'verb' => 'GET'], + ['root' => '/textprocessing', 'name' => 'TextProcessingApi#schedule', 'url' => '/schedule', 'verb' => 'POST'], + ['root' => '/textprocessing', 'name' => 'TextProcessingApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'], ], ]); diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 67e8b390c158b..5aea2a7a744b0 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -33,7 +33,7 @@ use OCP\Calendar\Resource\IBackend as IResourceBackend; use OCP\Calendar\Room\IBackend as IRoomBackend; use OCP\Collaboration\Reference\IReferenceProvider; -use OCP\LanguageModel\ILanguageModelProvider; +use OCP\TextProcessing\IProvider as ITextProcessingProvider; use OCP\SpeechToText\ISpeechToTextProvider; use OCP\Talk\ITalkBackend; use OCP\Translation\ITranslationProvider; @@ -116,8 +116,8 @@ class RegistrationContext { /** @var ServiceRegistration[] */ private $speechToTextProviders = []; - /** @var ServiceRegistration[] */ - private $languageModelProviders = []; + /** @var ServiceRegistration[] */ + private $textProcessingProviders = []; /** @var ServiceRegistration[] */ private $templateProviders = []; @@ -266,8 +266,8 @@ public function registerSpeechToTextProvider(string $providerClass): void { $providerClass ); } - public function registerLanguageModelProvider(string $providerClass): void { - $this->context->registerLanguageModelProvider( + public function registerTextProcessingProvider(string $providerClass): void { + $this->context->registerTextProcessingProvider( $this->appId, $providerClass ); @@ -439,8 +439,8 @@ public function registerSpeechToTextProvider(string $appId, string $class): void $this->speechToTextProviders[] = new ServiceRegistration($appId, $class); } - public function registerLanguageModelProvider(string $appId, string $class): void { - $this->languageModelProviders[] = new ServiceRegistration($appId, $class); + public function registerTextProcessingProvider(string $appId, string $class): void { + $this->textProcessingProviders[] = new ServiceRegistration($appId, $class); } public function registerTemplateProvider(string $appId, string $class): void { @@ -722,10 +722,10 @@ public function getSpeechToTextProviders(): array { } /** - * @return ServiceRegistration[] + * @return ServiceRegistration[] */ - public function getLanguageModelProviders(): array { - return $this->languageModelProviders; + public function getTextProcessingProviders(): array { + return $this->textProcessingProviders; } /** diff --git a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php index 713192b06f957..94ae39f2183e2 100644 --- a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php +++ b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php @@ -25,7 +25,7 @@ */ namespace OC\Repair; -use OC\LanguageModel\RemoveOldTasksBackgroundJob; +use OC\TextProcessing\RemoveOldTasksBackgroundJob; use OCP\BackgroundJob\IJobList; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; diff --git a/lib/private/Server.php b/lib/private/Server.php index d1f18a1235f03..516dc0b6e8b0c 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -110,7 +110,6 @@ use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; -use OC\LanguageModel\LanguageModelManager; use OC\LDAP\NullLDAPProviderFactory; use OC\KnownUser\KnownUserService; use OC\Lock\DBLockingProvider; @@ -229,7 +228,6 @@ use OCP\IUserManager; use OCP\IUserSession; use OCP\L10N\IFactory; -use OCP\LanguageModel\ILanguageModelManager; use OCP\LDAP\ILDAPProvider; use OCP\LDAP\ILDAPProviderFactory; use OCP\Lock\ILockingProvider; @@ -1463,7 +1461,7 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(ISpeechToTextManager::class, SpeechToTextManager::class); - $this->registerAlias(ILanguageModelManager::class, LanguageModelManager::class); + $this->registerAlias(\OCP\TextProcessing\IManager::class, \OC\TextProcessing\Manager::class); $this->connectDispatcher(); } diff --git a/lib/private/Setup.php b/lib/private/Setup.php index a80afee6d86c2..3c1ac559c87a0 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -53,7 +53,7 @@ use InvalidArgumentException; use OC\Authentication\Token\PublicKeyTokenProvider; use OC\Authentication\Token\TokenCleanupJob; -use OC\LanguageModel\RemoveOldTasksBackgroundJob; +use OC\TextProcessing\RemoveOldTasksBackgroundJob; use OC\Log\Rotate; use OC\Preview\BackgroundCleanupJob; use OCP\AppFramework\Utility\ITimeFactory; diff --git a/lib/private/LanguageModel/Db/Task.php b/lib/private/TextProcessing/Db/Task.php similarity index 88% rename from lib/private/LanguageModel/Db/Task.php rename to lib/private/TextProcessing/Db/Task.php index 4e46f19e8a91f..bc1bbdc13dba0 100644 --- a/lib/private/LanguageModel/Db/Task.php +++ b/lib/private/TextProcessing/Db/Task.php @@ -23,11 +23,10 @@ * along with this program. If not, see . */ -namespace OC\LanguageModel\Db; +namespace OC\TextProcessing\Db; use OCP\AppFramework\Db\Entity; -use OCP\LanguageModel\AbstractLanguageModelTask; -use OCP\LanguageModel\ILanguageModelTask; +use OCP\TextProcessing\Task as OCPTask; /** * @method setType(string $type) @@ -87,7 +86,7 @@ public function toRow(): array { }, self::$fields)); } - public static function fromLanguageModelTask(ILanguageModelTask $task): Task { + public static function fromPublicTask(OCPTask $task): Task { /** @var Task $task */ $task = Task::fromParams([ 'id' => $task->getId(), @@ -103,8 +102,8 @@ public static function fromLanguageModelTask(ILanguageModelTask $task): Task { return $task; } - public function toLanguageModelTask(): ILanguageModelTask { - $task = AbstractLanguageModelTask::factory($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); + public function toPublicTask(): OCPTask { + $task = OCPTask::factory($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); $task->setId($this->getId()); $task->setStatus($this->getStatus()); $task->setOutput($this->getOutput()); diff --git a/lib/private/LanguageModel/Db/TaskMapper.php b/lib/private/TextProcessing/Db/TaskMapper.php similarity index 98% rename from lib/private/LanguageModel/Db/TaskMapper.php rename to lib/private/TextProcessing/Db/TaskMapper.php index 9b93ea1990f26..508f3fdf3b8b7 100644 --- a/lib/private/LanguageModel/Db/TaskMapper.php +++ b/lib/private/TextProcessing/Db/TaskMapper.php @@ -23,7 +23,7 @@ * along with this program. If not, see . */ -namespace OC\LanguageModel\Db; +namespace OC\TextProcessing\Db; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Entity; diff --git a/lib/private/LanguageModel/LanguageModelManager.php b/lib/private/TextProcessing/Manager.php similarity index 61% rename from lib/private/LanguageModel/LanguageModelManager.php rename to lib/private/TextProcessing/Manager.php index 970d968c8839f..34e4b2bb4cc2c 100644 --- a/lib/private/LanguageModel/LanguageModelManager.php +++ b/lib/private/TextProcessing/Manager.php @@ -23,34 +23,27 @@ * along with this program. If not, see . */ -namespace OC\LanguageModel; +namespace OC\TextProcessing; use OC\AppFramework\Bootstrap\Coordinator; -use OC\LanguageModel\Db\Task; -use OC\LanguageModel\Db\TaskMapper; +use OC\TextProcessing\Db\Task as DbTask; +use \OCP\TextProcessing\Task as OCPTask; +use OC\TextProcessing\Db\TaskMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\BackgroundJob\IJobList; use OCP\Common\Exception\NotFoundException; use OCP\DB\Exception; use OCP\IServerContainer; -use OCP\LanguageModel\FreePromptTask; -use OCP\LanguageModel\HeadlineTask; -use OCP\LanguageModel\IHeadlineProvider; -use OCP\LanguageModel\ILanguageModelManager; -use OCP\LanguageModel\ILanguageModelProvider; -use OCP\LanguageModel\ILanguageModelTask; -use OCP\LanguageModel\ISummaryProvider; -use OCP\LanguageModel\ITopicsProvider; -use OCP\LanguageModel\SummaryTask; -use OCP\LanguageModel\TopicsTask; +use OCP\TextProcessing\IManager; +use OCP\TextProcessing\IProvider; use OCP\PreConditionNotMetException; use Psr\Log\LoggerInterface; use RuntimeException; use Throwable; -class LanguageModelManager implements ILanguageModelManager { - /** @var ?ILanguageModelProvider[] */ +class Manager implements IManager { + /** @var ?IProvider[] */ private ?array $providers = null; public function __construct( @@ -74,12 +67,12 @@ public function getProviders(): array { $this->providers = []; - foreach ($context->getLanguageModelProviders() as $providerServiceRegistration) { + foreach ($context->getTextProcessingProviders() as $providerServiceRegistration) { $class = $providerServiceRegistration->getService(); try { $this->providers[$class] = $this->serverContainer->get($class); } catch (Throwable $e) { - $this->logger->error('Failed to load LanguageModel provider ' . $class, [ + $this->logger->error('Failed to load Text processing provider ' . $class, [ 'exception' => $e, ]); } @@ -93,78 +86,57 @@ public function hasProviders(): bool { if ($context === null) { return false; } - return count($context->getLanguageModelProviders()) > 0; + return count($context->getTextProcessingProviders()) > 0; } /** * @inheritDoc */ - public function getAvailableTaskClasses(): array { + public function getAvailableTaskTypes(): array { $tasks = []; foreach ($this->getProviders() as $provider) { - $tasks[FreePromptTask::class] = true; - if ($provider instanceof ISummaryProvider) { - $tasks[SummaryTask::class] = true; - } - if ($provider instanceof IHeadlineProvider) { - $tasks[HeadlineTask::class] = true; - } - if ($provider instanceof ITopicsProvider) { - $tasks[TopicsTask::class] = true; - } + $tasks[$provider->getTaskType()] = true; } return array_keys($tasks); } - /** - * @inheritDoc - */ - public function getAvailableTaskTypes(): array { - return array_map(fn ($taskClass) => $taskClass::TYPE, $this->getAvailableTaskClasses()); - } - - public function canHandleTask(ILanguageModelTask $task): bool { - foreach ($this->getAvailableTaskClasses() as $class) { - if ($task instanceof $class) { - return true; - } - } - return false; + public function canHandleTask(OCPTask $task): bool { + return in_array($task->getType(), $this->getAvailableTaskTypes()); } /** * @inheritDoc */ - public function runTask(ILanguageModelTask $task): string { + public function runTask(OCPTask $task): string { if (!$this->canHandleTask($task)) { - throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); + throw new PreConditionNotMetException('No text processing provider is installed that can handle this task'); } foreach ($this->getProviders() as $provider) { if (!$task->canUseProvider($provider)) { continue; } try { - $task->setStatus(ILanguageModelTask::STATUS_RUNNING); + $task->setStatus(OCPTask::STATUS_RUNNING); if ($task->getId() === null) { - $taskEntity = $this->taskMapper->insert(Task::fromLanguageModelTask($task)); + $taskEntity = $this->taskMapper->insert(DbTask::fromPublicTask($task)); $task->setId($taskEntity->getId()); } else { - $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $this->taskMapper->update(DbTask::fromPublicTask($task)); } $output = $task->visitProvider($provider); $task->setOutput($output); - $task->setStatus(ILanguageModelTask::STATUS_SUCCESSFUL); - $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $task->setStatus(OCPTask::STATUS_SUCCESSFUL); + $this->taskMapper->update(DbTask::fromPublicTask($task)); return $output; } catch (\RuntimeException $e) { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); - $task->setStatus(ILanguageModelTask::STATUS_FAILED); - $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $task->setStatus(OCPTask::STATUS_FAILED); + $this->taskMapper->update(DbTask::fromPublicTask($task)); throw $e; } catch (\Throwable $e) { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); - $task->setStatus(ILanguageModelTask::STATUS_FAILED); - $this->taskMapper->update(Task::fromLanguageModelTask($task)); + $task->setStatus(OCPTask::STATUS_FAILED); + $this->taskMapper->update(DbTask::fromPublicTask($task)); throw new RuntimeException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage(), 0, $e); } } @@ -176,12 +148,12 @@ public function runTask(ILanguageModelTask $task): string { * @inheritDoc * @throws Exception */ - public function scheduleTask(ILanguageModelTask $task): void { + public function scheduleTask(OCPTask $task): void { if (!$this->canHandleTask($task)) { throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); } - $task->setStatus(ILanguageModelTask::STATUS_SCHEDULED); - $taskEntity = Task::fromLanguageModelTask($task); + $task->setStatus(OCPTask::STATUS_SCHEDULED); + $taskEntity = DbTask::fromPublicTask($task); $this->taskMapper->insert($taskEntity); $task->setId($taskEntity->getId()); $this->jobList->add(TaskBackgroundJob::class, [ @@ -191,14 +163,14 @@ public function scheduleTask(ILanguageModelTask $task): void { /** * @param int $id The id of the task - * @return ILanguageModelTask + * @return OCPTask * @throws RuntimeException If the query failed * @throws NotFoundException If the task could not be found */ - public function getTask(int $id): ILanguageModelTask { + public function getTask(int $id): OCPTask { try { $taskEntity = $this->taskMapper->find($id); - return $taskEntity->toLanguageModelTask(); + return $taskEntity->toPublicTask(); } catch (DoesNotExistException $e) { throw new NotFoundException('Could not find task with the provided id'); } catch (MultipleObjectsReturnedException $e) { diff --git a/lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php b/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php similarity index 96% rename from lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php rename to lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php index fa3a716a2c608..89d329acfbb76 100644 --- a/lib/private/LanguageModel/RemoveOldTasksBackgroundJob.php +++ b/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php @@ -24,9 +24,9 @@ */ -namespace OC\LanguageModel; +namespace OC\TextProcessing; -use OC\LanguageModel\Db\TaskMapper; +use OC\TextProcessing\Db\TaskMapper; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\TimedJob; use OCP\DB\Exception; diff --git a/lib/private/LanguageModel/TaskBackgroundJob.php b/lib/private/TextProcessing/TaskBackgroundJob.php similarity index 82% rename from lib/private/LanguageModel/TaskBackgroundJob.php rename to lib/private/TextProcessing/TaskBackgroundJob.php index 5ac37baf3326e..4c24b3e531f2a 100644 --- a/lib/private/LanguageModel/TaskBackgroundJob.php +++ b/lib/private/TextProcessing/TaskBackgroundJob.php @@ -24,19 +24,19 @@ */ -namespace OC\LanguageModel; +namespace OC\TextProcessing; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\QueuedJob; use OCP\EventDispatcher\IEventDispatcher; -use OCP\LanguageModel\Events\TaskFailedEvent; -use OCP\LanguageModel\Events\TaskSuccessfulEvent; -use OCP\LanguageModel\ILanguageModelManager; +use OCP\TextProcessing\Events\TaskFailedEvent; +use OCP\TextProcessing\Events\TaskSuccessfulEvent; +use OCP\TextProcessing\IManager; class TaskBackgroundJob extends QueuedJob { public function __construct( - ITimeFactory $timeFactory, - private ILanguageModelManager $languageModelManager, + ITimeFactory $timeFactory, + private IManager $textProcessingManager, private IEventDispatcher $eventDispatcher, ) { parent::__construct($timeFactory); @@ -51,9 +51,9 @@ public function __construct( */ protected function run($argument) { $taskId = $argument['taskId']; - $task = $this->languageModelManager->getTask($taskId); + $task = $this->textProcessingManager->getTask($taskId); try { - $this->languageModelManager->runTask($task); + $this->textProcessingManager->runTask($task); $event = new TaskSuccessfulEvent($task); } catch (\Throwable $e) { $event = new TaskFailedEvent($task, $e->getMessage()); diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 66435d4593466..720803a78d170 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -37,7 +37,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\IContainer; -use OCP\LanguageModel\ILanguageModelProvider; +use OCP\TextProcessing\IProvider as ITextProcessingProvider; use OCP\Notification\INotifier; use OCP\Preview\IProviderV2; use OCP\SpeechToText\ISpeechToTextProvider; @@ -221,14 +221,14 @@ public function registerWellKnownHandler(string $class): void; public function registerSpeechToTextProvider(string $providerClass): void; /** - * Register a custom LanguageModel provider class that provides a promptable language model - * through the OCP\LanguageModel APIs + * Register a custom text processing provider class that provides a promptable language model + * through the OCP\TextProcessing APIs * * @param string $providerClass - * @psalm-param class-string $providerClass + * @psalm-param class-string $providerClass * @since 27.1.0 */ - public function registerLanguageModelProvider(string $providerClass): void; + public function registerTextProcessingProvider(string $providerClass): void; /** * Register a custom template provider class that is able to inject custom templates diff --git a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php b/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php deleted file mode 100644 index 77a61ac5c6e08..0000000000000 --- a/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace OCP\LanguageModel; - -/** - * This LanguageModel Task represents headline generation - * which generates a headline for the passed text - * @since 27.1.0 - * @template-extends AbstractLanguageModelTask - */ -final class HeadlineTask extends AbstractLanguageModelTask { - /** - * @since 27.1.0 - */ - public const TYPE = 'headline'; - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$this->canUseProvider($provider)) { - throw new \RuntimeException('HeadlineTask#visitProvider expects IHeadlineProvider'); - } - return $provider->findHeadline($this->getInput()); - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function canUseProvider(ILanguageModelProvider $provider): bool { - return $provider instanceof IHeadlineProvider; - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function getType(): string { - return self::TYPE; - } -} diff --git a/lib/public/LanguageModel/IHeadlineProvider.php b/lib/public/LanguageModel/IHeadlineProvider.php deleted file mode 100644 index 30185f4d4b3d9..0000000000000 --- a/lib/public/LanguageModel/IHeadlineProvider.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -namespace OCP\LanguageModel; - -use RuntimeException; - -/** - * This LanguageModel Provider represents headline generation - * which generates a headline for the passed text - * @since 27.1.0 - */ -interface IHeadlineProvider extends ILanguageModelProvider { - /** - * @param string $text The text to find headline for - * @returns string the headline - * @since 27.1.0 - * @throws RuntimeException If the text could not be transcribed - */ - public function findHeadline(string $text): string; -} diff --git a/lib/public/LanguageModel/ILanguageModelTask.php b/lib/public/LanguageModel/ILanguageModelTask.php deleted file mode 100644 index 0f552c8de54d8..0000000000000 --- a/lib/public/LanguageModel/ILanguageModelTask.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace OCP\LanguageModel; - -/** - * @since 27.1.0 - * @template T of ILanguageModelProvider - */ -interface ILanguageModelTask extends \JsonSerializable { - /** - * @since 27.1.0 - */ - public const STATUS_FAILED = 4; - /** - * @since 27.1.0 - */ - public const STATUS_SUCCESSFUL = 3; - /** - * @since 27.1.0 - */ - public const STATUS_RUNNING = 2; - /** - * @since 27.1.0 - */ - public const STATUS_SCHEDULED = 1; - /** - * @since 27.1.0 - */ - public const STATUS_UNKNOWN = 0; - - /** - * @since 27.1.0 - */ - public const TYPES = [ - FreePromptTask::TYPE => FreePromptTask::class, - SummaryTask::TYPE => SummaryTask::class, - HeadlineTask::TYPE => HeadlineTask::class, - TopicsTask::TYPE => TopicsTask::class, - ]; - - /** - * @psalm-param T $provider - * @param ILanguageModelProvider $provider - * @return string - * @since 27.1.0 - */ - public function visitProvider(ILanguageModelProvider $provider): string; - - /** - * @psalm-param T $provider - * @param ILanguageModelProvider $provider - * @return bool - * @since 27.1.0 - */ - public function canUseProvider(ILanguageModelProvider $provider): bool; - - - /** - * @return string - * @since 27.1.0 - */ - public function getType(): string; - - /** - * @return ILanguageModelTask::STATUS_* - * @since 27.1.0 - */ - public function getStatus(): int; - - /** - * @param ILanguageModelTask::STATUS_* $status - * @since 27.1.0 - */ - public function setStatus(int $status): void; - - /** - * @param int|null $id - * @since 27.1.0 - */ - public function setId(?int $id): void; - - /** - * @return int|null - * @since 27.1.0 - */ - public function getId(): ?int; - - /** - * @return string - * @since 27.1.0 - */ - public function getInput(): string; - - /** - * @param string|null $output - * @since 27.1.0 - */ - public function setOutput(?string $output): void; - - /** - * @return null|string - * @since 27.1.0 - */ - public function getOutput(): ?string; - - /** - * @return string - * @since 27.1.0 - */ - public function getAppId(): string; - - /** - * @return string - * @since 27.1.0 - */ - public function getIdentifier(): string; - - /** - * @return string|null - * @since 27.1.0 - */ - public function getUserId(): ?string; -} diff --git a/lib/public/LanguageModel/ISummaryProvider.php b/lib/public/LanguageModel/ISummaryProvider.php deleted file mode 100644 index c286e74eb1949..0000000000000 --- a/lib/public/LanguageModel/ISummaryProvider.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -namespace OCP\LanguageModel; - -use RuntimeException; - -/** - * This LanguageModel Provider implements summarization - * which sums up the passed text. - * @since 27.1.0 - */ -interface ISummaryProvider extends ILanguageModelProvider { - /** - * @param string $text The text to summarize - * @returns string the summary - * @since 27.1.0 - * @throws RuntimeException If the text could not be transcribed - */ - public function summarize(string $text): string; -} diff --git a/lib/public/LanguageModel/SummaryTask.php b/lib/public/LanguageModel/SummaryTask.php deleted file mode 100644 index 47864532ae309..0000000000000 --- a/lib/public/LanguageModel/SummaryTask.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace OCP\LanguageModel; - -/** - * This is an absctract LanguageModel Task represents summarization - * which sums up the passed text. - * @since 27.1.0 - * @template-extends AbstractLanguageModelTask - */ -final class SummaryTask extends AbstractLanguageModelTask { - /** - * @since 27.1.0 - */ - public const TYPE = 'summarize'; - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$this->canUseProvider($provider)) { - throw new \RuntimeException('SummaryTask#visitProvider expects ISummaryProvider'); - } - return $provider->summarize($this->getInput()); - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function canUseProvider(ILanguageModelProvider $provider): bool { - return $provider instanceof ISummaryProvider; - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function getType(): string { - return self::TYPE; - } -} diff --git a/lib/public/LanguageModel/TopicsTask.php b/lib/public/LanguageModel/TopicsTask.php deleted file mode 100644 index ab2c5916061c3..0000000000000 --- a/lib/public/LanguageModel/TopicsTask.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * @author Marcel Klehr - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace OCP\LanguageModel; - -/** - * This LanguageModel Task represents topics synthesis - * which outputs comma-separated topics for the passed text - * @since 27.1.0 - * @template-extends AbstractLanguageModelTask - */ -final class TopicsTask extends AbstractLanguageModelTask { - /** - * @since 27.1.0 - */ - public const TYPE = 'topics'; - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function visitProvider(ILanguageModelProvider $provider): string { - if (!$this->canUseProvider($provider)) { - throw new \RuntimeException('TopicsTask#visitProvider expects ITopicsProvider'); - } - return $provider->findTopics($this->getInput()); - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function canUseProvider(ILanguageModelProvider $provider): bool { - return $provider instanceof ITopicsProvider; - } - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function getType(): string { - return self::TYPE; - } -} diff --git a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php b/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php similarity index 81% rename from lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php rename to lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php index c8abc7373ebb0..10c592fe03173 100644 --- a/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php +++ b/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php @@ -23,29 +23,30 @@ * along with this program. If not, see . * */ -namespace OCP\LanguageModel\Events; +namespace OCP\TextProcessing\Events; use OCP\EventDispatcher\Event; -use OCP\LanguageModel\ILanguageModelTask; +use OCP\TextProcessing\ILanguageModelTask; +use OCP\TextProcessing\Task; /** * @since 27.1.0 */ -abstract class AbstractLanguageModelEvent extends Event { +abstract class AbstractTextProcessingEvent extends Event { /** * @since 27.1.0 */ public function __construct( - private ILanguageModelTask $task + private Task $task ) { parent::__construct(); } /** - * @return ILanguageModelTask + * @return Task * @since 27.1.0 */ - public function getTask(): ILanguageModelTask { + public function getTask(): Task { return $this->task; } } diff --git a/lib/public/LanguageModel/Events/TaskFailedEvent.php b/lib/public/TextProcessing/Events/TaskFailedEvent.php similarity index 61% rename from lib/public/LanguageModel/Events/TaskFailedEvent.php rename to lib/public/TextProcessing/Events/TaskFailedEvent.php index f42203a6e48de..f9765e362dc73 100644 --- a/lib/public/LanguageModel/Events/TaskFailedEvent.php +++ b/lib/public/TextProcessing/Events/TaskFailedEvent.php @@ -1,20 +1,20 @@ . */ -namespace OCP\LanguageModel; +namespace OCP\TextProcessing; + +use OCP\IL10N; /** + * This is the text processing task type for free prompting * @since 27.1.0 - * @template-extends AbstractLanguageModelTask */ -final class FreePromptTask extends AbstractLanguageModelTask { +class FreePromptTaskType implements ITaskType { /** + * Constructor for FreePromptTaskType + * + * @param IL10N $l * @since 27.1.0 */ - public const TYPE = 'free_prompt'; - - /** - * @inheritDoc - * @since 27.1.0 - */ - public function visitProvider(ILanguageModelProvider $provider): string { - return $provider->prompt($this->getInput()); + public function __construct( + private IL10N $l, + ) { } + /** * @inheritDoc - * @since 27.1.0 */ - public function canUseProvider(ILanguageModelProvider $provider): bool { - return true; + public function getName(): string { + return $this->l->t('Free prompt'); } /** * @inheritDoc - * @since 27.1.0 */ - public function getType(): string { - return self::TYPE; + public function getDescription(): string { + return $this->l->t('Runs an arbitrary prompt through the built-in language model.'); } } diff --git a/lib/public/LanguageModel/ITopicsProvider.php b/lib/public/TextProcessing/HeadlineTaskType.php similarity index 64% rename from lib/public/LanguageModel/ITopicsProvider.php rename to lib/public/TextProcessing/HeadlineTaskType.php index f061976a3ba43..4ced298fd4dde 100644 --- a/lib/public/LanguageModel/ITopicsProvider.php +++ b/lib/public/TextProcessing/HeadlineTaskType.php @@ -23,22 +23,38 @@ * along with this program. If not, see . */ +namespace OCP\TextProcessing; -namespace OCP\LanguageModel; - -use RuntimeException; +use OCP\IL10N; /** - * This LanguageModel Provider implements topics synthesis - * which outputs comma-separated topics for the passed text + * This is the text processing task type for creating headline * @since 27.1.0 */ -interface ITopicsProvider extends ILanguageModelProvider { +class HeadlineTaskType implements ITaskType { /** - * @param string $text The text to find topics for - * @returns string the topics, comma separated + * Constructor for HeadlineTaskType + * + * @param IL10N $l * @since 27.1.0 - * @throws RuntimeException If the text could not be transcribed */ - public function findTopics(string $text): string; + public function __construct( + private IL10N $l, + ) { + } + + + /** + * @inheritDoc + */ + public function getName(): string { + return $this->l->t('Generate headline'); + } + + /** + * @inheritDoc + */ + public function getDescription(): string { + return $this->l->t('Generates a possible headline for a text'); + } } diff --git a/lib/public/LanguageModel/ILanguageModelManager.php b/lib/public/TextProcessing/IManager.php similarity index 83% rename from lib/public/LanguageModel/ILanguageModelManager.php rename to lib/public/TextProcessing/IManager.php index 0afc99b91ab66..90e25894d4f2e 100644 --- a/lib/public/LanguageModel/ILanguageModelManager.php +++ b/lib/public/TextProcessing/IManager.php @@ -24,7 +24,7 @@ */ -namespace OCP\LanguageModel; +namespace OCP\TextProcessing; use OCP\Common\Exception\NotFoundException; use OCP\PreConditionNotMetException; @@ -35,47 +35,43 @@ * without known which providers are installed * @since 27.1.0 */ -interface ILanguageModelManager { +interface IManager { /** * @since 27.1.0 */ public function hasProviders(): bool; /** - * @return string[] - * @since 27.1.0 - */ - public function getAvailableTaskClasses(): array; - - /** - * @return string[] + * @return class-string[] * @since 27.1.0 */ public function getAvailableTaskTypes(): array; /** + * @param Task $task The task to run * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @throws RuntimeException If something else failed * @since 27.1.0 */ - public function runTask(ILanguageModelTask $task): string; + public function runTask(Task $task): string; /** * Will schedule an LLM inference process in the background. The result will become available * with the \OCP\LanguageModel\Events\TaskSuccessfulEvent * If inference fails a \OCP\LanguageModel\Events\TaskFailedEvent will be dispatched instead * + * @param Task $task The task to schedule * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called * @since 27.1.0 */ - public function scheduleTask(ILanguageModelTask $task) : void; + public function scheduleTask(Task $task) : void; /** * @param int $id The id of the task - * @return ILanguageModelTask + * @return Task * @throws RuntimeException If the query failed * @throws NotFoundException If the task could not be found * @since 27.1.0 */ - public function getTask(int $id): ILanguageModelTask; + public function getTask(int $id): Task; } diff --git a/lib/public/TextProcessing/IProvider.php b/lib/public/TextProcessing/IProvider.php new file mode 100644 index 0000000000000..3eb83aef8c333 --- /dev/null +++ b/lib/public/TextProcessing/IProvider.php @@ -0,0 +1,61 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +namespace OCP\TextProcessing; + +use RuntimeException; + +/** + * This is the interface that is implemented by apps that + * implement a text processing provider + * @template T of ITaskType + * @since 27.1.0 + */ +interface IProvider { + /** + * The localized name of this provider + * @since 27.1.0 + */ + public function getName(): string; + + /** + * Processes a text + * + * @param string $prompt The input text + * @return string the output text + * @since 27.1.0 + * @throws RuntimeException If the text could not be processed + */ + public function process(string $prompt): string; + + /** + * Returns the task type class string of the task type, that this + * provider handles + * + * @return class-string + */ + public function getTaskType(): string; +} diff --git a/lib/public/LanguageModel/ILanguageModelProvider.php b/lib/public/TextProcessing/ITaskType.php similarity index 71% rename from lib/public/LanguageModel/ILanguageModelProvider.php rename to lib/public/TextProcessing/ITaskType.php index 34e7eb6c4e59d..d08da3f7ac733 100644 --- a/lib/public/LanguageModel/ILanguageModelProvider.php +++ b/lib/public/TextProcessing/ITaskType.php @@ -23,27 +23,27 @@ * along with this program. If not, see . */ - -namespace OCP\LanguageModel; - -use RuntimeException; +namespace OCP\TextProcessing; /** - * This is the minimum interface that is implemented by apps that - * implement a LanguageModel provider + * This is a task type interface that is implemented by text processing + * task types * @since 27.1.0 */ -interface ILanguageModelProvider { +interface ITaskType { /** + * Returns the localized name of this task type + * * @since 27.1.0 + * @return string */ public function getName(): string; /** - * @param string $prompt The prompt to call the model with - * @return string the output + * Returns the localized description of this task type + * * @since 27.1.0 - * @throws RuntimeException If the text could not be transcribed + * @return string */ - public function prompt(string $prompt): string; + public function getDescription(): string; } diff --git a/lib/public/TextProcessing/SummaryTaskType.php b/lib/public/TextProcessing/SummaryTaskType.php new file mode 100644 index 0000000000000..7db695c18f722 --- /dev/null +++ b/lib/public/TextProcessing/SummaryTaskType.php @@ -0,0 +1,60 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\TextProcessing; + +use OCP\IL10N; + +/** + * This is the text processing task type for summaries + * @since 27.1.0 + */ +class SummaryTaskType implements ITaskType { + /** + * Constructor for SummaryTaskType + * + * @param IL10N $l + * @since 27.1.0 + */ + public function __construct( + private IL10N $l, + ) { + } + + + /** + * @inheritDoc + */ + public function getName(): string { + return $this->l->t('Summarize'); + } + + /** + * @inheritDoc + */ + public function getDescription(): string { + return $this->l->t('Summarizes text by reducing its length without losing key information.'); + } +} diff --git a/lib/public/LanguageModel/AbstractLanguageModelTask.php b/lib/public/TextProcessing/Task.php similarity index 62% rename from lib/public/LanguageModel/AbstractLanguageModelTask.php rename to lib/public/TextProcessing/Task.php index 91b81b9615b84..59cd38b720c1e 100644 --- a/lib/public/LanguageModel/AbstractLanguageModelTask.php +++ b/lib/public/TextProcessing/Task.php @@ -23,25 +23,55 @@ * along with this program. If not, see . */ -namespace OCP\LanguageModel; +namespace OCP\TextProcessing; /** - * This is an abstract LanguageModel task that implements basic - * goodies for downstream tasks - * @since 28.0. - * @template T of ILanguageModelProvider - * @template-implements ILanguageModelTask + * This is a text processing task + * @since 27.1.0 + * @template T of ITaskType */ -abstract class AbstractLanguageModelTask implements ILanguageModelTask { +final class Task implements \JsonSerializable { protected ?int $id = null; protected ?string $output = null; /** - * @psalm-var ILanguageModelTask::STATUS_* + * @since 27.1.0 + */ + public const TYPES = [ + FreePromptTaskType::class, + SummaryTaskType::class, + HeadlineTaskType::class, + TopicsTaskType::class, + ]; + + /** + * @since 27.1.0 + */ + public const STATUS_FAILED = 4; + /** + * @since 27.1.0 + */ + public const STATUS_SUCCESSFUL = 3; + /** + * @since 27.1.0 + */ + public const STATUS_RUNNING = 2; + /** + * @since 27.1.0 + */ + public const STATUS_SCHEDULED = 1; + /** + * @since 27.1.0 + */ + public const STATUS_UNKNOWN = 0; + + /** + * @psalm-var self::STATUS_* */ - protected int $status = ILanguageModelTask::STATUS_UNKNOWN; + protected int $status = self::STATUS_UNKNOWN; /** + * @param class-string $type * @param string $input * @param string $appId * @param string|null $userId @@ -49,6 +79,7 @@ abstract class AbstractLanguageModelTask implements ILanguageModelTask { * @since 27.1.0 */ final public function __construct( + protected string $type, protected string $input, protected string $appId, protected ?string $userId, @@ -57,10 +88,36 @@ final public function __construct( } /** + * @psalm-param IProvider $provider + * @param IProvider $provider * @return string * @since 27.1.0 */ - abstract public function getType(): string; + public function visitProvider(IProvider $provider): string { + if ($this->canUseProvider($provider)) { + return $provider->process($this->getInput()); + } else { + throw new \RuntimeException('Task of type ' . $this->getType() . ' cannot visit provider with task type ' . $provider->getTaskType()); + } + } + + /** + * @psalm-param IProvider $provider + * @param IProvider $provider + * @return bool + * @since 27.1.0 + */ + public function canUseProvider(IProvider $provider): bool { + return $provider->getTaskType() === $this->getType(); + } + + /** + * @return class-string + * @since 27.1.0 + */ + final public function getType(): string { + return $this->type; + } /** * @return string|null @@ -79,7 +136,7 @@ final public function setOutput(?string $output): void { } /** - * @psalm-return ILanguageModelTask::STATUS_* + * @psalm-return self::STATUS_* * @since 27.1.0 */ final public function getStatus(): int { @@ -87,7 +144,7 @@ final public function getStatus(): int { } /** - * @psalm-param ILanguageModelTask::STATUS_* $status + * @psalm-param self::STATUS_* $status * @since 27.1.0 */ final public function setStatus(int $status): void { @@ -143,10 +200,10 @@ final public function getUserId(): ?string { } /** - * @return array + * @return array{id: ?string, type: class-string, status: int, userId: ?string, appId: string, input: string, output: ?string, identifier: string} * @since 27.1.0 */ - public function jsonSerialize() { + public function jsonSerialize(): array { return [ 'id' => $this->getId(), 'type' => $this->getType(), @@ -165,14 +222,14 @@ public function jsonSerialize() { * @param string|null $userId * @param string $appId * @param string $identifier - * @return ILanguageModelTask + * @return Task * @throws \InvalidArgumentException * @since 27.1.0 */ - final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier = ''): ILanguageModelTask { - if (!in_array($type, array_keys(self::TYPES))) { + final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier = ''): Task { + if (!in_array($type, self::TYPES)) { throw new \InvalidArgumentException('Unknown task type'); } - return new (ILanguageModelTask::TYPES[$type])($input, $appId, $userId, $identifier); + return new Task($type, $input, $appId, $userId, $identifier); } } diff --git a/lib/public/TextProcessing/TopicsTaskType.php b/lib/public/TextProcessing/TopicsTaskType.php new file mode 100644 index 0000000000000..8b41b3ee61aea --- /dev/null +++ b/lib/public/TextProcessing/TopicsTaskType.php @@ -0,0 +1,60 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\TextProcessing; + +use OCP\IL10N; + +/** + * This is the text processing task type for topics extraction + * @since 27.1.0 + */ +class TopicsTaskType implements ITaskType { + /** + * Constructor for TopicsTaskType + * + * @param IL10N $l + * @since 27.1.0 + */ + public function __construct( + private IL10N $l, + ) { + } + + + /** + * @inheritDoc + */ + public function getName(): string { + return $this->l->t('Extract topics'); + } + + /** + * @inheritDoc + */ + public function getDescription(): string { + return $this->l->t('Extracts topics from a text and outputs them separated by commas.'); + } +} diff --git a/tests/lib/LanguageModel/LanguageModelManagerTest.php b/tests/lib/TextProcessing/TextProcessingTest.php similarity index 53% rename from tests/lib/LanguageModel/LanguageModelManagerTest.php rename to tests/lib/TextProcessing/TextProcessingTest.php index 6f8d6cd868d56..797571019cebb 100644 --- a/tests/lib/LanguageModel/LanguageModelManagerTest.php +++ b/tests/lib/TextProcessing/TextProcessingTest.php @@ -6,93 +6,97 @@ * See the COPYING-README file. */ -namespace Test\LanguageModel; +namespace Test\TextProcessing; use OC\AppFramework\Bootstrap\Coordinator; use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\Bootstrap\ServiceRegistration; use OC\EventDispatcher\EventDispatcher; -use OC\LanguageModel\Db\Task; -use OC\LanguageModel\Db\TaskMapper; -use OC\LanguageModel\LanguageModelManager; -use OC\LanguageModel\RemoveOldTasksBackgroundJob; -use OC\LanguageModel\TaskBackgroundJob; +use OC\TextProcessing\Db\Task as DbTask; +use OC\TextProcessing\Db\TaskMapper; +use OC\TextProcessing\Manager; +use OC\TextProcessing\RemoveOldTasksBackgroundJob; +use OC\TextProcessing\TaskBackgroundJob; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Common\Exception\NotFoundException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IServerContainer; -use OCP\LanguageModel\Events\TaskFailedEvent; -use OCP\LanguageModel\Events\TaskSuccessfulEvent; -use OCP\LanguageModel\FreePromptTask; -use OCP\LanguageModel\HeadlineTask; -use OCP\LanguageModel\IHeadlineProvider; -use OCP\LanguageModel\ILanguageModelManager; -use OCP\LanguageModel\ILanguageModelProvider; -use OCP\LanguageModel\ILanguageModelTask; -use OCP\LanguageModel\ISummaryProvider; -use OCP\LanguageModel\SummaryTask; -use OCP\LanguageModel\TopicsTask; +use OCP\TextProcessing\Events\TaskFailedEvent; +use OCP\TextProcessing\Events\TaskSuccessfulEvent; +use OCP\TextProcessing\FreePromptTaskType; +use OCP\TextProcessing\IManager; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\SummaryTaskType; use OCP\PreConditionNotMetException; +use OCP\TextProcessing\Task; +use OCP\TextProcessing\TopicsTaskType; use PHPUnit\Framework\Constraint\IsInstanceOf; use Psr\Log\LoggerInterface; use Test\BackgroundJob\DummyJobList; -class TestVanillaLanguageModelProvider implements ILanguageModelProvider { +class SuccessfulSummaryProvider implements IProvider { public bool $ran = false; public function getName(): string { return 'TEST Vanilla LLM Provider'; } - public function prompt(string $prompt): string { + public function process(string $prompt): string { $this->ran = true; - return $prompt . ' Free Prompt'; + return $prompt . ' Summarize'; + } + + public function getTaskType(): string { + return SummaryTaskType::class; } } -class TestFailingLanguageModelProvider implements ILanguageModelProvider { +class FailingSummaryProvider implements IProvider { public bool $ran = false; public function getName(): string { return 'TEST Vanilla LLM Provider'; } - public function prompt(string $prompt): string { + public function process(string $prompt): string { $this->ran = true; throw new \Exception('ERROR'); } + + public function getTaskType(): string { + return SummaryTaskType::class; + } } -class TestAdvancedLanguageModelProvider implements ILanguageModelProvider, ISummaryProvider, IHeadlineProvider { +class FreePromptProvider implements IProvider { + public bool $ran = false; + public function getName(): string { - return 'TEST Full LLM Provider'; + return 'TEST Free Prompt Provider'; } - public function prompt(string $prompt): string { + public function process(string $prompt): string { + $this->ran = true; return $prompt . ' Free Prompt'; } - public function findHeadline(string $text): string { - return $text . ' Headline'; - } - - public function summarize(string $text): string { - return $text. ' Summarize'; + public function getTaskType(): string { + return FreePromptTaskType::class; } } -class LanguageModelManagerTest extends \Test\TestCase { - private ILanguageModelManager $languageModelManager; +class TextProcessingTest extends \Test\TestCase { + private IManager $manager; private Coordinator $coordinator; protected function setUp(): void { parent::setUp(); $this->providers = [ - TestVanillaLanguageModelProvider::class => new TestVanillaLanguageModelProvider(), - TestAdvancedLanguageModelProvider::class => new TestAdvancedLanguageModelProvider(), - TestFailingLanguageModelProvider::class => new TestFailingLanguageModelProvider(), + SuccessfulSummaryProvider::class => new SuccessfulSummaryProvider(), + FailingSummaryProvider::class => new FailingSummaryProvider(), + FreePromptProvider::class => new FreePromptProvider(), ]; $this->serverContainer = $this->createMock(IServerContainer::class); @@ -117,7 +121,7 @@ protected function setUp(): void { $this->taskMapper ->expects($this->any()) ->method('insert') - ->willReturnCallback(function (Task $task) { + ->willReturnCallback(function (DbTask $task) { $task->setId(count($this->tasksDb) ? max(array_keys($this->tasksDb)) : 1); $task->setLastUpdated($this->currentTime->getTimestamp()); $this->tasksDb[$task->getId()] = $task->toRow(); @@ -126,7 +130,7 @@ protected function setUp(): void { $this->taskMapper ->expects($this->any()) ->method('update') - ->willReturnCallback(function (Task $task) { + ->willReturnCallback(function (DbTask $task) { $task->setLastUpdated($this->currentTime->getTimestamp()); $this->tasksDb[$task->getId()] = $task->toRow(); return $task; @@ -138,7 +142,7 @@ protected function setUp(): void { if (!isset($this->tasksDb[$id])) { throw new DoesNotExistException('Could not find it'); } - return Task::fromRow($this->tasksDb[$id]); + return DbTask::fromRow($this->tasksDb[$id]); }); $this->taskMapper ->expects($this->any()) @@ -153,7 +157,7 @@ protected function setUp(): void { $this->jobList->expects($this->any())->method('add')->willReturnCallback(function () { }); - $this->languageModelManager = new LanguageModelManager( + $this->manager = new Manager( $this->serverContainer, $this->coordinator, \OC::$server->get(LoggerInterface::class), @@ -163,57 +167,54 @@ protected function setUp(): void { } public function testShouldNotHaveAnyProviders() { - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([]); - $this->assertCount(0, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(0, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertFalse($this->languageModelManager->hasProviders()); + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]); + $this->assertCount(0, $this->manager->getAvailableTaskTypes()); + $this->assertFalse($this->manager->hasProviders()); $this->expectException(PreConditionNotMetException::class); - $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null)); + $this->manager->runTask(new \OCP\TextProcessing\Task(FreePromptTaskType::class, 'Hello', 'test', null)); } public function testProviderShouldBeRegisteredAndRun() { - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ - new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([ + new ServiceRegistration('test', SuccessfulSummaryProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertTrue($this->languageModelManager->hasProviders()); - $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null))); + $this->assertCount(1, $this->manager->getAvailableTaskTypes()); + $this->assertTrue($this->manager->hasProviders()); + $this->assertEquals('Hello Summarize', $this->manager->runTask(new Task(SummaryTaskType::class, 'Hello', 'test', null))); // Summaries are not implemented by the vanilla provider, only free prompt $this->expectException(PreConditionNotMetException::class); - $this->languageModelManager->runTask(new SummaryTask('Hello', 'test', null)); + $this->manager->runTask(new Task(FreePromptTaskType::class, 'Hello', 'test', null)); } public function testProviderShouldBeRegisteredAndScheduled() { // register provider - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ - new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([ + new ServiceRegistration('test', SuccessfulSummaryProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertTrue($this->languageModelManager->hasProviders()); + $this->assertCount(1, $this->manager->getAvailableTaskTypes()); + $this->assertTrue($this->manager->hasProviders()); // create task object - $task = new FreePromptTask('Hello', 'test', null); + $task = new Task(SummaryTaskType::class, 'Hello', 'test', null); $this->assertNull($task->getId()); $this->assertNull($task->getOutput()); // schedule works - $this->assertEquals(ILanguageModelTask::STATUS_UNKNOWN, $task->getStatus()); - $this->languageModelManager->scheduleTask($task); + $this->assertEquals(Task::STATUS_UNKNOWN, $task->getStatus()); + $this->manager->scheduleTask($task); // Task object is up-to-date $this->assertNotNull($task->getId()); $this->assertNull($task->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task->getStatus()); + $this->assertEquals(Task::STATUS_SCHEDULED, $task->getStatus()); // Task object retrieved from db is up-to-date - $task2 = $this->languageModelManager->getTask($task->getId()); + $task2 = $this->manager->getTask($task->getId()); $this->assertEquals($task->getId(), $task2->getId()); $this->assertEquals('Hello', $task2->getInput()); $this->assertNull($task2->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); + $this->assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus()); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class)); @@ -221,79 +222,74 @@ public function testProviderShouldBeRegisteredAndScheduled() { // run background job $bgJob = new TaskBackgroundJob( \OC::$server->get(ITimeFactory::class), - $this->languageModelManager, + $this->manager, $this->eventDispatcher, ); $bgJob->setArgument(['taskId' => $task->getId()]); $bgJob->start($this->jobList); - $provider = $this->providers[TestVanillaLanguageModelProvider::class]; + $provider = $this->providers[SuccessfulSummaryProvider::class]; $this->assertTrue($provider->ran); // Task object retrieved from db is up-to-date - $task3 = $this->languageModelManager->getTask($task->getId()); + $task3 = $this->manager->getTask($task->getId()); $this->assertEquals($task->getId(), $task3->getId()); $this->assertEquals('Hello', $task3->getInput()); - $this->assertEquals('Hello Free Prompt', $task3->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SUCCESSFUL, $task3->getStatus()); + $this->assertEquals('Hello Summarize', $task3->getOutput()); + $this->assertEquals(Task::STATUS_SUCCESSFUL, $task3->getStatus()); } public function testMultipleProvidersShouldBeRegisteredAndRunCorrectly() { - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ - new ServiceRegistration('test', TestVanillaLanguageModelProvider::class), - new ServiceRegistration('test', TestAdvancedLanguageModelProvider::class), + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([ + new ServiceRegistration('test', SuccessfulSummaryProvider::class), + new ServiceRegistration('test', FreePromptProvider::class), ]); - $this->assertCount(3, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(3, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertTrue($this->languageModelManager->hasProviders()); + $this->assertCount(2, $this->manager->getAvailableTaskTypes()); + $this->assertTrue($this->manager->hasProviders()); // Try free prompt again - $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask(new FreePromptTask('Hello', 'test', null))); - - // Try headline task - $this->assertEquals('Hello Headline', $this->languageModelManager->runTask(new HeadlineTask('Hello', 'test', null))); + $this->assertEquals('Hello Free Prompt', $this->manager->runTask(new Task(FreePromptTaskType::class, 'Hello', 'test', null))); // Try summary task - $this->assertEquals('Hello Summarize', $this->languageModelManager->runTask(new SummaryTask('Hello', 'test', null))); + $this->assertEquals('Hello Summarize', $this->manager->runTask(new Task(SummaryTaskType::class, 'Hello', 'test', null))); // Topics are not implemented by both the vanilla provider and the full provider $this->expectException(PreConditionNotMetException::class); - $this->languageModelManager->runTask(new TopicsTask('Hello', 'test', null)); + $this->manager->runTask(new Task(TopicsTaskType::class, 'Hello', 'test', null)); } public function testNonexistentTask() { $this->expectException(NotFoundException::class); - $this->languageModelManager->getTask(98765432456); + $this->manager->getTask(98765432456); } public function testTaskFailure() { // register provider - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ - new ServiceRegistration('test', TestFailingLanguageModelProvider::class), + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([ + new ServiceRegistration('test', FailingSummaryProvider::class), ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertTrue($this->languageModelManager->hasProviders()); + $this->assertCount(1, $this->manager->getAvailableTaskTypes()); + $this->assertTrue($this->manager->hasProviders()); // create task object - $task = new FreePromptTask('Hello', 'test', null); + $task = new Task(SummaryTaskType::class, 'Hello', 'test', null); $this->assertNull($task->getId()); $this->assertNull($task->getOutput()); // schedule works - $this->assertEquals(ILanguageModelTask::STATUS_UNKNOWN, $task->getStatus()); - $this->languageModelManager->scheduleTask($task); + $this->assertEquals(Task::STATUS_UNKNOWN, $task->getStatus()); + $this->manager->scheduleTask($task); // Task object is up-to-date $this->assertNotNull($task->getId()); $this->assertNull($task->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task->getStatus()); + $this->assertEquals(Task::STATUS_SCHEDULED, $task->getStatus()); // Task object retrieved from db is up-to-date - $task2 = $this->languageModelManager->getTask($task->getId()); + $task2 = $this->manager->getTask($task->getId()); $this->assertEquals($task->getId(), $task2->getId()); $this->assertEquals('Hello', $task2->getInput()); $this->assertNull($task2->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_SCHEDULED, $task2->getStatus()); + $this->assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus()); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class)); @@ -301,31 +297,30 @@ public function testTaskFailure() { // run background job $bgJob = new TaskBackgroundJob( \OC::$server->get(ITimeFactory::class), - $this->languageModelManager, + $this->manager, $this->eventDispatcher, ); $bgJob->setArgument(['taskId' => $task->getId()]); $bgJob->start($this->jobList); - $provider = $this->providers[TestFailingLanguageModelProvider::class]; + $provider = $this->providers[FailingSummaryProvider::class]; $this->assertTrue($provider->ran); // Task object retrieved from db is up-to-date - $task3 = $this->languageModelManager->getTask($task->getId()); + $task3 = $this->manager->getTask($task->getId()); $this->assertEquals($task->getId(), $task3->getId()); $this->assertEquals('Hello', $task3->getInput()); $this->assertNull($task3->getOutput()); - $this->assertEquals(ILanguageModelTask::STATUS_FAILED, $task3->getStatus()); + $this->assertEquals(Task::STATUS_FAILED, $task3->getStatus()); } public function testOldTasksShouldBeCleanedUp() { - $this->registrationContext->expects($this->any())->method('getLanguageModelProviders')->willReturn([ - new ServiceRegistration('test', TestVanillaLanguageModelProvider::class) + $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([ + new ServiceRegistration('test', SuccessfulSummaryProvider::class) ]); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskClasses()); - $this->assertCount(1, $this->languageModelManager->getAvailableTaskTypes()); - $this->assertTrue($this->languageModelManager->hasProviders()); - $task = new FreePromptTask('Hello', 'test', null); - $this->assertEquals('Hello Free Prompt', $this->languageModelManager->runTask($task)); + $this->assertCount(1, $this->manager->getAvailableTaskTypes()); + $this->assertTrue($this->manager->hasProviders()); + $task = new Task(SummaryTaskType::class, 'Hello', 'test', null); + $this->assertEquals('Hello Summarize', $this->manager->runTask($task)); $this->currentTime = $this->currentTime->add(new \DateInterval('P1Y')); // run background job @@ -338,6 +333,6 @@ public function testOldTasksShouldBeCleanedUp() { $bgJob->start($this->jobList); $this->expectException(NotFoundException::class); - $this->languageModelManager->getTask($task->getId()); + $this->manager->getTask($task->getId()); } } From d8c44da4f4cb416ffa4382b39595d86ca552e56d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 14 Jul 2023 16:10:39 +0200 Subject: [PATCH 069/100] Fix psalm errors Signed-off-by: Marcel Klehr (cherry picked from commit 95d2d3af5ce3aba22a206b01c89373a38f9ffb3a) --- .../TextProcessingApiController.php | 20 +------------------ .../Events/AbstractTextProcessingEvent.php | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/core/Controller/TextProcessingApiController.php b/core/Controller/TextProcessingApiController.php index 7cc7199dfbdc8..0c723ebace348 100644 --- a/core/Controller/TextProcessingApiController.php +++ b/core/Controller/TextProcessingApiController.php @@ -58,17 +58,13 @@ public function __construct( * This endpoint returns all available LanguageModel task types * * @PublicPage - * @return DataResponse}, array{}> - * - * 200: Task types returned */ public function taskTypes(): DataResponse { $typeClasses = $this->languageModelManager->getAvailableTaskTypes(); - /** @var list $types */ $types = []; foreach ($typeClasses as $typeClass) { - /** @var ITaskType $object */ try { + /** @var ITaskType $object */ $object = $this->container->get($typeClass); } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) { $this->logger->warning('Could not find ' . $typeClass, ['exception' => $e]); @@ -92,15 +88,6 @@ public function taskTypes(): DataResponse { * @PublicPage * @UserRateThrottle(limit=20, period=120) * @AnonRateThrottle(limit=5, period=120) - * @param string $input The input for the language model task - * @param string $type The task type - * @param string $appId The originating app ID - * @param string $identifier An identifier to identify this task - * @return DataResponse|DataResponse - * - * 200: Task scheduled - * 400: Task type does not exist - * 412: Task type not available */ public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { try { @@ -127,11 +114,6 @@ public function schedule(string $input, string $type, string $appId, string $ide * * @PublicPage * @param int $id The id of the task - * @return DataResponse|DataResponse - * - * 200: Task returned - * 404: Task not found - * 500: Internal error */ public function getTask(int $id): DataResponse { try { diff --git a/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php b/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php index 10c592fe03173..329889e61f0d2 100644 --- a/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php +++ b/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php @@ -26,7 +26,6 @@ namespace OCP\TextProcessing\Events; use OCP\EventDispatcher\Event; -use OCP\TextProcessing\ILanguageModelTask; use OCP\TextProcessing\Task; /** From 87ac99582ba399aaa2b205f2dd74f6e19aae785b Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 14 Jul 2023 16:27:06 +0200 Subject: [PATCH 070/100] Fix psalm errors Signed-off-by: Marcel Klehr (cherry picked from commit df1cf18f368ce935dd3f57a99426b6712421ed3e) --- lib/public/TextProcessing/Task.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/public/TextProcessing/Task.php b/lib/public/TextProcessing/Task.php index 59cd38b720c1e..668ca6d09c4a1 100644 --- a/lib/public/TextProcessing/Task.php +++ b/lib/public/TextProcessing/Task.php @@ -28,7 +28,9 @@ /** * This is a text processing task * @since 27.1.0 - * @template T of ITaskType + * @psalm-template T of ITaskType + * @psalm-template S as class-string + * @psalm-template P as IProvider */ final class Task implements \JsonSerializable { protected ?int $id = null; @@ -71,7 +73,8 @@ final class Task implements \JsonSerializable { protected int $status = self::STATUS_UNKNOWN; /** - * @param class-string $type + * @psalm-param S $type + * @param string $type * @param string $input * @param string $appId * @param string|null $userId @@ -88,7 +91,7 @@ final public function __construct( } /** - * @psalm-param IProvider $provider + * @psalm-param P $provider * @param IProvider $provider * @return string * @since 27.1.0 @@ -102,7 +105,7 @@ public function visitProvider(IProvider $provider): string { } /** - * @psalm-param IProvider $provider + * @psalm-param P $provider * @param IProvider $provider * @return bool * @since 27.1.0 @@ -112,7 +115,7 @@ public function canUseProvider(IProvider $provider): bool { } /** - * @return class-string + * @psalm-return S * @since 27.1.0 */ final public function getType(): string { @@ -200,7 +203,7 @@ final public function getUserId(): ?string { } /** - * @return array{id: ?string, type: class-string, status: int, userId: ?string, appId: string, input: string, output: ?string, identifier: string} + * @psalm-return array{id: ?int, type: S, status: 0|1|2|3|4, userId: ?string, appId: string, input: string, output: ?string, identifier: string} * @since 27.1.0 */ public function jsonSerialize(): array { From 322bb9763aae853f63986408ce1672ef582c0592 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 17 Jul 2023 11:11:16 +0200 Subject: [PATCH 071/100] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Marcel Klehr (cherry picked from commit 590eefea70472db84439353ddefbba6df1a88280) --- lib/private/TextProcessing/Manager.php | 2 +- lib/public/TextProcessing/Events/TaskSuccessfulEvent.php | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/private/TextProcessing/Manager.php b/lib/private/TextProcessing/Manager.php index 34e4b2bb4cc2c..f52482bbb3219 100644 --- a/lib/private/TextProcessing/Manager.php +++ b/lib/private/TextProcessing/Manager.php @@ -27,7 +27,7 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\TextProcessing\Db\Task as DbTask; -use \OCP\TextProcessing\Task as OCPTask; +use OCP\TextProcessing\Task as OCPTask; use OC\TextProcessing\Db\TaskMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; diff --git a/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php b/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php index 73fbbb87f454f..2052c45045108 100644 --- a/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php +++ b/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php @@ -8,11 +8,4 @@ * @since 27.1.0 */ class TaskSuccessfulEvent extends AbstractTextProcessingEvent { - /** - * @param Task $task - * @since 27.1.0 - */ - public function __construct(Task $task) { - parent::__construct($task); - } } From 4118baa412c6eaa6600a9d76205a8da54a33a433 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 17 Jul 2023 11:20:43 +0200 Subject: [PATCH 072/100] Update autoloaders Signed-off-by: Marcel Klehr (cherry picked from commit add5962b4c2a9839c90a7db51ab41737f032bab4) --- lib/composer/composer/autoload_classmap.php | 38 ++++++++++----------- lib/composer/composer/autoload_static.php | 38 ++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index f99bb5f440bf2..c6a9c86abda4f 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -190,6 +190,7 @@ 'OCP\\Comments\\IllegalIDChangeException' => $baseDir . '/lib/public/Comments/IllegalIDChangeException.php', 'OCP\\Comments\\MessageTooLongException' => $baseDir . '/lib/public/Comments/MessageTooLongException.php', 'OCP\\Comments\\NotFoundException' => $baseDir . '/lib/public/Comments/NotFoundException.php', + 'OCP\\Common\\Exception\\NotFoundException' => $baseDir . '/lib/public/Common/Exception/NotFoundException.php', 'OCP\\Config\\BeforePreferenceDeletedEvent' => $baseDir . '/lib/public/Config/BeforePreferenceDeletedEvent.php', 'OCP\\Config\\BeforePreferenceSetEvent' => $baseDir . '/lib/public/Config/BeforePreferenceSetEvent.php', 'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php', @@ -485,20 +486,6 @@ 'OCP\\LDAP\\IDeletionFlagSupport' => $baseDir . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => $baseDir . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => $baseDir . '/lib/public/LDAP/ILDAPProviderFactory.php', - 'OCP\\LanguageModel\\AbstractLanguageModelTask' => $baseDir . '/lib/public/LanguageModel/AbstractLanguageModelTask.php', - 'OCP\\LanguageModel\\Events\\AbstractLanguageModelEvent' => $baseDir . '/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php', - 'OCP\\LanguageModel\\Events\\TaskFailedEvent' => $baseDir . '/lib/public/LanguageModel/Events/TaskFailedEvent.php', - 'OCP\\LanguageModel\\Events\\TaskSuccessfulEvent' => $baseDir . '/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php', - 'OCP\\LanguageModel\\FreePromptTask' => $baseDir . '/lib/public/LanguageModel/FreePromptTask.php', - 'OCP\\LanguageModel\\HeadlineTask' => $baseDir . '/lib/public/LanguageModel/HeadlineTask.php', - 'OCP\\LanguageModel\\IHeadlineProvider' => $baseDir . '/lib/public/LanguageModel/IHeadlineProvider.php', - 'OCP\\LanguageModel\\ILanguageModelManager' => $baseDir . '/lib/public/LanguageModel/ILanguageModelManager.php', - 'OCP\\LanguageModel\\ILanguageModelProvider' => $baseDir . '/lib/public/LanguageModel/ILanguageModelProvider.php', - 'OCP\\LanguageModel\\ILanguageModelTask' => $baseDir . '/lib/public/LanguageModel/ILanguageModelTask.php', - 'OCP\\LanguageModel\\ISummaryProvider' => $baseDir . '/lib/public/LanguageModel/ISummaryProvider.php', - 'OCP\\LanguageModel\\ITopicsProvider' => $baseDir . '/lib/public/LanguageModel/ITopicsProvider.php', - 'OCP\\LanguageModel\\SummaryTask' => $baseDir . '/lib/public/LanguageModel/SummaryTask.php', - 'OCP\\LanguageModel\\TopicsTask' => $baseDir . '/lib/public/LanguageModel/TopicsTask.php', 'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => $baseDir . '/lib/public/Lock/ManuallyLockedException.php', @@ -632,6 +619,17 @@ 'OCP\\Talk\\IConversationOptions' => $baseDir . '/lib/public/Talk/IConversationOptions.php', 'OCP\\Talk\\ITalkBackend' => $baseDir . '/lib/public/Talk/ITalkBackend.php', 'OCP\\Template' => $baseDir . '/lib/public/Template.php', + 'OCP\\TextProcessing\\Events\\AbstractTextProcessingEvent' => $baseDir . '/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php', + 'OCP\\TextProcessing\\Events\\TaskFailedEvent' => $baseDir . '/lib/public/TextProcessing/Events/TaskFailedEvent.php', + 'OCP\\TextProcessing\\Events\\TaskSuccessfulEvent' => $baseDir . '/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php', + 'OCP\\TextProcessing\\FreePromptTaskType' => $baseDir . '/lib/public/TextProcessing/FreePromptTaskType.php', + 'OCP\\TextProcessing\\HeadlineTaskType' => $baseDir . '/lib/public/TextProcessing/HeadlineTaskType.php', + 'OCP\\TextProcessing\\IManager' => $baseDir . '/lib/public/TextProcessing/IManager.php', + 'OCP\\TextProcessing\\IProvider' => $baseDir . '/lib/public/TextProcessing/IProvider.php', + 'OCP\\TextProcessing\\ITaskType' => $baseDir . '/lib/public/TextProcessing/ITaskType.php', + 'OCP\\TextProcessing\\SummaryTaskType' => $baseDir . '/lib/public/TextProcessing/SummaryTaskType.php', + 'OCP\\TextProcessing\\Task' => $baseDir . '/lib/public/TextProcessing/Task.php', + 'OCP\\TextProcessing\\TopicsTaskType' => $baseDir . '/lib/public/TextProcessing/TopicsTaskType.php', 'OCP\\Translation\\CouldNotTranslateException' => $baseDir . '/lib/public/Translation/CouldNotTranslateException.php', 'OCP\\Translation\\IDetectLanguageProvider' => $baseDir . '/lib/public/Translation/IDetectLanguageProvider.php', 'OCP\\Translation\\ITranslationManager' => $baseDir . '/lib/public/Translation/ITranslationManager.php', @@ -1041,7 +1039,6 @@ 'OC\\Core\\Controller\\GuestAvatarController' => $baseDir . '/core/Controller/GuestAvatarController.php', 'OC\\Core\\Controller\\HoverCardController' => $baseDir . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php', - 'OC\\Core\\Controller\\LanguageModelApiController' => $baseDir . '/core/Controller/LanguageModelApiController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\NavigationController' => $baseDir . '/core/Controller/NavigationController.php', @@ -1055,6 +1052,7 @@ 'OC\\Core\\Controller\\ReferenceController' => $baseDir . '/core/Controller/ReferenceController.php', 'OC\\Core\\Controller\\SearchController' => $baseDir . '/core/Controller/SearchController.php', 'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php', + 'OC\\Core\\Controller\\TextProcessingApiController' => $baseDir . '/core/Controller/TextProcessingApiController.php', 'OC\\Core\\Controller\\TranslationApiController' => $baseDir . '/core/Controller/TranslationApiController.php', 'OC\\Core\\Controller\\TwoFactorChallengeController' => $baseDir . '/core/Controller/TwoFactorChallengeController.php', 'OC\\Core\\Controller\\UnifiedSearchController' => $baseDir . '/core/Controller/UnifiedSearchController.php', @@ -1376,10 +1374,6 @@ 'OC\\L10N\\LanguageNotFoundException' => $baseDir . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\L10N\\LazyL10N' => $baseDir . '/lib/private/L10N/LazyL10N.php', 'OC\\LDAP\\NullLDAPProviderFactory' => $baseDir . '/lib/private/LDAP/NullLDAPProviderFactory.php', - 'OC\\LanguageModel\\Db\\Task' => $baseDir . '/lib/private/LanguageModel/Db/Task.php', - 'OC\\LanguageModel\\Db\\TaskMapper' => $baseDir . '/lib/private/LanguageModel/Db/TaskMapper.php', - 'OC\\LanguageModel\\LanguageModelManager' => $baseDir . '/lib/private/LanguageModel/LanguageModelManager.php', - 'OC\\LanguageModel\\TaskBackgroundJob' => $baseDir . '/lib/private/LanguageModel/TaskBackgroundJob.php', 'OC\\LargeFileHelper' => $baseDir . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => $baseDir . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => $baseDir . '/lib/private/Lock/DBLockingProvider.php', @@ -1510,6 +1504,7 @@ 'OC\\RepairException' => $baseDir . '/lib/private/RepairException.php', 'OC\\Repair\\AddBruteForceCleanupJob' => $baseDir . '/lib/private/Repair/AddBruteForceCleanupJob.php', 'OC\\Repair\\AddCleanupUpdaterBackupsJob' => $baseDir . '/lib/private/Repair/AddCleanupUpdaterBackupsJob.php', + 'OC\\Repair\\AddRemoveOldTasksBackgroundJob' => $baseDir . '/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php', 'OC\\Repair\\CleanTags' => $baseDir . '/lib/private/Repair/CleanTags.php', 'OC\\Repair\\CleanUpAbandonedApps' => $baseDir . '/lib/private/Repair/CleanUpAbandonedApps.php', 'OC\\Repair\\ClearFrontendCaches' => $baseDir . '/lib/private/Repair/ClearFrontendCaches.php', @@ -1661,6 +1656,11 @@ 'OC\\Template\\ResourceLocator' => $baseDir . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => $baseDir . '/lib/private/Template/ResourceNotFoundException.php', 'OC\\Template\\TemplateFileLocator' => $baseDir . '/lib/private/Template/TemplateFileLocator.php', + 'OC\\TextProcessing\\Db\\Task' => $baseDir . '/lib/private/TextProcessing/Db/Task.php', + 'OC\\TextProcessing\\Db\\TaskMapper' => $baseDir . '/lib/private/TextProcessing/Db/TaskMapper.php', + 'OC\\TextProcessing\\Manager' => $baseDir . '/lib/private/TextProcessing/Manager.php', + 'OC\\TextProcessing\\RemoveOldTasksBackgroundJob' => $baseDir . '/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php', + 'OC\\TextProcessing\\TaskBackgroundJob' => $baseDir . '/lib/private/TextProcessing/TaskBackgroundJob.php', 'OC\\Translation\\TranslationManager' => $baseDir . '/lib/private/Translation/TranslationManager.php', 'OC\\URLGenerator' => $baseDir . '/lib/private/URLGenerator.php', 'OC\\Updater' => $baseDir . '/lib/private/Updater.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 2b13d4532c6c1..5e4e19e159d37 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -223,6 +223,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Comments\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Comments/IllegalIDChangeException.php', 'OCP\\Comments\\MessageTooLongException' => __DIR__ . '/../../..' . '/lib/public/Comments/MessageTooLongException.php', 'OCP\\Comments\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Comments/NotFoundException.php', + 'OCP\\Common\\Exception\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Common/Exception/NotFoundException.php', 'OCP\\Config\\BeforePreferenceDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceDeletedEvent.php', 'OCP\\Config\\BeforePreferenceSetEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceSetEvent.php', 'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php', @@ -518,20 +519,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\LDAP\\IDeletionFlagSupport' => __DIR__ . '/../../..' . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProviderFactory.php', - 'OCP\\LanguageModel\\AbstractLanguageModelTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/AbstractLanguageModelTask.php', - 'OCP\\LanguageModel\\Events\\AbstractLanguageModelEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/AbstractLanguageModelEvent.php', - 'OCP\\LanguageModel\\Events\\TaskFailedEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/TaskFailedEvent.php', - 'OCP\\LanguageModel\\Events\\TaskSuccessfulEvent' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/Events/TaskSuccessfulEvent.php', - 'OCP\\LanguageModel\\FreePromptTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/FreePromptTask.php', - 'OCP\\LanguageModel\\HeadlineTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/HeadlineTask.php', - 'OCP\\LanguageModel\\IHeadlineProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/IHeadlineProvider.php', - 'OCP\\LanguageModel\\ILanguageModelManager' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelManager.php', - 'OCP\\LanguageModel\\ILanguageModelProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelProvider.php', - 'OCP\\LanguageModel\\ILanguageModelTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ILanguageModelTask.php', - 'OCP\\LanguageModel\\ISummaryProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ISummaryProvider.php', - 'OCP\\LanguageModel\\ITopicsProvider' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/ITopicsProvider.php', - 'OCP\\LanguageModel\\SummaryTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/SummaryTask.php', - 'OCP\\LanguageModel\\TopicsTask' => __DIR__ . '/../../..' . '/lib/public/LanguageModel/TopicsTask.php', 'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php', 'OCP\\Lock\\ManuallyLockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/ManuallyLockedException.php', @@ -665,6 +652,17 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Talk\\IConversationOptions' => __DIR__ . '/../../..' . '/lib/public/Talk/IConversationOptions.php', 'OCP\\Talk\\ITalkBackend' => __DIR__ . '/../../..' . '/lib/public/Talk/ITalkBackend.php', 'OCP\\Template' => __DIR__ . '/../../..' . '/lib/public/Template.php', + 'OCP\\TextProcessing\\Events\\AbstractTextProcessingEvent' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php', + 'OCP\\TextProcessing\\Events\\TaskFailedEvent' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Events/TaskFailedEvent.php', + 'OCP\\TextProcessing\\Events\\TaskSuccessfulEvent' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php', + 'OCP\\TextProcessing\\FreePromptTaskType' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/FreePromptTaskType.php', + 'OCP\\TextProcessing\\HeadlineTaskType' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/HeadlineTaskType.php', + 'OCP\\TextProcessing\\IManager' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/IManager.php', + 'OCP\\TextProcessing\\IProvider' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/IProvider.php', + 'OCP\\TextProcessing\\ITaskType' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/ITaskType.php', + 'OCP\\TextProcessing\\SummaryTaskType' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/SummaryTaskType.php', + 'OCP\\TextProcessing\\Task' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Task.php', + 'OCP\\TextProcessing\\TopicsTaskType' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/TopicsTaskType.php', 'OCP\\Translation\\CouldNotTranslateException' => __DIR__ . '/../../..' . '/lib/public/Translation/CouldNotTranslateException.php', 'OCP\\Translation\\IDetectLanguageProvider' => __DIR__ . '/../../..' . '/lib/public/Translation/IDetectLanguageProvider.php', 'OCP\\Translation\\ITranslationManager' => __DIR__ . '/../../..' . '/lib/public/Translation/ITranslationManager.php', @@ -1074,7 +1072,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Controller\\GuestAvatarController' => __DIR__ . '/../../..' . '/core/Controller/GuestAvatarController.php', 'OC\\Core\\Controller\\HoverCardController' => __DIR__ . '/../../..' . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php', - 'OC\\Core\\Controller\\LanguageModelApiController' => __DIR__ . '/../../..' . '/core/Controller/LanguageModelApiController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\NavigationController' => __DIR__ . '/../../..' . '/core/Controller/NavigationController.php', @@ -1088,6 +1085,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Controller\\ReferenceController' => __DIR__ . '/../../..' . '/core/Controller/ReferenceController.php', 'OC\\Core\\Controller\\SearchController' => __DIR__ . '/../../..' . '/core/Controller/SearchController.php', 'OC\\Core\\Controller\\SetupController' => __DIR__ . '/../../..' . '/core/Controller/SetupController.php', + 'OC\\Core\\Controller\\TextProcessingApiController' => __DIR__ . '/../../..' . '/core/Controller/TextProcessingApiController.php', 'OC\\Core\\Controller\\TranslationApiController' => __DIR__ . '/../../..' . '/core/Controller/TranslationApiController.php', 'OC\\Core\\Controller\\TwoFactorChallengeController' => __DIR__ . '/../../..' . '/core/Controller/TwoFactorChallengeController.php', 'OC\\Core\\Controller\\UnifiedSearchController' => __DIR__ . '/../../..' . '/core/Controller/UnifiedSearchController.php', @@ -1409,10 +1407,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\L10N\\LanguageNotFoundException' => __DIR__ . '/../../..' . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\L10N\\LazyL10N' => __DIR__ . '/../../..' . '/lib/private/L10N/LazyL10N.php', 'OC\\LDAP\\NullLDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/private/LDAP/NullLDAPProviderFactory.php', - 'OC\\LanguageModel\\Db\\Task' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/Db/Task.php', - 'OC\\LanguageModel\\Db\\TaskMapper' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/Db/TaskMapper.php', - 'OC\\LanguageModel\\LanguageModelManager' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/LanguageModelManager.php', - 'OC\\LanguageModel\\TaskBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/LanguageModel/TaskBackgroundJob.php', 'OC\\LargeFileHelper' => __DIR__ . '/../../..' . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/DBLockingProvider.php', @@ -1543,6 +1537,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\RepairException' => __DIR__ . '/../../..' . '/lib/private/RepairException.php', 'OC\\Repair\\AddBruteForceCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Repair/AddBruteForceCleanupJob.php', 'OC\\Repair\\AddCleanupUpdaterBackupsJob' => __DIR__ . '/../../..' . '/lib/private/Repair/AddCleanupUpdaterBackupsJob.php', + 'OC\\Repair\\AddRemoveOldTasksBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php', 'OC\\Repair\\CleanTags' => __DIR__ . '/../../..' . '/lib/private/Repair/CleanTags.php', 'OC\\Repair\\CleanUpAbandonedApps' => __DIR__ . '/../../..' . '/lib/private/Repair/CleanUpAbandonedApps.php', 'OC\\Repair\\ClearFrontendCaches' => __DIR__ . '/../../..' . '/lib/private/Repair/ClearFrontendCaches.php', @@ -1694,6 +1689,11 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Template\\ResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceNotFoundException.php', 'OC\\Template\\TemplateFileLocator' => __DIR__ . '/../../..' . '/lib/private/Template/TemplateFileLocator.php', + 'OC\\TextProcessing\\Db\\Task' => __DIR__ . '/../../..' . '/lib/private/TextProcessing/Db/Task.php', + 'OC\\TextProcessing\\Db\\TaskMapper' => __DIR__ . '/../../..' . '/lib/private/TextProcessing/Db/TaskMapper.php', + 'OC\\TextProcessing\\Manager' => __DIR__ . '/../../..' . '/lib/private/TextProcessing/Manager.php', + 'OC\\TextProcessing\\RemoveOldTasksBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php', + 'OC\\TextProcessing\\TaskBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/TextProcessing/TaskBackgroundJob.php', 'OC\\Translation\\TranslationManager' => __DIR__ . '/../../..' . '/lib/private/Translation/TranslationManager.php', 'OC\\URLGenerator' => __DIR__ . '/../../..' . '/lib/private/URLGenerator.php', 'OC\\Updater' => __DIR__ . '/../../..' . '/lib/private/Updater.php', From 2d296ce698c7a55906d4a818fb17df15ab136588 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 17 Jul 2023 11:34:16 +0200 Subject: [PATCH 073/100] cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit 2811932247da3301c25259396c1b4a1667db1b54) --- lib/public/TextProcessing/Events/TaskSuccessfulEvent.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php b/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php index 2052c45045108..df4d2ba622741 100644 --- a/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php +++ b/lib/public/TextProcessing/Events/TaskSuccessfulEvent.php @@ -2,8 +2,6 @@ namespace OCP\TextProcessing\Events; -use OCP\TextProcessing\Task; - /** * @since 27.1.0 */ From 96ec6ff5eaa28bd7e8f53d84b266c0fb709cf205 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 17 Jul 2023 13:30:20 +0200 Subject: [PATCH 074/100] Fix psalm errors Signed-off-by: Marcel Klehr (cherry picked from commit d63c122ec0071cc1d0417eaa5eb1a85e41290263) --- lib/public/TextProcessing/FreePromptTaskType.php | 2 ++ lib/public/TextProcessing/HeadlineTaskType.php | 2 ++ lib/public/TextProcessing/IProvider.php | 1 + lib/public/TextProcessing/SummaryTaskType.php | 2 ++ lib/public/TextProcessing/TopicsTaskType.php | 2 ++ 5 files changed, 9 insertions(+) diff --git a/lib/public/TextProcessing/FreePromptTaskType.php b/lib/public/TextProcessing/FreePromptTaskType.php index aa1d6842b54f2..dcc27df77c9b8 100644 --- a/lib/public/TextProcessing/FreePromptTaskType.php +++ b/lib/public/TextProcessing/FreePromptTaskType.php @@ -46,6 +46,7 @@ public function __construct( /** * @inheritDoc + * @since 27.1.0 */ public function getName(): string { return $this->l->t('Free prompt'); @@ -53,6 +54,7 @@ public function getName(): string { /** * @inheritDoc + * @since 27.1.0 */ public function getDescription(): string { return $this->l->t('Runs an arbitrary prompt through the built-in language model.'); diff --git a/lib/public/TextProcessing/HeadlineTaskType.php b/lib/public/TextProcessing/HeadlineTaskType.php index 4ced298fd4dde..ad38848ea87cb 100644 --- a/lib/public/TextProcessing/HeadlineTaskType.php +++ b/lib/public/TextProcessing/HeadlineTaskType.php @@ -46,6 +46,7 @@ public function __construct( /** * @inheritDoc + * @since 27.1.0 */ public function getName(): string { return $this->l->t('Generate headline'); @@ -53,6 +54,7 @@ public function getName(): string { /** * @inheritDoc + * @since 27.1.0 */ public function getDescription(): string { return $this->l->t('Generates a possible headline for a text'); diff --git a/lib/public/TextProcessing/IProvider.php b/lib/public/TextProcessing/IProvider.php index 3eb83aef8c333..6132e60b49371 100644 --- a/lib/public/TextProcessing/IProvider.php +++ b/lib/public/TextProcessing/IProvider.php @@ -55,6 +55,7 @@ public function process(string $prompt): string; * Returns the task type class string of the task type, that this * provider handles * + * @since 27.1.0 * @return class-string */ public function getTaskType(): string; diff --git a/lib/public/TextProcessing/SummaryTaskType.php b/lib/public/TextProcessing/SummaryTaskType.php index 7db695c18f722..3d80cee47f8a3 100644 --- a/lib/public/TextProcessing/SummaryTaskType.php +++ b/lib/public/TextProcessing/SummaryTaskType.php @@ -46,6 +46,7 @@ public function __construct( /** * @inheritDoc + * @since 27.1.0 */ public function getName(): string { return $this->l->t('Summarize'); @@ -53,6 +54,7 @@ public function getName(): string { /** * @inheritDoc + * @since 27.1.0 */ public function getDescription(): string { return $this->l->t('Summarizes text by reducing its length without losing key information.'); diff --git a/lib/public/TextProcessing/TopicsTaskType.php b/lib/public/TextProcessing/TopicsTaskType.php index 8b41b3ee61aea..6162b9a13e98f 100644 --- a/lib/public/TextProcessing/TopicsTaskType.php +++ b/lib/public/TextProcessing/TopicsTaskType.php @@ -46,6 +46,7 @@ public function __construct( /** * @inheritDoc + * @since 27.1.0 */ public function getName(): string { return $this->l->t('Extract topics'); @@ -53,6 +54,7 @@ public function getName(): string { /** * @inheritDoc + * @since 27.1.0 */ public function getDescription(): string { return $this->l->t('Extracts topics from a text and outputs them separated by commas.'); From 00e3fb143f42ff9506ef0d5f58ce2acaa8712449 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 19 Jul 2023 13:15:14 +0200 Subject: [PATCH 075/100] Remove Task::factory method Signed-off-by: Marcel Klehr (cherry picked from commit 7389567c7d05ba994533b7273cba1e5818a20b94) --- core/Controller/TextProcessingApiController.php | 2 +- lib/private/TextProcessing/Db/Task.php | 2 +- lib/public/TextProcessing/Task.php | 17 ----------------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/core/Controller/TextProcessingApiController.php b/core/Controller/TextProcessingApiController.php index 0c723ebace348..58036fb25bc53 100644 --- a/core/Controller/TextProcessingApiController.php +++ b/core/Controller/TextProcessingApiController.php @@ -91,7 +91,7 @@ public function taskTypes(): DataResponse { */ public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { try { - $task = Task::factory($type, $input, $this->userId, $appId, $identifier); + $task = new Task($type, $input, $this->userId, $appId, $identifier); } catch (InvalidArgumentException) { return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); } diff --git a/lib/private/TextProcessing/Db/Task.php b/lib/private/TextProcessing/Db/Task.php index bc1bbdc13dba0..8e800c76df8e3 100644 --- a/lib/private/TextProcessing/Db/Task.php +++ b/lib/private/TextProcessing/Db/Task.php @@ -103,7 +103,7 @@ public static function fromPublicTask(OCPTask $task): Task { } public function toPublicTask(): OCPTask { - $task = OCPTask::factory($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); + $task = new OCPTask($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); $task->setId($this->getId()); $task->setStatus($this->getStatus()); $task->setOutput($this->getOutput()); diff --git a/lib/public/TextProcessing/Task.php b/lib/public/TextProcessing/Task.php index 668ca6d09c4a1..446e414cb045c 100644 --- a/lib/public/TextProcessing/Task.php +++ b/lib/public/TextProcessing/Task.php @@ -218,21 +218,4 @@ public function jsonSerialize(): array { 'identifier' => $this->getIdentifier(), ]; } - - /** - * @param string $type - * @param string $input - * @param string|null $userId - * @param string $appId - * @param string $identifier - * @return Task - * @throws \InvalidArgumentException - * @since 27.1.0 - */ - final public static function factory(string $type, string $input, ?string $userId, string $appId, string $identifier = ''): Task { - if (!in_array($type, self::TYPES)) { - throw new \InvalidArgumentException('Unknown task type'); - } - return new Task($type, $input, $appId, $userId, $identifier); - } } From cac51714e227ef652f97eabd0cfd888b2c5fa276 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 19 Jul 2023 17:32:28 +0200 Subject: [PATCH 076/100] Fix tests: Adjust constructor signature Signed-off-by: Marcel Klehr (cherry picked from commit fd0fd97a6594f47c9a5fc4f833c2defa07078ae2) --- lib/private/TextProcessing/Db/Task.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/TextProcessing/Db/Task.php b/lib/private/TextProcessing/Db/Task.php index 8e800c76df8e3..8c2ddb74f1fb7 100644 --- a/lib/private/TextProcessing/Db/Task.php +++ b/lib/private/TextProcessing/Db/Task.php @@ -103,7 +103,7 @@ public static function fromPublicTask(OCPTask $task): Task { } public function toPublicTask(): OCPTask { - $task = new OCPTask($this->getType(), $this->getInput(), $this->getuserId(), $this->getAppId(), $this->getIdentifier()); + $task = new OCPTask($this->getType(), $this->getInput(), $this->getAppId(), $this->getuserId(), $this->getIdentifier()); $task->setId($this->getId()); $task->setStatus($this->getStatus()); $task->setOutput($this->getOutput()); From ad645c96f3d406206d2fb4aa76b3a96ddbfaa8d3 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 19 Jul 2023 17:33:11 +0200 Subject: [PATCH 077/100] Fix tests: Adjust constructor signature Signed-off-by: Marcel Klehr (cherry picked from commit 6d568b0d32d1255f76608e9d6b4b154dc57e5fea) --- core/Controller/TextProcessingApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Controller/TextProcessingApiController.php b/core/Controller/TextProcessingApiController.php index 58036fb25bc53..9ed332644e145 100644 --- a/core/Controller/TextProcessingApiController.php +++ b/core/Controller/TextProcessingApiController.php @@ -91,7 +91,7 @@ public function taskTypes(): DataResponse { */ public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { try { - $task = new Task($type, $input, $this->userId, $appId, $identifier); + $task = new Task($type, $input, $appId, $this->userId, $identifier); } catch (InvalidArgumentException) { return new DataResponse(['message' => $this->l->t('Requested task type does not exist')], Http::STATUS_BAD_REQUEST); } From cc5818f8a9c716fe628d86f3704d0af80c34d396 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 24 Jul 2023 12:12:06 +0200 Subject: [PATCH 078/100] First pass at ai admin settings Signed-off-by: Marcel Klehr (cherry picked from commit fc9780a41d586e2983f18e128a4095484e5860ac) --- apps/settings/appinfo/info.xml | 2 + apps/settings/img/ai.svg | 1 + .../lib/Controller/AISettingsController.php | 105 +++++++++++ .../Sections/Admin/ArtificialIntelligence.php | 58 +++++++ .../Settings/Admin/ArtificialIntelligence.php | 163 ++++++++++++++++++ apps/settings/src/components/AdminAI.vue | 83 +++++++++ apps/settings/templates/settings/admin/ai.php | 28 +++ .../SpeechToText/ISpeechToTextManager.php | 6 + lib/public/TextProcessing/IManager.php | 6 + .../Translation/ITranslationManager.php | 6 + webpack.modules.js | 1 + 11 files changed, 459 insertions(+) create mode 100644 apps/settings/img/ai.svg create mode 100644 apps/settings/lib/Controller/AISettingsController.php create mode 100644 apps/settings/lib/Sections/Admin/ArtificialIntelligence.php create mode 100644 apps/settings/lib/Settings/Admin/ArtificialIntelligence.php create mode 100644 apps/settings/src/components/AdminAI.vue create mode 100644 apps/settings/templates/settings/admin/ai.php diff --git a/apps/settings/appinfo/info.xml b/apps/settings/appinfo/info.xml index 7864405e292bd..2ec40fd758d7b 100644 --- a/apps/settings/appinfo/info.xml +++ b/apps/settings/appinfo/info.xml @@ -19,6 +19,7 @@ OCA\Settings\Settings\Admin\Mail OCA\Settings\Settings\Admin\Overview + OCA\Settings\Settings\Admin\ArtificialIntelligence OCA\Settings\Settings\Admin\Server OCA\Settings\Settings\Admin\Sharing OCA\Settings\Settings\Admin\Security @@ -27,6 +28,7 @@ OCA\Settings\Sections\Admin\Delegation OCA\Settings\Sections\Admin\Groupware OCA\Settings\Sections\Admin\Overview + OCA\Settings\Sections\Admin\ArtificialIntelligence OCA\Settings\Sections\Admin\Security OCA\Settings\Sections\Admin\Server OCA\Settings\Sections\Admin\Sharing diff --git a/apps/settings/img/ai.svg b/apps/settings/img/ai.svg new file mode 100644 index 0000000000000..5d59fd6afe84d --- /dev/null +++ b/apps/settings/img/ai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/settings/lib/Controller/AISettingsController.php b/apps/settings/lib/Controller/AISettingsController.php new file mode 100644 index 0000000000000..53aca66c94633 --- /dev/null +++ b/apps/settings/lib/Controller/AISettingsController.php @@ -0,0 +1,105 @@ + + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Christoph Wurst + * @author Daniel Kesselberg + * @author Joas Schilling + * @author Lukas Reschke + * @author Morris Jobke + * @author Roeland Jago Douma + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCA\Settings\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\IUserSession; +use OCP\Mail\IMailer; + +class AISettingsController extends Controller { + + /** @var IL10N */ + private $l10n; + /** @var IConfig */ + private $config; + /** @var IUserSession */ + private $userSession; + /** @var IMailer */ + private $mailer; + /** @var IURLGenerator */ + private $urlGenerator; + + /** + * @param string $appName + * @param IRequest $request + * @param IL10N $l10n + * @param IConfig $config + * @param IUserSession $userSession + * @param IURLGenerator $urlGenerator, + * @param IMailer $mailer + */ + public function __construct($appName, + IRequest $request, + IL10N $l10n, + IConfig $config, + IUserSession $userSession, + IURLGenerator $urlGenerator, + IMailer $mailer) { + parent::__construct($appName, $request); + $this->l10n = $l10n; + $this->config = $config; + $this->userSession = $userSession; + $this->urlGenerator = $urlGenerator; + $this->mailer = $mailer; + } + + /** + * Sets the email settings + * + * @PasswordConfirmationRequired + * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\ArtificialIntelligence) + * + * @param array $settings + * @return DataResponse + */ + public function setAISettings($settings) { + $params = get_defined_vars(); + $configs = []; + foreach ($params as $key => $value) { + $configs[$key] = empty($value) ? null : $value; + } + + // Delete passwords from config in case no auth is specified + if ($params['mail_smtpauth'] !== 1) { + $configs['mail_smtpname'] = null; + $configs['mail_smtppassword'] = null; + } + + $this->config->setSystemValues($configs); + + $this->config->setAppValue('core', 'emailTestSuccessful', '0'); + + return new DataResponse(); + } +} diff --git a/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php new file mode 100644 index 0000000000000..1a25cdf515672 --- /dev/null +++ b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php @@ -0,0 +1,58 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\Sections\Admin; + +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IIconSection; + +class ArtificialIntelligence implements IIconSection { + + /** @var IL10N */ + private $l; + + /** @var IURLGenerator */ + private $urlGenerator; + + public function __construct(IL10N $l, IURLGenerator $urlGenerator) { + $this->l = $l; + $this->urlGenerator = $urlGenerator; + } + + public function getIcon(): string { + return $this->urlGenerator->imagePath('settings', 'ai.svg'); + } + + public function getID(): string { + return 'ai'; + } + + public function getName(): string { + return $this->l->t('Artificial Intelligence'); + } + + public function getPriority(): int { + return 40; + } +} diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php new file mode 100644 index 0000000000000..d33802d6c4521 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -0,0 +1,163 @@ + + * + * @author Marcel Klehr + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IServerContainer; +use OCP\Settings\IDelegatedSettings; +use OCP\SpeechToText\ISpeechToTextManager; +use OCP\TextProcessing\IManager; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\ITaskType; +use OCP\Translation\ITranslationManager; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +class ArtificialIntelligence implements IDelegatedSettings { + public function __construct( + private IConfig $config, + private IL10N $l, + private IInitialState $initialState, + private ITranslationManager $translationManager, + private ISpeechToTextManager $sttManager, + private IManager $textProcessingManager, + private IServerContainer $container, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $translationProviders = []; + $translationPreferences = []; + foreach ($this->translationManager->getProviders() as $provider) { + $translationProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + ]; + $translationPreferences[] = $provider::class; + } + + $sttProviders = []; + foreach ($this->sttManager->getProviders() as $provider) { + $sttProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + ]; + } + + $textProcessingProviders = []; + /** @var array, class-string> $textProcessingSettings */ + $textProcessingSettings = []; + foreach ($this->textProcessingManager->getProviders() as $provider) { + $textProcessingProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + 'taskType' => $provider->getTaskType(), + ]; + $textProcessingSettings[$provider->getTaskType()] = $provider::class; + } + $textProcessingTaskTypes = []; + foreach ($textProcessingSettings as $taskTypeClass => $providerClass) { + /** @var ITaskType $taskType */ + try { + $taskType = $this->container->get($taskTypeClass); + } catch (NotFoundExceptionInterface $e) { + continue; + } catch (ContainerExceptionInterface $e) { + continue; + } + $textProcessingTaskTypes[] = [ + 'class' => $taskTypeClass, + 'name' => $taskType->getName(), + 'description' => $taskType->getDescription(), + ]; + } + + $this->initialState->provideInitialState('ai-stt-providers', $sttProviders); + $this->initialState->provideInitialState('ai-translation-providers', $translationProviders); + $this->initialState->provideInitialState('ai-text-processing-providers', $textProcessingProviders); + $this->initialState->provideInitialState('ai-text-processing-task-types', $textProcessingTaskTypes); + + $settings = [ + 'ai.stt_provider' => count($sttProviders) > 0 ? $sttProviders[0]['class'] : null, + 'ai.textprocessing_provider_preferences' => $textProcessingSettings, + 'ai.translation_provider_preferences' => $translationPreferences, + ]; + foreach ($settings as $key => $defaultValue) { + $value = $defaultValue; + $json = $this->config->getAppValue('core', $key, ''); + if ($json !== '') { + $value = json_decode($json, JSON_OBJECT_AS_ARRAY); + switch($key) { + case 'ai.textprocessing_provider_preferences': + // fill $value with $defaultValue values + $value = array_merge($defaultValue, $value); + break; + case 'ai.translation_provider_preferences': + $value += array_diff($defaultValue, $value); // Add entries from $defaultValue that are not in $value to the end of $value + break; + default: + break; + } + } + $settings[$key] = $value; + } + + $this->initialState->provideInitialState('ai-settings', $settings); + + return new TemplateResponse('settings', 'settings/admin/ai'); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'ai'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 10; + } + + public function getName(): ?string { + return $this->l->t('Artificial Intelligence'); + } + + public function getAuthorizedAppConfig(): array { + return [ + 'core' => ['/ai_.*/'], + ]; + } +} diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue new file mode 100644 index 0000000000000..2e1104a49d288 --- /dev/null +++ b/apps/settings/src/components/AdminAI.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/settings/templates/settings/admin/ai.php b/apps/settings/templates/settings/admin/ai.php new file mode 100644 index 0000000000000..fac7ad8d25bea --- /dev/null +++ b/apps/settings/templates/settings/admin/ai.php @@ -0,0 +1,28 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +script('settings', [ + 'vue-settings-admin-ai', +]); +?> + +
+
diff --git a/lib/public/SpeechToText/ISpeechToTextManager.php b/lib/public/SpeechToText/ISpeechToTextManager.php index eff00ec0fa199..96973cfca08ef 100644 --- a/lib/public/SpeechToText/ISpeechToTextManager.php +++ b/lib/public/SpeechToText/ISpeechToTextManager.php @@ -40,6 +40,12 @@ interface ISpeechToTextManager { */ public function hasProviders(): bool; + /** + * @return ISpeechToTextProvider[] + * @since 27.1.0 + */ + public function getProviders(): array; + /** * Will schedule a transcription process in the background. The result will become available * with the \OCP\SpeechToText\Events\TranscriptionFinishedEvent diff --git a/lib/public/TextProcessing/IManager.php b/lib/public/TextProcessing/IManager.php index 90e25894d4f2e..50d012eca6862 100644 --- a/lib/public/TextProcessing/IManager.php +++ b/lib/public/TextProcessing/IManager.php @@ -41,6 +41,12 @@ interface IManager { */ public function hasProviders(): bool; + /** + * @return IProvider[] + * @since 27.1.0 + */ + public function getProviders(): array; + /** * @return class-string[] * @since 27.1.0 diff --git a/lib/public/Translation/ITranslationManager.php b/lib/public/Translation/ITranslationManager.php index 4450f19c42419..5b342faea75d6 100644 --- a/lib/public/Translation/ITranslationManager.php +++ b/lib/public/Translation/ITranslationManager.php @@ -38,6 +38,12 @@ interface ITranslationManager { */ public function hasProviders(): bool; + /** + * @return ITranslationProvider[] + * @since 27.1.0 + */ + public function getProviders(): array; + /** * @since 26.0.0 */ diff --git a/webpack.modules.js b/webpack.modules.js index 4bc14dbead844..b23f14c3a71fc 100644 --- a/webpack.modules.js +++ b/webpack.modules.js @@ -79,6 +79,7 @@ module.exports = { apps: path.join(__dirname, 'apps/settings/src', 'apps.js'), 'legacy-admin': path.join(__dirname, 'apps/settings/src', 'admin.js'), 'vue-settings-admin-basic-settings': path.join(__dirname, 'apps/settings/src', 'main-admin-basic-settings.js'), + 'vue-settings-admin-ai': path.join(__dirname, 'apps/settings/src', 'main-admin-ai.js'), 'vue-settings-admin-delegation': path.join(__dirname, 'apps/settings/src', 'main-admin-delegation.js'), 'vue-settings-admin-security': path.join(__dirname, 'apps/settings/src', 'main-admin-security.js'), 'vue-settings-apps-users-management': path.join(__dirname, 'apps/settings/src', 'main-apps-users-management.js'), From 43926a2c17796d209282647970f01a090197492e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 24 Jul 2023 14:06:31 +0200 Subject: [PATCH 079/100] AI Admin settings: Implement mt settings, stt settings and tp settings Signed-off-by: Marcel Klehr (cherry picked from commit 4e33d044443973f2fc82e74bca0bc43e845a36e7) --- apps/settings/src/components/AdminAI.vue | 85 ++++++++++++++++-------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue index 2e1104a49d288..f11cb0c3da046 100644 --- a/apps/settings/src/components/AdminAI.vue +++ b/apps/settings/src/components/AdminAI.vue @@ -1,34 +1,53 @@ + From d4b2012b21410417e38d3916dbf521cd772f91b5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 24 Jul 2023 14:07:34 +0200 Subject: [PATCH 080/100] fix(TextProcessing): Inject L10N\IFactory instead of IL10N Signed-off-by: Marcel Klehr (cherry picked from commit 8ec1926aba549eeaea872d7dd2fcb69592b3a3bc) --- lib/public/TextProcessing/FreePromptTaskType.php | 8 ++++++-- lib/public/TextProcessing/HeadlineTaskType.php | 10 +++++++--- lib/public/TextProcessing/SummaryTaskType.php | 9 +++++++-- lib/public/TextProcessing/TopicsTaskType.php | 8 ++++++-- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/public/TextProcessing/FreePromptTaskType.php b/lib/public/TextProcessing/FreePromptTaskType.php index dcc27df77c9b8..a1e52e268a37c 100644 --- a/lib/public/TextProcessing/FreePromptTaskType.php +++ b/lib/public/TextProcessing/FreePromptTaskType.php @@ -26,21 +26,25 @@ namespace OCP\TextProcessing; use OCP\IL10N; +use OCP\L10N\IFactory; /** * This is the text processing task type for free prompting * @since 27.1.0 */ class FreePromptTaskType implements ITaskType { + private IL10N $l; + /** * Constructor for FreePromptTaskType * - * @param IL10N $l + * @param IFactory $l10nFactory * @since 27.1.0 */ public function __construct( - private IL10N $l, + IFactory $l10nFactory, ) { + $this->l = $l10nFactory->get('core'); } diff --git a/lib/public/TextProcessing/HeadlineTaskType.php b/lib/public/TextProcessing/HeadlineTaskType.php index ad38848ea87cb..7061ca1d69bb7 100644 --- a/lib/public/TextProcessing/HeadlineTaskType.php +++ b/lib/public/TextProcessing/HeadlineTaskType.php @@ -26,21 +26,25 @@ namespace OCP\TextProcessing; use OCP\IL10N; +use OCP\L10N\IFactory; /** * This is the text processing task type for creating headline * @since 27.1.0 */ class HeadlineTaskType implements ITaskType { + private IL10N $l; + /** * Constructor for HeadlineTaskType * - * @param IL10N $l + * @param IFactory $l10nFactory * @since 27.1.0 */ public function __construct( - private IL10N $l, + IFactory $l10nFactory, ) { + $this->l = $l10nFactory->get('core'); } @@ -57,6 +61,6 @@ public function getName(): string { * @since 27.1.0 */ public function getDescription(): string { - return $this->l->t('Generates a possible headline for a text'); + return $this->l->t('Generates a possible headline for a text.'); } } diff --git a/lib/public/TextProcessing/SummaryTaskType.php b/lib/public/TextProcessing/SummaryTaskType.php index 3d80cee47f8a3..03e4b5b8ecea8 100644 --- a/lib/public/TextProcessing/SummaryTaskType.php +++ b/lib/public/TextProcessing/SummaryTaskType.php @@ -26,21 +26,26 @@ namespace OCP\TextProcessing; use OCP\IL10N; +use OCP\L10N\IFactory; /** * This is the text processing task type for summaries * @since 27.1.0 */ class SummaryTaskType implements ITaskType { + + private IL10N $l; + /** * Constructor for SummaryTaskType * - * @param IL10N $l + * @param IFactory $l10nFactory * @since 27.1.0 */ public function __construct( - private IL10N $l, + IFactory $l10nFactory, ) { + $this->l = $l10nFactory->get('core'); } diff --git a/lib/public/TextProcessing/TopicsTaskType.php b/lib/public/TextProcessing/TopicsTaskType.php index 6162b9a13e98f..5a994a7a8d21e 100644 --- a/lib/public/TextProcessing/TopicsTaskType.php +++ b/lib/public/TextProcessing/TopicsTaskType.php @@ -26,21 +26,25 @@ namespace OCP\TextProcessing; use OCP\IL10N; +use OCP\L10N\IFactory; /** * This is the text processing task type for topics extraction * @since 27.1.0 */ class TopicsTaskType implements ITaskType { + private IL10N $l; + /** * Constructor for TopicsTaskType * - * @param IL10N $l + * @param IFactory $l10nFactory * @since 27.1.0 */ public function __construct( - private IL10N $l, + IFactory $l10nFactory, ) { + $this->l = $l10nFactory->get('core'); } From 674fd354b5d264bb8775b3a6c333a473fe4108f7 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 24 Jul 2023 14:47:15 +0200 Subject: [PATCH 081/100] AI admin settings: Add save mechanism Signed-off-by: Marcel Klehr (cherry picked from commit a840e8c6e5a0d7b7c3a371776fd395976b2c2fdf) --- apps/settings/appinfo/routes.php | 1 + .../lib/Controller/AISettingsController.php | 66 ++++--------------- .../Settings/Admin/ArtificialIntelligence.php | 2 +- apps/settings/src/components/AdminAI.vue | 45 ++++++------- 4 files changed, 39 insertions(+), 75 deletions(-) diff --git a/apps/settings/appinfo/routes.php b/apps/settings/appinfo/routes.php index 938842dd576c4..e238510b1a75f 100644 --- a/apps/settings/appinfo/routes.php +++ b/apps/settings/appinfo/routes.php @@ -75,6 +75,7 @@ ['name' => 'ChangePassword#changeUserPassword', 'url' => '/settings/users/changepassword', 'verb' => 'POST' , 'root' => ''], ['name' => 'TwoFactorSettings#index', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'GET' , 'root' => ''], ['name' => 'TwoFactorSettings#update', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'PUT' , 'root' => ''], + ['name' => 'AISettings#update', 'url' => '/settings/api/admin/ai', 'verb' => 'PUT' , 'root' => ''], ['name' => 'Help#help', 'url' => '/settings/help/{mode}', 'verb' => 'GET', 'defaults' => ['mode' => ''] , 'root' => ''], diff --git a/apps/settings/lib/Controller/AISettingsController.php b/apps/settings/lib/Controller/AISettingsController.php index 53aca66c94633..2c443c1a771fc 100644 --- a/apps/settings/lib/Controller/AISettingsController.php +++ b/apps/settings/lib/Controller/AISettingsController.php @@ -1,14 +1,6 @@ - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst - * @author Daniel Kesselberg - * @author Joas Schilling - * @author Lukas Reschke - * @author Morris Jobke - * @author Roeland Jago Douma + * @copyright Copyright (c) 2023 Marcel Klehr * * @license AGPL-3.0 * @@ -36,70 +28,40 @@ use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Mail\IMailer; +use function GuzzleHttp\Promise\queue; class AISettingsController extends Controller { - /** @var IL10N */ - private $l10n; - /** @var IConfig */ - private $config; - /** @var IUserSession */ - private $userSession; - /** @var IMailer */ - private $mailer; - /** @var IURLGenerator */ - private $urlGenerator; - /** * @param string $appName * @param IRequest $request - * @param IL10N $l10n * @param IConfig $config - * @param IUserSession $userSession - * @param IURLGenerator $urlGenerator, - * @param IMailer $mailer */ - public function __construct($appName, - IRequest $request, - IL10N $l10n, - IConfig $config, - IUserSession $userSession, - IURLGenerator $urlGenerator, - IMailer $mailer) { + public function __construct( + $appName, + IRequest $request, + private IConfig $config, + ) { parent::__construct($appName, $request); - $this->l10n = $l10n; - $this->config = $config; - $this->userSession = $userSession; - $this->urlGenerator = $urlGenerator; - $this->mailer = $mailer; } /** * Sets the email settings * - * @PasswordConfirmationRequired * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\ArtificialIntelligence) * * @param array $settings * @return DataResponse */ - public function setAISettings($settings) { - $params = get_defined_vars(); - $configs = []; - foreach ($params as $key => $value) { - $configs[$key] = empty($value) ? null : $value; - } - - // Delete passwords from config in case no auth is specified - if ($params['mail_smtpauth'] !== 1) { - $configs['mail_smtpname'] = null; - $configs['mail_smtppassword'] = null; + public function update($settings) { + $keys = ['ai.stt_provider', 'ai.textprocessing_provider_preferences', 'ai.translation_provider_preferences']; + foreach ($keys as $key) { + if (!isset($settings[$key])) { + continue; + } + $this->config->setAppValue('core', $key, json_encode($settings[$key])); } - $this->config->setSystemValues($configs); - - $this->config->setAppValue('core', 'emailTestSuccessful', '0'); - return new DataResponse(); } } diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php index d33802d6c4521..ad5425d8ded9b 100644 --- a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -157,7 +157,7 @@ public function getName(): ?string { public function getAuthorizedAppConfig(): array { return [ - 'core' => ['/ai_.*/'], + 'core' => ['/ai..*/'], ]; } } diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue index f11cb0c3da046..649c484c70a24 100644 --- a/apps/settings/src/components/AdminAI.vue +++ b/apps/settings/src/components/AdminAI.vue @@ -2,8 +2,10 @@
- -
{{i+1}} {{ translationProviders.find(p => p.class === providerClass)?.name }}
+ +
+ {{ i+1 }} {{ translationProviders.find(p => p.class === providerClass)?.name }} +
+ type="radio" + @update:checked="saveChanges"> {{ provider.name }} @@ -29,9 +32,16 @@

{{ t('settings', 'Task:') }} {{ getTaskType(type).name }}

{{ getTaskType(type).description }}

 

- - - + + +

 

@@ -74,24 +84,15 @@ export default { } }, methods: { - saveChanges() { + async saveChanges() { this.loading = true - - const data = { - enforced: this.enforced, - enforcedGroups: this.enforcedGroups, - excludedGroups: this.excludedGroups, + const data = this.settings + try { + await axios.put(generateUrl('/settings/api/admin/ai'), data) + } catch (err) { + console.error('could not save changes', err) } - axios.put(generateUrl('/settings/api/admin/twofactorauth'), data) - .then(resp => resp.data) - .then(state => { - this.state = state - this.dirty = false - }) - .catch(err => { - console.error('could not save changes', err) - }) - .then(() => { this.loading = false }) + this.loading = false }, getTaskType(type) { if (!Array.isArray(this.textProcessingTaskTypes)) { From 7fd0b1b49d2e172ad1b256d1278db668db494e0f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:17:57 +0200 Subject: [PATCH 082/100] AI admin settings: Use config values in AI feature managers Signed-off-by: Marcel Klehr (cherry picked from commit 2a3ef102f7527041609ad96bc59d6b21f42980d4) --- .../Settings/Admin/ArtificialIntelligence.php | 2 +- .../SpeechToText/SpeechToTextManager.php | 15 ++++++++++- lib/private/TextProcessing/Manager.php | 18 ++++++++++++- .../Translation/TranslationManager.php | 25 ++++++++++++++++--- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php index ad5425d8ded9b..2f36e4b033070 100644 --- a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -112,7 +112,7 @@ public function getForm() { $value = $defaultValue; $json = $this->config->getAppValue('core', $key, ''); if ($json !== '') { - $value = json_decode($json, JSON_OBJECT_AS_ARRAY); + $value = json_decode($json, true); switch($key) { case 'ai.textprocessing_provider_preferences': // fill $value with $defaultValue values diff --git a/lib/private/SpeechToText/SpeechToTextManager.php b/lib/private/SpeechToText/SpeechToTextManager.php index 757fc02485e98..bdd04ad365176 100644 --- a/lib/private/SpeechToText/SpeechToTextManager.php +++ b/lib/private/SpeechToText/SpeechToTextManager.php @@ -34,6 +34,7 @@ use OCP\Files\File; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; +use OCP\IConfig; use OCP\IServerContainer; use OCP\PreConditionNotMetException; use OCP\SpeechToText\ISpeechToTextManager; @@ -53,6 +54,7 @@ public function __construct( private Coordinator $coordinator, private LoggerInterface $logger, private IJobList $jobList, + private IConfig $config, ) { } @@ -111,7 +113,18 @@ public function transcribeFile(File $file): string { throw new PreConditionNotMetException('No SpeechToText providers have been registered'); } - foreach ($this->getProviders() as $provider) { + $providers = $this->getProviders(); + + $json = $this->config->getAppValue('core', 'ai.stt_provider', ''); + if ($json !== '') { + $className = json_decode($json, true); + $provider = current(array_filter($providers, fn ($provider) => $provider::class === $className)); + if ($provider !== false) { + $providers = [$provider]; + } + } + + foreach ($providers as $provider) { try { return $provider->transcribeFile($file); } catch (\Throwable $e) { diff --git a/lib/private/TextProcessing/Manager.php b/lib/private/TextProcessing/Manager.php index f52482bbb3219..05e046a004978 100644 --- a/lib/private/TextProcessing/Manager.php +++ b/lib/private/TextProcessing/Manager.php @@ -27,6 +27,7 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\TextProcessing\Db\Task as DbTask; +use OCP\IConfig; use OCP\TextProcessing\Task as OCPTask; use OC\TextProcessing\Db\TaskMapper; use OCP\AppFramework\Db\DoesNotExistException; @@ -52,6 +53,7 @@ public function __construct( private LoggerInterface $logger, private IJobList $jobList, private TaskMapper $taskMapper, + private IConfig $config, ) { } @@ -111,7 +113,21 @@ public function runTask(OCPTask $task): string { if (!$this->canHandleTask($task)) { throw new PreConditionNotMetException('No text processing provider is installed that can handle this task'); } - foreach ($this->getProviders() as $provider) { + $providers = $this->getProviders(); + $json = $this->config->getAppValue('core', 'ai.textprocessing_provider_preferences', ''); + if ($json !== '') { + $preferences = json_decode($json, true); + if (isset($preferences[$task->getType()])) { + // If a preference for this task type is set, move the preferred provider to the start + $provider = current(array_filter($providers, fn ($provider) => $provider::class === $preferences[$task->getType()])); + if ($provider !== false) { + $providers = array_filter($providers, fn ($p) => $p !== $provider); + array_unshift($providers, $provider); + } + } + } + + foreach ($providers as $provider) { if (!$task->canUseProvider($provider)) { continue; } diff --git a/lib/private/Translation/TranslationManager.php b/lib/private/Translation/TranslationManager.php index 8456c41cdfceb..48a0e2cdebdda 100644 --- a/lib/private/Translation/TranslationManager.php +++ b/lib/private/Translation/TranslationManager.php @@ -28,6 +28,7 @@ use InvalidArgumentException; use OC\AppFramework\Bootstrap\Coordinator; +use OCP\IConfig; use OCP\IServerContainer; use OCP\PreConditionNotMetException; use OCP\Translation\CouldNotTranslateException; @@ -48,6 +49,7 @@ public function __construct( private IServerContainer $serverContainer, private Coordinator $coordinator, private LoggerInterface $logger, + private IConfig $config, ) { } @@ -64,8 +66,25 @@ public function translate(string $text, ?string &$fromLanguage, string $toLangua throw new PreConditionNotMetException('No translation providers available'); } + $providers = $this->getProviders(); + $json = $this->config->getAppValue('core', 'ai.translation_provider_preferences', ''); + + if ($json !== '') { + $precedence = json_decode($json, true); + $newProviders = []; + foreach ($precedence as $className) { + $provider = current(array_filter($providers, fn ($provider) => $provider::class === $className)); + if ($provider !== false) { + $newProviders[] = $provider; + } + } + // Add all providers that haven't been added so far + $newProviders += array_udiff($providers, $newProviders, fn ($a, $b) => $a::class > $b::class ? 1 : ($a::class < $b::class ? -1 : 0)); + $providers = $newProviders; + } + if ($fromLanguage === null) { - foreach ($this->getProviders() as $provider) { + foreach ($providers as $provider) { if ($provider instanceof IDetectLanguageProvider) { $fromLanguage = $provider->detectLanguage($text); } @@ -84,11 +103,11 @@ public function translate(string $text, ?string &$fromLanguage, string $toLangua return $text; } - foreach ($this->getProviders() as $provider) { + foreach ($providers as $provider) { try { return $provider->translate($fromLanguage, $toLanguage, $text); } catch (RuntimeException $e) { - $this->logger->warning("Failed to translate from {$fromLanguage} to {$toLanguage}", ['exception' => $e]); + $this->logger->warning("Failed to translate from {$fromLanguage} to {$toLanguage} using provider {$provider->getName()}", ['exception' => $e]); } } From 850a92ec880845889cd56d702f8b7b1d1db4cc10 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:18:28 +0200 Subject: [PATCH 083/100] AI admin settings: Add a draggable icon in UI for translation provider precedence Signed-off-by: Marcel Klehr (cherry picked from commit 2d29413d5d7193b97c808c983d57d73efd463d23) --- apps/settings/src/components/AdminAI.vue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue index 649c484c70a24..5461e3f69d8d8 100644 --- a/apps/settings/src/components/AdminAI.vue +++ b/apps/settings/src/components/AdminAI.vue @@ -4,7 +4,7 @@ :description="t('settings', 'Machine translation can be implemented by different apps. Here you can define the precedence of the machine translation apps you have installed at the moment.')">
- {{ i+1 }} {{ translationProviders.find(p => p.class === providerClass)?.name }} + {{ i+1 }} {{ translationProviders.find(p => p.class === providerClass)?.name }}
@@ -58,6 +58,7 @@ import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadi import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js' import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js' import draggable from 'vuedraggable' +import DragVerticalIcon from 'vue-material-design-icons/DragVertical.vue' import { loadState } from '@nextcloud/initial-state' import { generateUrl } from '@nextcloud/router' @@ -69,6 +70,7 @@ export default { NcSettingsSection, NcSelect, draggable, + DragVerticalIcon, }, data() { return { @@ -86,7 +88,7 @@ export default { methods: { async saveChanges() { this.loading = true - const data = this.settings + const data = {settings: this.settings} try { await axios.put(generateUrl('/settings/api/admin/ai'), data) } catch (err) { @@ -108,10 +110,19 @@ export default { margin-bottom: 5px; } +.draggable__item, +.draggable__item * { + cursor: grab; +} + .draggable__number { border-radius: 20px; border: 2px solid var(--color-primary-default); color: var(--color-primary-default); padding: 2px 7px; } + +.drag-vertical-icon { + float: left; +} From 40d4da1319b8e32fa8af28bd9d33008673ac6f43 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:21:37 +0200 Subject: [PATCH 084/100] AI admin settings: Update composer autoloaders Signed-off-by: Marcel Klehr (cherry picked from commit aa74d6fd31fb4324fe8daead1d3a2c57ee39f4f3) --- apps/settings/composer/composer/autoload_classmap.php | 3 +++ apps/settings/composer/composer/autoload_static.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 32de1ff6d2a7e..a2d62b530179e 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -16,6 +16,7 @@ 'OCA\\Settings\\Activity\\Setting' => $baseDir . '/../lib/Activity/Setting.php', 'OCA\\Settings\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => $baseDir . '/../lib/BackgroundJobs/VerifyUserData.php', + 'OCA\\Settings\\Controller\\AISettingsController' => $baseDir . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => $baseDir . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => $baseDir . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => $baseDir . '/../lib/Controller/AuthSettingsController.php', @@ -42,6 +43,7 @@ 'OCA\\Settings\\Search\\AppSearch' => $baseDir . '/../lib/Search/AppSearch.php', 'OCA\\Settings\\Search\\SectionSearch' => $baseDir . '/../lib/Search/SectionSearch.php', 'OCA\\Settings\\Sections\\Admin\\Additional' => $baseDir . '/../lib/Sections/Admin/Additional.php', + 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => $baseDir . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => $baseDir . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => $baseDir . '/../lib/Sections/Admin/Groupware.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => $baseDir . '/../lib/Sections/Admin/Overview.php', @@ -56,6 +58,7 @@ 'OCA\\Settings\\Service\\AuthorizedGroupService' => $baseDir . '/../lib/Service/AuthorizedGroupService.php', 'OCA\\Settings\\Service\\NotFoundException' => $baseDir . '/../lib/Service/NotFoundException.php', 'OCA\\Settings\\Service\\ServiceException' => $baseDir . '/../lib/Service/ServiceException.php', + 'OCA\\Settings\\Settings\\Admin\\ArtificialIntelligence' => $baseDir . '/../lib/Settings/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Settings\\Admin\\Delegation' => $baseDir . '/../lib/Settings/Admin/Delegation.php', 'OCA\\Settings\\Settings\\Admin\\Mail' => $baseDir . '/../lib/Settings/Admin/Mail.php', 'OCA\\Settings\\Settings\\Admin\\Overview' => $baseDir . '/../lib/Settings/Admin/Overview.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 57235766a7c5e..c76e3d84ae317 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -31,6 +31,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Activity\\Setting' => __DIR__ . '/..' . '/../lib/Activity/Setting.php', 'OCA\\Settings\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => __DIR__ . '/..' . '/../lib/BackgroundJobs/VerifyUserData.php', + 'OCA\\Settings\\Controller\\AISettingsController' => __DIR__ . '/..' . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AuthSettingsController.php', @@ -57,6 +58,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Search\\AppSearch' => __DIR__ . '/..' . '/../lib/Search/AppSearch.php', 'OCA\\Settings\\Search\\SectionSearch' => __DIR__ . '/..' . '/../lib/Search/SectionSearch.php', 'OCA\\Settings\\Sections\\Admin\\Additional' => __DIR__ . '/..' . '/../lib/Sections/Admin/Additional.php', + 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => __DIR__ . '/..' . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => __DIR__ . '/..' . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => __DIR__ . '/..' . '/../lib/Sections/Admin/Groupware.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => __DIR__ . '/..' . '/../lib/Sections/Admin/Overview.php', @@ -71,6 +73,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Service\\AuthorizedGroupService' => __DIR__ . '/..' . '/../lib/Service/AuthorizedGroupService.php', 'OCA\\Settings\\Service\\NotFoundException' => __DIR__ . '/..' . '/../lib/Service/NotFoundException.php', 'OCA\\Settings\\Service\\ServiceException' => __DIR__ . '/..' . '/../lib/Service/ServiceException.php', + 'OCA\\Settings\\Settings\\Admin\\ArtificialIntelligence' => __DIR__ . '/..' . '/../lib/Settings/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Settings\\Admin\\Delegation' => __DIR__ . '/..' . '/../lib/Settings/Admin/Delegation.php', 'OCA\\Settings\\Settings\\Admin\\Mail' => __DIR__ . '/..' . '/../lib/Settings/Admin/Mail.php', 'OCA\\Settings\\Settings\\Admin\\Overview' => __DIR__ . '/..' . '/../lib/Settings/Admin/Overview.php', From 6c8c047b4d20d5f74b31d849f405b0d1d53ac4f2 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:27:20 +0200 Subject: [PATCH 085/100] AI admin settings: strict_types Signed-off-by: Marcel Klehr (cherry picked from commit f72f3f7df4f551b8a6c01d3e3b5969d1bb0264d8) --- apps/settings/lib/Controller/AISettingsController.php | 3 +++ apps/settings/lib/Settings/Admin/ArtificialIntelligence.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/settings/lib/Controller/AISettingsController.php b/apps/settings/lib/Controller/AISettingsController.php index 2c443c1a771fc..7f016d79c2595 100644 --- a/apps/settings/lib/Controller/AISettingsController.php +++ b/apps/settings/lib/Controller/AISettingsController.php @@ -1,4 +1,7 @@ * diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php index 2f36e4b033070..ab95358683e03 100644 --- a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -1,4 +1,7 @@ * From 832e0d392d5ba85619f5f0ba87c2606d269264dc Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:28:57 +0200 Subject: [PATCH 086/100] AI admin settings: cs:fix Signed-off-by: Marcel Klehr (cherry picked from commit 4ec150c9b6d24e877a7843fc005cfb6b8997363b) --- lib/public/TextProcessing/SummaryTaskType.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/public/TextProcessing/SummaryTaskType.php b/lib/public/TextProcessing/SummaryTaskType.php index 03e4b5b8ecea8..670a7cb4da602 100644 --- a/lib/public/TextProcessing/SummaryTaskType.php +++ b/lib/public/TextProcessing/SummaryTaskType.php @@ -33,7 +33,6 @@ * @since 27.1.0 */ class SummaryTaskType implements ITaskType { - private IL10N $l; /** From 8507112f90f95e233f39ab015885966c29b0c507 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 25 Jul 2023 14:47:27 +0200 Subject: [PATCH 087/100] AI admin settings: lint:fix Signed-off-by: Marcel Klehr (cherry picked from commit 112268a48aebf2f2025ee106f41fd6186b08bb59) --- apps/settings/src/admin.js | 2 +- apps/settings/src/components/AdminAI.vue | 2 +- .../AdminDelegation/GroupSelect.vue | 4 +- .../src/components/AuthTokenSection.vue | 2 +- apps/settings/src/components/Markdown.vue | 2 +- .../components/PersonalInfo/AvatarSection.vue | 12 +++--- .../PersonalInfo/DetailsSection.vue | 20 +++++----- .../PersonalInfo/DisplayNameSection.vue | 2 +- .../EmailSection/EmailSection.vue | 6 +-- .../PersonalInfo/LanguageSection/Language.vue | 2 +- .../PersonalInfo/LocaleSection/Locale.vue | 4 +- .../shared/AccountPropertySection.vue | 2 +- .../PersonalInfo/shared/HeaderBar.vue | 2 +- apps/settings/src/main-admin-ai.js | 39 +++++++++++++++++++ apps/settings/src/main-admin-security.js | 2 +- 15 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 apps/settings/src/main-admin-ai.js diff --git a/apps/settings/src/admin.js b/apps/settings/src/admin.js index 8bdfa1d0770f1..6d84405c8b857 100644 --- a/apps/settings/src/admin.js +++ b/apps/settings/src/admin.js @@ -227,7 +227,7 @@ window.addEventListener('DOMContentLoaded', () => { OC.SetupChecks.checkSetup(), OC.SetupChecks.checkGeneric(), OC.SetupChecks.checkWOFF2Loading(OC.filePath('core', '', 'fonts/NotoSans-Regular-latin.woff2'), OC.theme.docPlaceholderUrl), - OC.SetupChecks.checkDataProtected() + OC.SetupChecks.checkDataProtected(), ).then((check1, check2, check3, check4, check5, check6, check7, check8, check9, check10, check11) => { const messages = [].concat(check1, check2, check3, check4, check5, check6, check7, check8, check9, check10, check11) const $el = $('#postsetupchecks') diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue index 5461e3f69d8d8..05175bb798801 100644 --- a/apps/settings/src/components/AdminAI.vue +++ b/apps/settings/src/components/AdminAI.vue @@ -88,7 +88,7 @@ export default { methods: { async saveChanges() { this.loading = true - const data = {settings: this.settings} + const data = { settings: this.settings } try { await axios.put(generateUrl('/settings/api/admin/ai'), data) } catch (err) { diff --git a/apps/settings/src/components/AdminDelegation/GroupSelect.vue b/apps/settings/src/components/AdminDelegation/GroupSelect.vue index 82b5e51fb4502..915935167604a 100644 --- a/apps/settings/src/components/AdminDelegation/GroupSelect.vue +++ b/apps/settings/src/components/AdminDelegation/GroupSelect.vue @@ -1,6 +1,6 @@