Browse Source
First pass at ai admin settings
First pass at ai admin settings
Signed-off-by: Marcel Klehr <mklehr@gmx.net>pull/39567/head
committed by
Julien Veyssier
No known key found for this signature in database
GPG Key ID: 4141FEE162030638
11 changed files with 459 additions and 0 deletions
-
2apps/settings/appinfo/info.xml
-
1apps/settings/img/ai.svg
-
105apps/settings/lib/Controller/AISettingsController.php
-
58apps/settings/lib/Sections/Admin/ArtificialIntelligence.php
-
163apps/settings/lib/Settings/Admin/ArtificialIntelligence.php
-
83apps/settings/src/components/AdminAI.vue
-
28apps/settings/templates/settings/admin/ai.php
-
6lib/public/SpeechToText/ISpeechToTextManager.php
-
6lib/public/TextProcessing/IManager.php
-
6lib/public/Translation/ITranslationManager.php
-
1webpack.modules.js
@ -0,0 +1 @@ |
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z" /></svg> |
@ -0,0 +1,105 @@ |
|||
<?php |
|||
/** |
|||
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> |
|||
* @copyright Copyright (c) 2016, ownCloud, Inc. |
|||
* |
|||
* @author Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* @author Daniel Kesselberg <mail@danielkesselberg.de> |
|||
* @author Joas Schilling <coding@schilljs.com> |
|||
* @author Lukas Reschke <lukas@statuscode.ch> |
|||
* @author Morris Jobke <hey@morrisjobke.de> |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/> |
|||
* |
|||
*/ |
|||
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(); |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/** |
|||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
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; |
|||
} |
|||
} |
@ -0,0 +1,163 @@ |
|||
<?php |
|||
/** |
|||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
|||
* |
|||
* @author Marcel Klehr <mklehr@gmx.net> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
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<ITaskType>, class-string<IProvider>> $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_.*/'], |
|||
]; |
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
<template> |
|||
<NcSettingsSection :title="t('settings', 'Artificial Intelligence')" |
|||
:description="t('settings', 'Artificial Intelligence features can be implemented by different apps. Here you can set which app should be used for which features.')"> |
|||
<h3>{{ t('settings', 'Translations') }}</h3> |
|||
<h3>{{ t('settings', 'Speech-To-Text') }}</h3> |
|||
<template v-for="provider in sttProviders"> |
|||
<NcCheckboxRadioSwitch :key="provider.class" |
|||
:checked.sync="settings['ai.stt_provider']" |
|||
:value="provider.class" |
|||
name="stt_provider" |
|||
type="radio">{{ provider.name }}</NcCheckboxRadioSwitch> |
|||
</template> |
|||
<template v-if="sttProviders.length === 0"> |
|||
<NcCheckboxRadioSwitch disabled type="radio">{{ t('settings', 'No apps are currently installed that provide Speech-To-Text functionality') }}</NcCheckboxRadioSwitch> |
|||
</template> |
|||
<h3>{{ t('settings', 'Text processing') }}</h3> |
|||
<template v-for="(type, provider) in settings['ai.textprocessing_provider_preferences']"> |
|||
<h4>{{ type }}</h4> |
|||
<!--<p>{{ getTaskType(type).description }}</p> |
|||
<NcSelect v-model="settings['ai.textprocessing_provider_preferences'][type]" :options="textProcessingProviders.filter(provider => provider.taskType === type)" />--> |
|||
</template> |
|||
<template v-if="Object.keys(settings['ai.textprocessing_provider_preferences']).length === 0 || !Array.isArray(this.textProcessingTaskTypes)"> |
|||
<p>{{ t('settings', 'No apps are currently installed that provide Text processing functionality') }}</p> |
|||
</template> |
|||
</NcSettingsSection> |
|||
</template> |
|||
|
|||
<script> |
|||
import axios from '@nextcloud/axios' |
|||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' |
|||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js' |
|||
import { loadState } from '@nextcloud/initial-state' |
|||
|
|||
import { generateUrl } from '@nextcloud/router' |
|||
|
|||
export default { |
|||
name: 'AdminAI', |
|||
components: { |
|||
NcCheckboxRadioSwitch, |
|||
NcSettingsSection, |
|||
}, |
|||
data() { |
|||
return { |
|||
loading: false, |
|||
dirty: false, |
|||
groups: [], |
|||
loadingGroups: false, |
|||
sttProviders: loadState('settings', 'ai-stt-providers'), |
|||
translationProviders: loadState('settings', 'ai-translation-providers'), |
|||
textProcessingProviders: loadState('settings', 'ai-text-processing-providers'), |
|||
textProcessingTaskTypes: loadState('settings', 'ai-text-processing-task-types'), |
|||
settings: loadState('settings', 'ai-settings'), |
|||
} |
|||
}, |
|||
methods: { |
|||
saveChanges() { |
|||
this.loading = true |
|||
|
|||
const data = { |
|||
enforced: this.enforced, |
|||
enforcedGroups: this.enforcedGroups, |
|||
excludedGroups: this.excludedGroups, |
|||
} |
|||
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 }) |
|||
}, |
|||
getTaskType(type) { |
|||
if (!Array.isArray(this.textProcessingTaskTypes)) { |
|||
return null |
|||
} |
|||
return this.textProcessingTaskTypes.find(taskType => taskType === type) |
|||
} |
|||
}, |
|||
} |
|||
</script> |
@ -0,0 +1,28 @@ |
|||
<?php |
|||
/** |
|||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
script('settings', [ |
|||
'vue-settings-admin-ai', |
|||
]); |
|||
?>
|
|||
|
|||
<div id="ai-settings"> |
|||
</div> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue