From fc9780a41d586e2983f18e128a4095484e5860ac Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 24 Jul 2023 12:12:06 +0200 Subject: [PATCH] First pass at ai admin settings Signed-off-by: Marcel Klehr --- 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 92d2928cd31..fefa2fadaef 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 00000000000..5d59fd6afe8 --- /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 00000000000..53aca66c946 --- /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 00000000000..1a25cdf5156 --- /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 00000000000..d33802d6c45 --- /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 00000000000..2e1104a49d2 --- /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 00000000000..fac7ad8d25b --- /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 eff00ec0fa1..96973cfca08 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 90e25894d4f..50d012eca68 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 4450f19c424..5b342faea75 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 bbdbb720245..31c12f09fb2 100644 --- a/webpack.modules.js +++ b/webpack.modules.js @@ -82,6 +82,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'),