Skip to content

Commit 05d4bb2

Browse files
authored
Merge pull request #252 from nextcloud/feat/auto-detect
feat: implement auto detection for task types and toggle for analyze image
2 parents f0dcbe2 + 804ef06 commit 05d4bb2

File tree

6 files changed

+163
-10
lines changed

6 files changed

+163
-10
lines changed

appinfo/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
['name' => 'config#setSensitiveUserConfig', 'url' => '/config/sensitive', 'verb' => 'PUT'],
1212
['name' => 'config#setAdminConfig', 'url' => '/admin-config', 'verb' => 'PUT'],
1313
['name' => 'config#setSensitiveAdminConfig', 'url' => '/admin-config/sensitive', 'verb' => 'PUT'],
14+
['name' => 'config#autoDetectFeatures', 'url' => '/admin-config/auto-detect-features', 'verb' => 'POST'],
1415

1516
['name' => 'openAiAPI#getModels', 'url' => '/models', 'verb' => 'GET'],
1617
['name' => 'openAiAPI#getUserQuotaInfo', 'url' => '/quota-info', 'verb' => 'GET'],

lib/AppInfo/Application.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ public function register(IRegistrationContext $context): void {
100100
$context->registerTaskProcessingProvider(AudioToTextProvider::class);
101101
}
102102

103+
$serviceUrl = $this->appConfig->getValueString(Application::APP_ID, 'url');
104+
$isUsingOpenAI = $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL;
105+
103106
if ($this->appConfig->getValueString(Application::APP_ID, 'llm_provider_enabled', '1') === '1') {
104107
$context->registerTaskProcessingProvider(TextToTextProvider::class);
105108
$context->registerTaskProcessingProvider(TextToTextChatProvider::class);
@@ -119,10 +122,12 @@ public function register(IRegistrationContext $context): void {
119122
if (class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToTextProofread')) {
120123
$context->registerTaskProcessingProvider(\OCA\OpenAi\TaskProcessing\ProofreadProvider::class);
121124
}
122-
if (!class_exists('OCP\\TaskProcessing\\TaskTypes\\AnalyzeImages')) {
123-
$context->registerTaskProcessingTaskType(\OCA\OpenAi\TaskProcessing\AnalyzeImagesTaskType::class);
125+
if ($isUsingOpenAI || $this->appConfig->getValueString(Application::APP_ID, 'analyze_image_provider_enabled') === '1') {
126+
if (!class_exists('OCP\\TaskProcessing\\TaskTypes\\AnalyzeImages')) {
127+
$context->registerTaskProcessingTaskType(\OCA\OpenAi\TaskProcessing\AnalyzeImagesTaskType::class);
128+
}
129+
$context->registerTaskProcessingProvider(\OCA\OpenAi\TaskProcessing\AnalyzeImagesProvider::class);
124130
}
125-
$context->registerTaskProcessingProvider(\OCA\OpenAi\TaskProcessing\AnalyzeImagesProvider::class);
126131
}
127132
if (!class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToSpeech')) {
128133
$context->registerTaskProcessingTaskType(\OCA\OpenAi\TaskProcessing\TextToSpeechTaskType::class);
@@ -133,8 +138,6 @@ public function register(IRegistrationContext $context): void {
133138
}
134139

135140
// only register audio chat stuff if we're using OpenAI or stt+llm+tts are enabled
136-
$serviceUrl = $this->appConfig->getValueString(Application::APP_ID, 'url');
137-
$isUsingOpenAI = $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL;
138141
if (
139142
$isUsingOpenAI
140143
|| (

lib/Controller/ConfigController.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace OCA\OpenAi\Controller;
99

1010
use Exception;
11+
use OCA\OpenAi\Service\OpenAiAPIService;
1112
use OCA\OpenAi\Service\OpenAiSettingsService;
1213
use OCP\AppFramework\Controller;
1314
use OCP\AppFramework\Http;
@@ -22,6 +23,7 @@ public function __construct(
2223
string $appName,
2324
IRequest $request,
2425
private OpenAiSettingsService $openAiSettingsService,
26+
private OpenAiAPIService $openAiAPIService,
2527
private ?string $userId,
2628
) {
2729
parent::__construct($appName, $request);
@@ -98,4 +100,17 @@ public function setSensitiveAdminConfig(array $values): DataResponse {
98100

99101
return new DataResponse('');
100102
}
103+
104+
/**
105+
* Set admin config values
106+
* @return DataResponse
107+
*/
108+
public function autoDetectFeatures(): DataResponse {
109+
try {
110+
$config = $this->openAiAPIService->autoDetectFeatures();
111+
return new DataResponse($config);
112+
} catch (Exception $e) {
113+
return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
114+
}
115+
}
101116
}

lib/Service/OpenAiAPIService.php

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,11 @@ public function updateExpImgProcessingTime(int $runtime): void {
891891
* @param array $params Query parameters (key/val pairs)
892892
* @param string $method HTTP query method
893893
* @param string|null $contentType
894+
* @param bool $logErrors if set to false error logs will be suppressed
894895
* @return array decoded request result or error
895896
* @throws Exception
896897
*/
897-
public function request(?string $userId, string $endPoint, array $params = [], string $method = 'GET', ?string $contentType = null): array {
898+
public function request(?string $userId, string $endPoint, array $params = [], string $method = 'GET', ?string $contentType = null, bool $logErrors = true): array {
898899
try {
899900
$serviceUrl = $this->openAiSettingsService->getServiceUrl();
900901
if ($serviceUrl === '') {
@@ -1000,10 +1001,12 @@ public function request(?string $userId, string $endPoint, array $params = [], s
10001001
} catch (ClientException|ServerException $e) {
10011002
$responseBody = $e->getResponse()->getBody();
10021003
$parsedResponseBody = json_decode($responseBody, true);
1003-
if ($e->getResponse()->getStatusCode() === 404) {
1004-
$this->logger->debug('API request error : ' . $e->getMessage(), ['response_body' => $responseBody, 'exception' => $e]);
1005-
} else {
1006-
$this->logger->warning('API request error : ' . $e->getMessage(), ['response_body' => $responseBody, 'exception' => $e]);
1004+
if ($logErrors) {
1005+
if ($e->getResponse()->getStatusCode() === 404) {
1006+
$this->logger->debug('API request error : ' . $e->getMessage(), ['response_body' => $responseBody, 'exception' => $e]);
1007+
} else {
1008+
$this->logger->warning('API request error : ' . $e->getMessage(), ['response_body' => $responseBody, 'exception' => $e]);
1009+
}
10071010
}
10081011
throw new Exception(
10091012
$this->l10n->t('API request error: ') . (
@@ -1019,4 +1022,86 @@ public function request(?string $userId, string $endPoint, array $params = [], s
10191022
);
10201023
}
10211024
}
1025+
1026+
/**
1027+
* Check if the T2I provider is available
1028+
*
1029+
* @return bool whether the T2I provider is available
1030+
*/
1031+
public function isT2IAvailable(): bool {
1032+
if ($this->isUsingOpenAi()) {
1033+
return true;
1034+
}
1035+
try {
1036+
$params = [
1037+
'prompt' => 'a',
1038+
'model' => 'invalid-model',
1039+
];
1040+
$this->request(null, 'images/generations', $params, 'POST', logErrors: false);
1041+
} catch (Exception $e) {
1042+
return $e->getCode() !== Http::STATUS_NOT_FOUND && $e->getCode() !== Http::STATUS_UNAUTHORIZED;
1043+
}
1044+
return true;
1045+
}
1046+
1047+
/**
1048+
* Check if the STT provider is available
1049+
*
1050+
* @return bool whether the STT provider is available
1051+
*/
1052+
public function isSTTAvailable(): bool {
1053+
if ($this->isUsingOpenAi()) {
1054+
return true;
1055+
}
1056+
try {
1057+
$params = [
1058+
'model' => 'invalid-model',
1059+
'file' => 'a',
1060+
];
1061+
$this->request(null, 'audio/translations', $params, 'POST', 'multipart/form-data', logErrors: false);
1062+
} catch (Exception $e) {
1063+
return $e->getCode() !== Http::STATUS_NOT_FOUND && $e->getCode() !== Http::STATUS_UNAUTHORIZED;
1064+
}
1065+
return true;
1066+
}
1067+
1068+
/**
1069+
* Check if the TTS provider is available
1070+
*
1071+
* @return bool whether the TTS provider is available
1072+
*/
1073+
public function isTTSAvailable(): bool {
1074+
if ($this->isUsingOpenAi()) {
1075+
return true;
1076+
}
1077+
try {
1078+
$params = [
1079+
'input' => 'a',
1080+
'voice' => 'invalid-voice',
1081+
'model' => 'invalid-model',
1082+
'response_format' => 'mp3',
1083+
];
1084+
1085+
$this->request(null, 'audio/speech', $params, 'POST', logErrors: false);
1086+
} catch (Exception $e) {
1087+
return $e->getCode() !== Http::STATUS_NOT_FOUND && $e->getCode() !== Http::STATUS_UNAUTHORIZED;
1088+
}
1089+
return true;
1090+
}
1091+
1092+
/**
1093+
* Updates the admin config with the availability of the providers
1094+
*
1095+
* @return array the updated config
1096+
* @throws Exception
1097+
*/
1098+
public function autoDetectFeatures(): array {
1099+
$config = [];
1100+
$config['t2i_provider_enabled'] = $this->isT2IAvailable();
1101+
$config['stt_provider_enabled'] = $this->isSTTAvailable();
1102+
$config['tts_provider_enabled'] = $this->isTTSAvailable();
1103+
$this->openAiSettingsService->setAdminConfig($config);
1104+
$config['analyze_image_provider_enabled'] = $this->openAiSettingsService->getAnalyzeImageProviderEnabled();
1105+
return $config;
1106+
}
10221107
}

lib/Service/OpenAiSettingsService.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class OpenAiSettingsService {
4040
't2i_provider_enabled' => 'boolean',
4141
'stt_provider_enabled' => 'boolean',
4242
'tts_provider_enabled' => 'boolean',
43+
'analyze_image_provider_enabled' => 'boolean',
4344
'chat_endpoint_enabled' => 'boolean',
4445
'basic_user' => 'string',
4546
'basic_password' => 'string',
@@ -321,6 +322,7 @@ public function getAdminConfig(): array {
321322
't2i_provider_enabled' => $this->getT2iProviderEnabled(),
322323
'stt_provider_enabled' => $this->getSttProviderEnabled(),
323324
'tts_provider_enabled' => $this->getTtsProviderEnabled(),
325+
'analyze_image_provider_enabled' => $this->getAnalyzeImageProviderEnabled(),
324326
'chat_endpoint_enabled' => $this->getChatEndpointEnabled(),
325327
'basic_user' => $this->getAdminBasicUser(),
326328
'basic_password' => $this->getAdminBasicPassword(),
@@ -400,6 +402,19 @@ public function getTtsProviderEnabled(): bool {
400402
return $this->appConfig->getValueString(Application::APP_ID, 'tts_provider_enabled', '1') === '1';
401403
}
402404

405+
/**
406+
* @return bool
407+
*/
408+
public function getAnalyzeImageProviderEnabled(): bool {
409+
$config = $this->appConfig->getValueString(Application::APP_ID, 'analyze_image_provider_enabled');
410+
if ($config === '') {
411+
$serviceUrl = $this->getServiceUrl();
412+
$isUsingOpenAI = $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL;
413+
return $isUsingOpenAI;
414+
}
415+
return $config === '1';
416+
}
417+
403418
////////////////////////////////////////////
404419
//////////// Setters for settings //////////
405420

@@ -731,6 +746,9 @@ public function setAdminConfig(array $adminConfig): void {
731746
if (isset($adminConfig['tts_provider_enabled'])) {
732747
$this->setTtsProviderEnabled($adminConfig['tts_provider_enabled']);
733748
}
749+
if (isset($adminConfig['analyze_image_provider_enabled'])) {
750+
$this->setAnalyzeImageProviderEnabled($adminConfig['analyze_image_provider_enabled']);
751+
}
734752
if (isset($adminConfig['default_tts_voice'])) {
735753
$this->setAdminDefaultTtsVoice($adminConfig['default_tts_voice']);
736754
}
@@ -833,6 +851,14 @@ public function setTtsProviderEnabled(bool $enabled): void {
833851
$this->appConfig->setValueString(Application::APP_ID, 'tts_provider_enabled', $enabled ? '1' : '0');
834852
}
835853

854+
/**
855+
* @param bool $enabled
856+
* @return void
857+
*/
858+
public function setAnalyzeImageProviderEnabled(bool $enabled): void {
859+
$this->appConfig->setValueString(Application::APP_ID, 'analyze_image_provider_enabled', $enabled ? '1' : '0');
860+
}
861+
836862
/**
837863
* @param bool $enabled
838864
*/

src/components/AdminSettings.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,11 @@
586586
@update:model-value="onCheckboxChanged($event, 'tts_provider_enabled', false)">
587587
{{ t('integration_openai', 'Text-to-speech provider') }}
588588
</NcCheckboxRadioSwitch>
589+
<NcCheckboxRadioSwitch
590+
:model-value="state.analyze_image_provider_enabled"
591+
@update:model-value="onCheckboxChanged($event, 'analyze_image_provider_enabled', false)">
592+
{{ t('integration_openai', 'Analyze image provider') }}
593+
</NcCheckboxRadioSwitch>
589594
</div>
590595
</div>
591596
</div>
@@ -700,6 +705,23 @@ export default {
700705
label: model.id + (model.owned_by ? ' (' + model.owned_by + ')' : ''),
701706
}
702707
},
708+
autoDetectFeatures() {
709+
return axios.post(generateUrl('/apps/integration_openai/admin-config/auto-detect-features')).then((response) => {
710+
const data = response.data ?? {}
711+
console.debug(data)
712+
this.state = {
713+
...this.state,
714+
...data,
715+
}
716+
}).catch((error) => {
717+
showError(
718+
t('integration_openai', 'Failed to auto update config')
719+
+ ': ' + this.reduceStars(error.response?.data?.error),
720+
{ timeout: 10000 },
721+
)
722+
console.error(error)
723+
})
724+
},
703725
704726
getModels(shouldSave = true) {
705727
this.models = null
@@ -837,6 +859,7 @@ export default {
837859
if (getModels) {
838860
this.getModels()
839861
}
862+
this.autoDetectFeatures()
840863
}, 2000),
841864
onInput: debounce(async function() {
842865
// sanitize quotas

0 commit comments

Comments
 (0)