mirror of
https://github.com/nextcloud/server.git
synced 2026-02-27 18:37:17 +01:00
feat(settings/admin/ai): Add Task Processing API settings
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
committed by
Julien Veyssier
parent
e4368f6a9f
commit
9dca5ae0e0
@@ -37,7 +37,7 @@ class AISettingsController extends Controller {
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function update($settings) {
|
||||
$keys = ['ai.stt_provider', 'ai.textprocessing_provider_preferences', 'ai.translation_provider_preferences', 'ai.text2image_provider'];
|
||||
$keys = ['ai.stt_provider', 'ai.textprocessing_provider_preferences', 'ai.taskprocessing_provider_preferences', 'ai.translation_provider_preferences', 'ai.text2image_provider'];
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($settings[$key])) {
|
||||
continue;
|
||||
|
||||
@@ -35,6 +35,7 @@ class ArtificialIntelligence implements IDelegatedSettings {
|
||||
private IManager $textProcessingManager,
|
||||
private ContainerInterface $container,
|
||||
private \OCP\TextToImage\IManager $text2imageManager,
|
||||
private \OCP\TaskProcessing\IManager $taskProcessingManager,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -98,17 +99,42 @@ class ArtificialIntelligence implements IDelegatedSettings {
|
||||
];
|
||||
}
|
||||
|
||||
$taskProcessingProviders = [];
|
||||
/** @var array<class-string<ITaskType>, string|class-string<IProvider>> $taskProcessingSettings */
|
||||
$taskProcessingSettings = [];
|
||||
foreach ($this->taskProcessingManager->getProviders() as $provider) {
|
||||
$taskProcessingProviders[] = [
|
||||
'id' => $provider->getId(),
|
||||
'name' => $provider->getName(),
|
||||
'taskType' => $provider->getTaskTypeId(),
|
||||
];
|
||||
if (!isset($taskProcessingSettings[$provider->getTaskTypeId()])) {
|
||||
$taskProcessingSettings[$provider->getTaskTypeId()] = $provider->getId();
|
||||
}
|
||||
}
|
||||
$taskProcessingTaskTypes = [];
|
||||
foreach ($this->taskProcessingManager->getAvailableTaskTypes() as $taskTypeId => $taskTypeDefinition) {
|
||||
$taskProcessingTaskTypes[] = [
|
||||
'id' => $taskTypeId,
|
||||
'name' => $taskTypeDefinition['name'],
|
||||
'description' => $taskTypeDefinition['description'],
|
||||
];
|
||||
}
|
||||
|
||||
$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);
|
||||
$this->initialState->provideInitialState('ai-text2image-providers', $text2imageProviders);
|
||||
$this->initialState->provideInitialState('ai-task-processing-providers', $taskProcessingProviders);
|
||||
$this->initialState->provideInitialState('ai-task-processing-task-types', $taskProcessingTaskTypes);
|
||||
|
||||
$settings = [
|
||||
'ai.stt_provider' => count($sttProviders) > 0 ? $sttProviders[0]['class'] : null,
|
||||
'ai.textprocessing_provider_preferences' => $textProcessingSettings,
|
||||
'ai.translation_provider_preferences' => $translationPreferences,
|
||||
'ai.textprocessing_provider_preferences' => $textProcessingSettings,
|
||||
'ai.text2image_provider' => count($text2imageProviders) > 0 ? $text2imageProviders[0]['id'] : null,
|
||||
'ai.taskprocessing_provider_preferences' => $taskProcessingSettings,
|
||||
];
|
||||
foreach ($settings as $key => $defaultValue) {
|
||||
$value = $defaultValue;
|
||||
@@ -116,6 +142,7 @@ class ArtificialIntelligence implements IDelegatedSettings {
|
||||
if ($json !== '') {
|
||||
$value = json_decode($json, true);
|
||||
switch($key) {
|
||||
case 'ai.taskprocessing_provider_preferences':
|
||||
case 'ai.textprocessing_provider_preferences':
|
||||
// fill $value with $defaultValue values
|
||||
$value = array_merge($defaultValue, $value);
|
||||
|
||||
@@ -3,7 +3,34 @@
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<div class="ai-settings">
|
||||
<NcSettingsSection :name="t('settings', 'Unified task processing')"
|
||||
:description="t('settings', 'AI tasks can be implemented by different apps. Here you can set which app should be used for which task.')">
|
||||
<template v-for="type in taskProcessingTaskTypes">
|
||||
<div :key="type">
|
||||
<h3>{{ t('settings', 'Task:') }} {{ type.name }}</h3>
|
||||
<p>{{ type.description }}</p>
|
||||
<p> </p>
|
||||
<NcSelect v-model="settings['ai.taskprocessing_provider_preferences'][type.id]"
|
||||
:clearable="false"
|
||||
:options="taskProcessingProviders.filter(p => p.taskType === type.id).map(p => p.id)"
|
||||
@input="saveChanges">
|
||||
<template #option="{label}">
|
||||
{{ taskProcessingProviders.find(p => p.id === label)?.name }}
|
||||
</template>
|
||||
<template #selected-option="{label}">
|
||||
{{ taskProcessingProviders.find(p => p.id === label)?.name }}
|
||||
</template>
|
||||
</NcSelect>
|
||||
<p> </p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!hasTaskProcessing">
|
||||
<NcNoteCard type="info">
|
||||
{{ t('settings', 'None of your currently installed apps provide Task processing functionality') }}
|
||||
</NcNoteCard>
|
||||
</template>
|
||||
</NcSettingsSection>
|
||||
<NcSettingsSection :name="t('settings', 'Machine translation')"
|
||||
: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.')">
|
||||
<draggable v-model="settings['ai.translation_provider_preferences']" @change="saveChanges">
|
||||
@@ -62,8 +89,8 @@
|
||||
:description="t('settings', 'Text processing tasks can be implemented by different apps. Here you can set which app should be used for which task.')">
|
||||
<template v-for="type in tpTaskTypes">
|
||||
<div :key="type">
|
||||
<h3>{{ t('settings', 'Task:') }} {{ getTaskType(type).name }}</h3>
|
||||
<p>{{ getTaskType(type).description }}</p>
|
||||
<h3>{{ t('settings', 'Task:') }} {{ getTextProcessingTaskType(type).name }}</h3>
|
||||
<p>{{ getTextProcessingTaskType(type).description }}</p>
|
||||
<p> </p>
|
||||
<NcSelect v-model="settings['ai.textprocessing_provider_preferences'][type]"
|
||||
:clearable="false"
|
||||
@@ -127,6 +154,8 @@ export default {
|
||||
textProcessingProviders: loadState('settings', 'ai-text-processing-providers'),
|
||||
textProcessingTaskTypes: loadState('settings', 'ai-text-processing-task-types'),
|
||||
text2imageProviders: loadState('settings', 'ai-text2image-providers'),
|
||||
taskProcessingProviders: loadState('settings', 'ai-task-processing-providers'),
|
||||
taskProcessingTaskTypes: loadState('settings', 'ai-task-processing-task-types'),
|
||||
settings: loadState('settings', 'ai-settings'),
|
||||
}
|
||||
},
|
||||
@@ -138,11 +167,14 @@ export default {
|
||||
return Object.keys(this.settings['ai.textprocessing_provider_preferences']).length > 0 && Array.isArray(this.textProcessingTaskTypes)
|
||||
},
|
||||
tpTaskTypes() {
|
||||
return Object.keys(this.settings['ai.textprocessing_provider_preferences']).filter(type => !!this.getTaskType(type))
|
||||
return Object.keys(this.settings['ai.textprocessing_provider_preferences']).filter(type => !!this.getTextProcessingTaskType(type))
|
||||
},
|
||||
hasText2ImageProviders() {
|
||||
return this.text2imageProviders.length > 0
|
||||
},
|
||||
hasTaskProcessing() {
|
||||
return Object.keys(this.settings['ai.taskprocessing_provider_preferences']).length > 0 && Array.isArray(this.taskProcessingTaskTypes)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
moveUp(i) {
|
||||
@@ -171,7 +203,7 @@ export default {
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
getTaskType(type) {
|
||||
getTextProcessingTaskType(type) {
|
||||
if (!Array.isArray(this.textProcessingTaskTypes)) {
|
||||
return null
|
||||
}
|
||||
@@ -203,4 +235,8 @@ export default {
|
||||
.drag-vertical-icon {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ai-settings h3 {
|
||||
font-size: 16px; /* to offset against the 20px section heading */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -24,6 +24,7 @@ use OCP\Files\IAppData;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\L10N\IFactory;
|
||||
@@ -66,6 +67,7 @@ class Manager implements IManager {
|
||||
private IAppData $appData;
|
||||
|
||||
public function __construct(
|
||||
private IConfig $config,
|
||||
private Coordinator $coordinator,
|
||||
private IServerContainer $serverContainer,
|
||||
private LoggerInterface $logger,
|
||||
@@ -497,11 +499,23 @@ class Manager implements IManager {
|
||||
}
|
||||
|
||||
public function getPreferredProvider(string $taskType) {
|
||||
$providers = $this->getProviders();
|
||||
foreach ($providers as $provider) {
|
||||
if ($provider->getTaskTypeId() === $taskType) {
|
||||
return $provider;
|
||||
try {
|
||||
$preferences = json_decode($this->config->getAppValue('core', 'ai.taskprocessing_provider_preferences', 'null'), associative: true, flags: JSON_THROW_ON_ERROR);
|
||||
if (isset($preferences[$taskType])) {
|
||||
$providers = $this->getProviders();
|
||||
$provider = current(array_values(array_filter($providers, fn ($provider) => $provider->getId() === $preferences[$taskType])));
|
||||
if ($provider !== false) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
// By default, use the first available provider
|
||||
foreach ($providers as $provider) {
|
||||
if ($provider->getTaskTypeId() === $taskType) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
} catch (\JsonException $e) {
|
||||
$this->logger->warning('Failed to parse provider preferences while getting preferred provider for task type ' . $taskType, ['exception' => $e]);
|
||||
}
|
||||
throw new \OCP\TaskProcessing\Exception\Exception('No matching provider found');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user