From 39bd8d3033468b561c3a4c2d608390bffd50c1a2 Mon Sep 17 00:00:00 2001 From: Lukas Schaefer Date: Fri, 7 Aug 2026 11:00:32 -0400 Subject: [PATCH] Fix mistral for pdf documents Signed-off-by: Lukas Schaefer --- lib/Capabilities.php | 6 +- .../Translation/TranslationProvider.php | 4 +- lib/Service/OpenAiAPIService.php | 73 +++++-------------- lib/Service/OpenAiFileService.php | 25 ++++--- lib/Service/OpenAiSettingsService.php | 59 ++++++++++++--- lib/Service/TranslateService.php | 2 +- lib/TaskProcessing/AnalyzeImagesProvider.php | 2 +- .../AudioToAudioChatProvider.php | 14 ++-- .../AudioToAudioTranslateProvider.php | 6 +- .../AudioToTextSubtitlesProvider.php | 4 +- lib/TaskProcessing/ChangeToneProvider.php | 2 +- lib/TaskProcessing/ContextWriteProvider.php | 2 +- lib/TaskProcessing/EmojiProvider.php | 2 +- lib/TaskProcessing/HeadlineProvider.php | 2 +- lib/TaskProcessing/ImageToTextOcrProvider.php | 2 +- lib/TaskProcessing/ProofreadProvider.php | 4 +- .../ReformatParagraphsProvider.php | 2 +- lib/TaskProcessing/ReformulateProvider.php | 2 +- lib/TaskProcessing/SummaryProvider.php | 2 +- lib/TaskProcessing/TextToImageProvider.php | 4 +- lib/TaskProcessing/TextToSpeechProvider.php | 6 +- .../TextToTextImproveProvider.php | 2 +- lib/TaskProcessing/TextToTextProvider.php | 2 +- lib/TaskProcessing/TopicsProvider.php | 2 +- tests/unit/Providers/OpenAiProviderTest.php | 2 + tests/unit/Service/ServiceOverrideTest.php | 2 + 26 files changed, 124 insertions(+), 111 deletions(-) diff --git a/lib/Capabilities.php b/lib/Capabilities.php index b9dcab54..ba2335db 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -10,19 +10,19 @@ namespace OCA\OpenAi; use OCA\OpenAi\AppInfo\Application; -use OCA\OpenAi\Service\OpenAiAPIService; +use OCA\OpenAi\Service\OpenAiSettingsService; use OCP\Capabilities\IPublicCapability; class Capabilities implements IPublicCapability { public function __construct( - private OpenAiAPIService $openAiAPIService, + private OpenAiSettingsService $openAiSettingsService, ) { } public function getCapabilities(): array { return [ Application::APP_ID => [ - 'uses_openai' => $this->openAiAPIService->isUsingOpenAi(), + 'uses_openai' => $this->openAiSettingsService->isUsingOpenAi(), ], ]; } diff --git a/lib/OldProcessing/Translation/TranslationProvider.php b/lib/OldProcessing/Translation/TranslationProvider.php index b9d6da0e..395d84b9 100644 --- a/lib/OldProcessing/Translation/TranslationProvider.php +++ b/lib/OldProcessing/Translation/TranslationProvider.php @@ -69,7 +69,7 @@ public function detectLanguage(string $text): ?string { $prompt = 'What language is this (answer with the language name only, in English): ' . $text; $adminModel = $this->openAiSettingsService->getAdminDefaultCompletionModelId(); try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($this->userId, $adminModel, $prompt, null, null, 1, 100); $completion = $completion['messages']; } else { @@ -107,7 +107,7 @@ public function translate(?string $fromLanguage, string $toLanguage, string $tex } $adminModel = $this->openAiSettingsService->getAdminDefaultCompletionModelId(); - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($this->userId, $adminModel, $prompt, null, null, 1, PHP_INT_MAX); $completion = $completion['messages']; } else { diff --git a/lib/Service/OpenAiAPIService.php b/lib/Service/OpenAiAPIService.php index ac853808..86a4ab6b 100644 --- a/lib/Service/OpenAiAPIService.php +++ b/lib/Service/OpenAiAPIService.php @@ -74,52 +74,13 @@ public function createQuotaUsage(string $userId, int $type, int $usage) { $this->quotaUsageMapper->createQuotaUsage($userId, $type, $usage, $rule['pool'] ? $rule['id'] : -1); } - /** - * @param ?string $serviceType - * @return bool - */ - public function isUsingOpenAi(?string $serviceType = null): bool { - $serviceUrl = ''; - if ($serviceType === Application::SERVICE_TYPE_IMAGE) { - $serviceUrl = $this->openAiSettingsService->getImageServiceUrl(); - } elseif ($serviceType === Application::SERVICE_TYPE_STT) { - $serviceUrl = $this->openAiSettingsService->getSttServiceUrl(); - } elseif ($serviceType === Application::SERVICE_TYPE_TTS) { - $serviceUrl = $this->openAiSettingsService->getTtsServiceUrl(); - } - if ($serviceUrl === '') { - $serviceUrl = $this->openAiSettingsService->getServiceUrl(); - } - return $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL; - } - - /** - * @param ?string $serviceType - * @return bool - */ - public function isUsingOpenRouter(?string $serviceType = null): bool { - $serviceUrl = ''; - if ($serviceType === Application::SERVICE_TYPE_IMAGE) { - $serviceUrl = $this->openAiSettingsService->getImageServiceUrl(); - } elseif ($serviceType === Application::SERVICE_TYPE_STT) { - $serviceUrl = $this->openAiSettingsService->getSttServiceUrl(); - } elseif ($serviceType === Application::SERVICE_TYPE_TTS) { - $serviceUrl = $this->openAiSettingsService->getTtsServiceUrl(); - } - if ($serviceUrl === '') { - $serviceUrl = $this->openAiSettingsService->getServiceUrl(); - } - // Return true if the service URL references OpenRouter (e.g., openrouter.ai) - return str_starts_with(strtolower($serviceUrl), 'https://openrouter.ai'); - } - /** * @param ?string $serviceType * * @return string */ public function getServiceName(?string $serviceType = null): string { - if ($this->isUsingOpenAi($serviceType)) { + if ($this->openAiSettingsService->isUsingOpenAi($serviceType)) { if ($serviceType === Application::SERVICE_TYPE_IMAGE) { return $this->l10n->t('OpenAI\'s Image Generation'); } @@ -243,7 +204,7 @@ public function getModels(?string $userId, bool $refresh = false, ?string $servi try { $this->logger->debug('Actually getting OpenAI models with a network request'); - $params = $this->isUsingOpenRouter($serviceType) ? ['output_modalities' => 'all'] : []; + $params = $this->openAiSettingsService->isUsingOpenRouter($serviceType) ? ['output_modalities' => 'all'] : []; $modelsResponse = $this->request($userId, 'models', $params, serviceType: $serviceType); } catch (Exception $e) { $this->logger->warning('Error retrieving models (exc): ' . $e->getMessage()); @@ -275,7 +236,7 @@ public function getModels(?string $userId, bool $refresh = false, ?string $servi * @param string $userId */ private function hasOwnOpenAiApiKey(string $userId): bool { - if (!$this->isUsingOpenAi()) { + if (!$this->openAiSettingsService->isUsingOpenAi()) { return false; } @@ -296,7 +257,7 @@ public function getModelEnumValues(?string $userId, ?string $serviceType = null) $modelEnumValues = array_map(function (array $model) { return new ShapeEnumValue($model['id'], $model['id']); }, $modelResponse['data'] ?? []); - if ($this->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { array_unshift($modelEnumValues, new ShapeEnumValue($this->l10n->t('Default'), 'Default')); } return $modelEnumValues; @@ -722,7 +683,7 @@ private function buildChatCompletionRequestParams( $messages[] = [ // o1-* models don't support system messages // system prompts as a user message seems to work fine though - 'role' => ($this->isUsingOpenAi() && str_starts_with($modelRequestParam, 'o1-')) + 'role' => ($this->openAiSettingsService->isUsingOpenAi() && str_starts_with($modelRequestParam, 'o1-')) ? 'user' : 'system', 'content' => $systemPrompt, @@ -845,7 +806,7 @@ private function buildChatCompletionRequestParams( if ($tools !== null) { $params['tools'] = $tools; } - if ($userId !== null && $this->isUsingOpenAi()) { + if ($userId !== null && $this->openAiSettingsService->isUsingOpenAi()) { $params['user'] = $userId; } @@ -856,7 +817,7 @@ private function buildChatCompletionRequestParams( if ($extraParams !== null) { $params = array_merge($extraParams, $params); } - if ($stream && $this->isUsingOpenAi()) { + if ($stream && $this->openAiSettingsService->isUsingOpenAi()) { $params['stream_options'] = array_merge( is_array($params['stream_options'] ?? null) ? $params['stream_options'] : [], ['include_usage' => true], @@ -953,7 +914,7 @@ public function transcribe( throw new Exception($this->l10n->t('Audio transcription quota exceeded'), Http::STATUS_TOO_MANY_REQUESTS); } // enforce whisper for OpenAI - if ($this->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { $model = Application::DEFAULT_TRANSCRIPTION_MODEL_ID; } @@ -1158,7 +1119,7 @@ public function requestSpeechCreation( * @return int */ public function getExpTextProcessingTime(): int { - return $this->isUsingOpenAi() + return $this->openAiSettingsService->isUsingOpenAi() ? intval($this->appConfig->getValueString(Application::APP_ID, 'openai_text_generation_time', strval(Application::DEFAULT_OPENAI_TEXT_GENERATION_TIME), lazy: true)) : intval($this->appConfig->getValueString(Application::APP_ID, 'localai_text_generation_time', strval(Application::DEFAULT_LOCALAI_TEXT_GENERATION_TIME), lazy: true)); } @@ -1171,7 +1132,7 @@ public function updateExpTextProcessingTime(int $runtime): void { $oldTime = floatval($this->getExpTextProcessingTime()); $newTime = (1.0 - Application::EXPECTED_RUNTIME_LOWPASS_FACTOR) * $oldTime + Application::EXPECTED_RUNTIME_LOWPASS_FACTOR * floatval($runtime); - if ($this->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { $this->appConfig->setValueString(Application::APP_ID, 'openai_text_generation_time', strval(intval($newTime)), lazy: true); } else { $this->appConfig->setValueString(Application::APP_ID, 'localai_text_generation_time', strval(intval($newTime)), lazy: true); @@ -1182,7 +1143,7 @@ public function updateExpTextProcessingTime(int $runtime): void { * @return int */ public function getExpImgProcessingTime(): int { - return $this->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE) + return $this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE) ? intval($this->appConfig->getValueString(Application::APP_ID, 'openai_image_generation_time', strval(Application::DEFAULT_OPENAI_IMAGE_GENERATION_TIME), lazy: true)) : intval($this->appConfig->getValueString(Application::APP_ID, 'localai_image_generation_time', strval(Application::DEFAULT_LOCALAI_IMAGE_GENERATION_TIME), lazy: true)); } @@ -1195,7 +1156,7 @@ public function updateExpImgProcessingTime(int $runtime): void { $oldTime = floatval($this->getExpImgProcessingTime()); $newTime = (1.0 - Application::EXPECTED_RUNTIME_LOWPASS_FACTOR) * $oldTime + Application::EXPECTED_RUNTIME_LOWPASS_FACTOR * floatval($runtime); - if ($this->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE)) { + if ($this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE)) { $this->appConfig->setValueString(Application::APP_ID, 'openai_image_generation_time', strval(intval($newTime)), lazy: true); } else { $this->appConfig->setValueString(Application::APP_ID, 'localai_image_generation_time', strval(intval($newTime)), lazy: true); @@ -1242,7 +1203,7 @@ public function request( return ['error' => 'An API key is required for api.openai.com']; } - if ($this->isUsingOpenAi($serviceType) || !$useBasicAuth) { + if ($this->openAiSettingsService->isUsingOpenAi($serviceType) || !$useBasicAuth) { if ($apiKey !== '') { $options['headers']['Authorization'] = 'Bearer ' . $apiKey; } @@ -1252,7 +1213,7 @@ public function request( } } - if (!$this->isUsingOpenAi($serviceType)) { + if (!$this->openAiSettingsService->isUsingOpenAi($serviceType)) { $options['nextcloud']['allow_local_address'] = true; } @@ -1555,7 +1516,7 @@ private function normalizeChatCompletionResponse(array $response): array { * @return bool whether the T2I provider is available */ public function isT2IAvailable(): bool { - if ($this->openAiSettingsService->imageOverrideEnabled() || $this->isUsingOpenAi()) { + if ($this->openAiSettingsService->imageOverrideEnabled() || $this->openAiSettingsService->isUsingOpenAi()) { return true; } try { @@ -1576,7 +1537,7 @@ public function isT2IAvailable(): bool { * @return bool whether the STT provider is available */ public function isSTTAvailable(): bool { - if ($this->openAiSettingsService->sttOverrideEnabled() || $this->isUsingOpenAi()) { + if ($this->openAiSettingsService->sttOverrideEnabled() || $this->openAiSettingsService->isUsingOpenAi()) { return true; } try { @@ -1597,7 +1558,7 @@ public function isSTTAvailable(): bool { * @return bool whether the TTS provider is available */ public function isTTSAvailable(): bool { - if ($this->openAiSettingsService->ttsOverrideEnabled() || $this->isUsingOpenAi()) { + if ($this->openAiSettingsService->ttsOverrideEnabled() || $this->openAiSettingsService->isUsingOpenAi()) { return true; } try { diff --git a/lib/Service/OpenAiFileService.php b/lib/Service/OpenAiFileService.php index d1977bca..a289488c 100644 --- a/lib/Service/OpenAiFileService.php +++ b/lib/Service/OpenAiFileService.php @@ -9,7 +9,6 @@ namespace OCA\OpenAi\Service; -use OCA\OpenAi\AppInfo\Application; use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\IL10N; @@ -119,7 +118,7 @@ public function buildFileContentFromFile(?File $file): array { throw new ProcessingException('File is not readable'); } // Maximum file size for openai is 50MB. - if ($this->isUsingOpenAi() && $file->getSize() > self::MAX_FILE_SIZE_BYTES) { + if ($this->openAiSettingsService->isUsingOpenAi() && $file->getSize() > self::MAX_FILE_SIZE_BYTES) { throw new UserFacingProcessingException( 'Filesize of input files too large. Max is 50MB', 0, @@ -160,7 +159,7 @@ private function buildImageContent(File $file, string $fileType): array { $this->l10n->t('Image attachments are unsupported.'), ); } - if ($this->isUsingOpenAi() && !in_array($fileType, self::VALID_IMAGE_MIME_TYPES, true)) { + if ($this->openAiSettingsService->isUsingOpenAi() && !in_array($fileType, self::VALID_IMAGE_MIME_TYPES, true)) { throw new UserFacingProcessingException( 'Invalid input file type for OpenAI ' . $fileType, 0, @@ -228,7 +227,7 @@ private function buildVideoContent(File $file, string $fileType): array { } /** - * @return list + * @return list> */ private function buildDocumentContent(File $file, string $fileType): array { if (!$this->openAiSettingsService->getMultimodalDocumentEnabled()) { @@ -239,11 +238,22 @@ private function buildDocumentContent(File $file, string $fileType): array { $this->l10n->t('Document attachments are unsupported.'), ); } + $dataUri = 'data:' . $fileType . ';base64,' . base64_encode(stream_get_contents($file->fopen('rb'))); + + if ($this->openAiSettingsService->isUsingMistral()) { + // Mistral does not accept the default openai shape so we use a fallback for them + return [[ + 'type' => 'document_url', + 'document_url' => $dataUri, + 'document_name' => $file->getName(), + ]]; + } + return [[ 'type' => 'file', 'file' => [ 'filename' => $file->getName(), - 'file_data' => 'data:' . $fileType . ';base64,' . base64_encode(stream_get_contents($file->fopen('rb'))), + 'file_data' => $dataUri, ], ]]; } @@ -266,9 +276,4 @@ private function buildTextContent(File $file, string $fileType): array { 'text' => 'Filename:' . $file->getName() . "\nContent:\n" . stream_get_contents($file->fopen('rb')), ]]; } - - private function isUsingOpenAi(): bool { - $serviceUrl = $this->openAiSettingsService->getServiceUrl(); - return $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL; - } } diff --git a/lib/Service/OpenAiSettingsService.php b/lib/Service/OpenAiSettingsService.php index f63705c1..7533f6fc 100644 --- a/lib/Service/OpenAiSettingsService.php +++ b/lib/Service/OpenAiSettingsService.php @@ -263,6 +263,51 @@ public function getServiceUrl(): string { return $this->appConfig->getValueString(Application::APP_ID, 'url'); } + /** + * Resolve the effective service URL for a service type, falling back to the main URL. + * + * @param ?string $serviceType + */ + private function resolveServiceUrl(?string $serviceType = null): string { + $serviceUrl = ''; + if ($serviceType === Application::SERVICE_TYPE_IMAGE) { + $serviceUrl = $this->getImageServiceUrl(); + } elseif ($serviceType === Application::SERVICE_TYPE_STT) { + $serviceUrl = $this->getSttServiceUrl(); + } elseif ($serviceType === Application::SERVICE_TYPE_TTS) { + $serviceUrl = $this->getTtsServiceUrl(); + } + if ($serviceUrl === '') { + $serviceUrl = $this->getServiceUrl(); + } + return $serviceUrl; + } + + /** + * @param ?string $serviceType + */ + public function isUsingOpenAi(?string $serviceType = null): bool { + $serviceUrl = $this->resolveServiceUrl($serviceType); + return $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL; + } + + /** + * @param ?string $serviceType + */ + public function isUsingOpenRouter(?string $serviceType = null): bool { + $serviceUrl = $this->resolveServiceUrl($serviceType); + // Return true if the service URL references OpenRouter (e.g., openrouter.ai) + return str_starts_with(strtolower($serviceUrl), 'https://openrouter.ai'); + } + + /** + * @param ?string $serviceType + */ + public function isUsingMistral(?string $serviceType = null): bool { + $serviceUrl = $this->resolveServiceUrl($serviceType); + return str_starts_with(strtolower($serviceUrl), 'https://api.mistral.ai'); + } + /** * @return string */ @@ -630,7 +675,7 @@ public function getAdminConfig(): array { * @return array{api_key: string, basic_password: string, basic_user: string, is_custom_service: bool, use_basic_auth: bool, stt_language: string} */ public function getUserConfig(string $userId): array { - $isCustomService = $this->getServiceUrl() !== '' && $this->getServiceUrl() !== Application::OPENAI_API_BASE_URL; + $isCustomService = !$this->isUsingOpenAi(); return [ 'api_key' => $this->getUserApiKey($userId), 'basic_user' => $this->getUserBasicUser($userId, false), @@ -645,10 +690,8 @@ public function getUserConfig(string $userId): array { * @return bool */ public function getUseMaxCompletionTokensParam(): bool { - $serviceUrl = $this->getServiceUrl(); - $isUsingOpenAI = $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL; // we know OpenAI expects "use_max_completion_tokens_param", let's assume the other services don't - $default = $isUsingOpenAI ? '1' : '0'; + $default = $this->isUsingOpenAi() ? '1' : '0'; return $this->appConfig->getValueString(Application::APP_ID, 'use_max_completion_tokens_param', $default, lazy: true) === '1'; } @@ -663,14 +706,8 @@ public function getTranslationProviderEnabled(): bool { * @return bool */ public function getIsImageRetrievalAuthenticated(): bool { - $serviceUrl = $this->getServiceUrl(); // the image_request_auth default depends on the service used for image generation - // if we override it, we check the one we are really gonna use - if ($this->imageOverrideEnabled()) { - $serviceUrl = $this->getImageServiceUrl(); - } - $isUsingOpenAI = $serviceUrl === '' || $serviceUrl === Application::OPENAI_API_BASE_URL; - $default = $isUsingOpenAI ? '0' : '1'; + $default = $this->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE) ? '0' : '1'; return $this->appConfig->getValueString(Application::APP_ID, 'image_request_auth', $default, lazy: true) === '1'; } diff --git a/lib/Service/TranslateService.php b/lib/Service/TranslateService.php index ecdb2d6a..77e783c0 100644 --- a/lib/Service/TranslateService.php +++ b/lib/Service/TranslateService.php @@ -104,7 +104,7 @@ public function translate( } $prompt = $promptStart . PHP_EOL . PHP_EOL . $chunk; - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completionsObj = $this->openAiAPIService->createChatCompletion( $userId, $model, $prompt, TranslateService::SYSTEM_PROMPT, null, 1, $maxTokens, TranslateService::JSON_RESPONSE_FORMAT ); diff --git a/lib/TaskProcessing/AnalyzeImagesProvider.php b/lib/TaskProcessing/AnalyzeImagesProvider.php index 1ba992e3..e6490577 100644 --- a/lib/TaskProcessing/AnalyzeImagesProvider.php +++ b/lib/TaskProcessing/AnalyzeImagesProvider.php @@ -113,7 +113,7 @@ public function process( $reportOutput = $options->getReportIntermediateOutput(); $preferStreaming = $options->getPreferStreaming(); - if (!$this->openAiAPIService->isUsingOpenAi() && !$this->openAiSettingsService->getChatEndpointEnabled()) { + if (!$this->openAiSettingsService->isUsingOpenAi() && !$this->openAiSettingsService->getChatEndpointEnabled()) { throw new ProcessingException('Must support chat completion endpoint'); } diff --git a/lib/TaskProcessing/AudioToAudioChatProvider.php b/lib/TaskProcessing/AudioToAudioChatProvider.php index c7f36194..663dcd92 100644 --- a/lib/TaskProcessing/AudioToAudioChatProvider.php +++ b/lib/TaskProcessing/AudioToAudioChatProvider.php @@ -70,7 +70,7 @@ public function getInputShapeDefaults(): array { } public function getOptionalInputShape(): array { - $isUsingOpenAi = $this->openAiAPIService->isUsingOpenAi(); + $isUsingOpenAi = $this->openAiSettingsService->isUsingOpenAi(); $ois = [ 'llm_model' => new ShapeDescriptor( $this->l->t('Completion model'), @@ -96,7 +96,7 @@ public function getOptionalInputShape(): array { ); $ois['speed'] = new ShapeDescriptor( $this->l->t('Speed'), - $this->openAiAPIService->isUsingOpenAi() + $this->openAiSettingsService->isUsingOpenAi() ? $this->l->t('Speech speed modifier (Valid values: 0.25-4)') : $this->l->t('Speech speed modifier'), EShapeType::Number @@ -106,7 +106,7 @@ public function getOptionalInputShape(): array { } public function getOptionalInputShapeEnumValues(): array { - $isUsingOpenAi = $this->openAiAPIService->isUsingOpenAi(); + $isUsingOpenAi = $this->openAiSettingsService->isUsingOpenAi(); $voices = json_decode($this->appConfig->getValueString(Application::APP_ID, 'tts_voices', lazy: true)) ?: Application::DEFAULT_SPEECH_VOICES; $models = $this->openAiAPIService->getModelEnumValues($this->userId); $enumValues = [ @@ -122,7 +122,7 @@ public function getOptionalInputShapeEnumValues(): array { } public function getOptionalInputShapeDefaults(): array { - $isUsingOpenAi = $this->openAiAPIService->isUsingOpenAi(); + $isUsingOpenAi = $this->openAiSettingsService->isUsingOpenAi(); $adminVoice = $this->appConfig->getValueString(Application::APP_ID, 'default_speech_voice', lazy: true) ?: Application::DEFAULT_SPEECH_VOICE; $adminLlmModel = $isUsingOpenAi ? 'gpt-audio' @@ -192,7 +192,7 @@ public function process(?string $userId, array $input, callable $reportProgress) if (isset($input['llm_model']) && is_string($input['llm_model'])) { $llmModel = $input['llm_model']; } else { - $isUsingOpenAi = $this->openAiAPIService->isUsingOpenAi(); + $isUsingOpenAi = $this->openAiSettingsService->isUsingOpenAi(); $llmModel = $isUsingOpenAi ? 'gpt-4o-audio-preview' : $this->openAiSettingsService->getAdminDefaultCompletionModelId(); @@ -207,7 +207,7 @@ public function process(?string $userId, array $input, callable $reportProgress) $speed = 1; if (isset($input['speed']) && is_numeric($input['speed'])) { $speed = $input['speed']; - if ($this->openAiAPIService->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { if ($speed > 4) { $speed = 4; } elseif ($speed < 0.25) { @@ -221,7 +221,7 @@ public function process(?string $userId, array $input, callable $reportProgress) // Using the chat API if connected to OpenAI // there is an issue if the history mostly contains text, the model will answer text even if we add the audio modality - if ($this->openAiAPIService->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { return $this->oneStep($userId, $systemPrompt, $inputFile, $history, $outputVoice, $sttModel, $llmModel, $ttsModel, $speed, $serviceName); } diff --git a/lib/TaskProcessing/AudioToAudioTranslateProvider.php b/lib/TaskProcessing/AudioToAudioTranslateProvider.php index 0c5236cf..8ad94542 100644 --- a/lib/TaskProcessing/AudioToAudioTranslateProvider.php +++ b/lib/TaskProcessing/AudioToAudioTranslateProvider.php @@ -95,7 +95,7 @@ public function getOptionalInputShape(): array { ), 'tts_speed' => new ShapeDescriptor( $this->l->t('Speed'), - $this->openAiAPIService->isUsingOpenAi(Application::SERVICE_TYPE_TTS) + $this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_TTS) ? $this->l->t('Speech speed modifier (Valid values: 0.25-4)') : $this->l->t('Speech speed modifier'), EShapeType::Number @@ -206,7 +206,7 @@ public function process( } // translate - $completionModel = $this->openAiAPIService->isUsingOpenAi() + $completionModel = $this->openAiSettingsService->isUsingOpenAi() ? ($this->appConfig->getValueString(Application::APP_ID, 'default_completion_model_id', Application::DEFAULT_MODEL_ID, lazy: true) ?: Application::DEFAULT_MODEL_ID) : $this->appConfig->getValueString(Application::APP_ID, 'default_completion_model_id', lazy: true); $maxTokens = $this->openAiSettingsService->getMaxTokens(); @@ -268,7 +268,7 @@ public function process( $speed = 1; if (isset($input['tts_speed']) && is_numeric($input['tts_speed'])) { $speed = $input['tts_speed']; - if ($this->openAiAPIService->isUsingOpenAi(Application::SERVICE_TYPE_TTS)) { + if ($this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_TTS)) { if ($speed > 4) { $speed = 4; } elseif ($speed < 0.25) { diff --git a/lib/TaskProcessing/AudioToTextSubtitlesProvider.php b/lib/TaskProcessing/AudioToTextSubtitlesProvider.php index 5cd3be49..d10e7520 100644 --- a/lib/TaskProcessing/AudioToTextSubtitlesProvider.php +++ b/lib/TaskProcessing/AudioToTextSubtitlesProvider.php @@ -11,6 +11,7 @@ use OCA\OpenAi\AppInfo\Application; use OCA\OpenAi\Service\OpenAiAPIService; +use OCA\OpenAi\Service\OpenAiSettingsService; use OCP\Files\File; use OCP\IAppConfig; use OCP\IL10N; @@ -27,6 +28,7 @@ class AudioToTextSubtitlesProvider implements ISynchronousProvider { public function __construct( private OpenAiAPIService $openAiAPIService, + private OpenAiSettingsService $openAiSettingsService, private LoggerInterface $logger, private IAppConfig $appConfig, private IL10N $l, @@ -131,7 +133,7 @@ public function process(?string $userId, array $input, callable $reportProgress) $this->l->t('The input file type is invalid. Only audio or video files are allowed.'), ); } - if ($this->openAiAPIService->isUsingOpenAi()) { + if ($this->openAiSettingsService->isUsingOpenAi()) { $validFileTypes = [ 'audio/mp3', 'audio/mp4', diff --git a/lib/TaskProcessing/ChangeToneProvider.php b/lib/TaskProcessing/ChangeToneProvider.php index a0320fff..fc05e27b 100644 --- a/lib/TaskProcessing/ChangeToneProvider.php +++ b/lib/TaskProcessing/ChangeToneProvider.php @@ -156,7 +156,7 @@ public function process( foreach ($chunks as $textInput) { $prompt = "Reformulate the following text in a $toneInput tone in its original language. Output only the reformulation. Here is the text:" . "\n\n" . $textInput . "\n\n" . 'Do not mention the used language in your reformulation. Here is your reformulation in the same language:'; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { if ($preferStreaming) { $chunks = $this->openAiAPIService->createStreamedChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $time = microtime(true); diff --git a/lib/TaskProcessing/ContextWriteProvider.php b/lib/TaskProcessing/ContextWriteProvider.php index 0df2982e..80ac7c66 100644 --- a/lib/TaskProcessing/ContextWriteProvider.php +++ b/lib/TaskProcessing/ContextWriteProvider.php @@ -152,7 +152,7 @@ public function process( . ' Also, use the *WRITING STYLE* as a guide for how to write the text ONLY and not as a source of facts or events.' . ' Detect the language used in the *SOURCE_MATERIAL*. Make sure to use the same language in your response. Do not mention the language explicitly.'; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { if ($preferStreaming) { $chunks = $this->openAiAPIService->createStreamedChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $time = microtime(true); diff --git a/lib/TaskProcessing/EmojiProvider.php b/lib/TaskProcessing/EmojiProvider.php index 7a517c73..47ac8c8a 100644 --- a/lib/TaskProcessing/EmojiProvider.php +++ b/lib/TaskProcessing/EmojiProvider.php @@ -116,7 +116,7 @@ public function process(?string $userId, array $input, callable $reportProgress) } try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $completion = $completion['messages']; } else { diff --git a/lib/TaskProcessing/HeadlineProvider.php b/lib/TaskProcessing/HeadlineProvider.php index 54f630ae..e5adcfbf 100644 --- a/lib/TaskProcessing/HeadlineProvider.php +++ b/lib/TaskProcessing/HeadlineProvider.php @@ -116,7 +116,7 @@ public function process(?string $userId, array $input, callable $reportProgress) } try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $completion = $completion['messages']; } else { diff --git a/lib/TaskProcessing/ImageToTextOcrProvider.php b/lib/TaskProcessing/ImageToTextOcrProvider.php index d7113640..542fa8da 100644 --- a/lib/TaskProcessing/ImageToTextOcrProvider.php +++ b/lib/TaskProcessing/ImageToTextOcrProvider.php @@ -105,7 +105,7 @@ public function process( $reportOutput = $options->getReportIntermediateOutput(); $preferStreaming = $options->getPreferStreaming(); - if (!$this->openAiAPIService->isUsingOpenAi() && !$this->openAiSettingsService->getChatEndpointEnabled()) { + if (!$this->openAiSettingsService->isUsingOpenAi() && !$this->openAiSettingsService->getChatEndpointEnabled()) { throw new ProcessingException('Must support chat completion endpoint'); } diff --git a/lib/TaskProcessing/ProofreadProvider.php b/lib/TaskProcessing/ProofreadProvider.php index 6e02d061..bdf5ebaa 100644 --- a/lib/TaskProcessing/ProofreadProvider.php +++ b/lib/TaskProcessing/ProofreadProvider.php @@ -140,7 +140,7 @@ public function process(?string $userId, array $input, callable $reportProgress) foreach ($chunks as $textInput) { try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $textInput, $systemPrompt, null, 1, $maxTokens); $completion = $completion['messages']; } else { @@ -167,7 +167,7 @@ public function process(?string $userId, array $input, callable $reportProgress) if (count($chunks) > 1) { $systemPrompt = 'Repeat the proofread feedback list. Ensure that no information is lost, but also not duplicated. '; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $result, $systemPrompt, null, 1, $maxTokens); $completion = $completion['messages']; } else { diff --git a/lib/TaskProcessing/ReformatParagraphsProvider.php b/lib/TaskProcessing/ReformatParagraphsProvider.php index 8b5d02ae..0d7b8f2c 100644 --- a/lib/TaskProcessing/ReformatParagraphsProvider.php +++ b/lib/TaskProcessing/ReformatParagraphsProvider.php @@ -183,7 +183,7 @@ public function process(?string $userId, array $input, callable $reportProgress) Aside from transportation, the price TEXT; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $chunk, $systemPrompt, null, 1, $maxTokens); $completion = $completion['messages']; } else { diff --git a/lib/TaskProcessing/ReformulateProvider.php b/lib/TaskProcessing/ReformulateProvider.php index f1056ec0..42f961ea 100644 --- a/lib/TaskProcessing/ReformulateProvider.php +++ b/lib/TaskProcessing/ReformulateProvider.php @@ -138,7 +138,7 @@ public function process( foreach ($chunks as $chunk) { $prompt = 'Reformulate the following text. Use the same language as the original text. Output only the reformulation. Here is the text:' . "\n\n" . $chunk . "\n\n" . 'Do not mention the used language in your reformulation. Here is your reformulation in the same language:'; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { if ($preferStreaming) { $chunks = $this->openAiAPIService->createStreamedChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $time = microtime(true); diff --git a/lib/TaskProcessing/SummaryProvider.php b/lib/TaskProcessing/SummaryProvider.php index ab9dce24..68d50435 100644 --- a/lib/TaskProcessing/SummaryProvider.php +++ b/lib/TaskProcessing/SummaryProvider.php @@ -171,7 +171,7 @@ public function process(?string $userId, array $input, callable $reportProgress) $summarySystemPrompt .= 'Use simple language and vocabulary appropriate for a 5 year old. '; } } - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { foreach ($prompts as $p) { $completion = $this->openAiAPIService->createChatCompletion($userId, $model, $p, $summarySystemPrompt, null, 1, $maxTokens); diff --git a/lib/TaskProcessing/TextToImageProvider.php b/lib/TaskProcessing/TextToImageProvider.php index c7e52d28..7293231a 100644 --- a/lib/TaskProcessing/TextToImageProvider.php +++ b/lib/TaskProcessing/TextToImageProvider.php @@ -11,6 +11,7 @@ use OCA\OpenAi\AppInfo\Application; use OCA\OpenAi\Service\OpenAiAPIService; +use OCA\OpenAi\Service\OpenAiSettingsService; use OCA\OpenAi\Service\WatermarkingService; use OCP\Http\Client\IClientService; use OCP\IAppConfig; @@ -27,6 +28,7 @@ class TextToImageProvider implements ISynchronousWatermarkingProvider { public function __construct( private OpenAiAPIService $openAiAPIService, + private OpenAiSettingsService $openAiSettingsService, private IL10N $l, private LoggerInterface $logger, private IClientService $clientService, @@ -85,7 +87,7 @@ public function getOptionalInputShapeEnumValues(): array { } public function getOptionalInputShapeDefaults(): array { - $adminModel = $this->openAiAPIService->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE) + $adminModel = $this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_IMAGE) ? ($this->appConfig->getValueString(Application::APP_ID, 'default_image_model_id', Application::DEFAULT_MODEL_ID, lazy: true) ?: Application::DEFAULT_MODEL_ID) : $this->appConfig->getValueString(Application::APP_ID, 'default_image_model_id', lazy: true); return [ diff --git a/lib/TaskProcessing/TextToSpeechProvider.php b/lib/TaskProcessing/TextToSpeechProvider.php index 687fb4f7..c8875f91 100644 --- a/lib/TaskProcessing/TextToSpeechProvider.php +++ b/lib/TaskProcessing/TextToSpeechProvider.php @@ -11,6 +11,7 @@ use OCA\OpenAi\AppInfo\Application; use OCA\OpenAi\Service\OpenAiAPIService; +use OCA\OpenAi\Service\OpenAiSettingsService; use OCA\OpenAi\Service\WatermarkingService; use OCP\IAppConfig; use OCP\IL10N; @@ -26,6 +27,7 @@ class TextToSpeechProvider implements ISynchronousWatermarkingProvider { public function __construct( private OpenAiAPIService $openAiAPIService, + private OpenAiSettingsService $openAiSettingsService, private IL10N $l, private LoggerInterface $logger, private IAppConfig $appConfig, @@ -72,7 +74,7 @@ public function getOptionalInputShape(): array { ), 'speed' => new ShapeDescriptor( $this->l->t('Speed'), - $this->openAiAPIService->isUsingOpenAi(Application::SERVICE_TYPE_TTS) + $this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_TTS) ? $this->l->t('Speech speed modifier (Valid values: 0.25-4)') : $this->l->t('Speech speed modifier'), EShapeType::Number @@ -139,7 +141,7 @@ public function process(?string $userId, array $input, callable $reportProgress, $speed = 1; if (isset($input['speed']) && is_numeric($input['speed'])) { $speed = $input['speed']; - if ($this->openAiAPIService->isUsingOpenAi(Application::SERVICE_TYPE_TTS)) { + if ($this->openAiSettingsService->isUsingOpenAi(Application::SERVICE_TYPE_TTS)) { if ($speed > 4) { $speed = 4; } elseif ($speed < 0.25) { diff --git a/lib/TaskProcessing/TextToTextImproveProvider.php b/lib/TaskProcessing/TextToTextImproveProvider.php index 86c4e12c..6bfc662e 100644 --- a/lib/TaskProcessing/TextToTextImproveProvider.php +++ b/lib/TaskProcessing/TextToTextImproveProvider.php @@ -148,7 +148,7 @@ public function process( . "\n\n*INSTRUCTIONS*:\n$instructions\n\n*TEXT*:\n\n$chunk\n\n" . 'Do not mention the used language in your output. Here is your improved text in the same language:'; try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { if ($preferStreaming) { $chunks = $this->openAiAPIService->createStreamedChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $time = microtime(true); diff --git a/lib/TaskProcessing/TextToTextProvider.php b/lib/TaskProcessing/TextToTextProvider.php index c80d44cb..88aba2ad 100644 --- a/lib/TaskProcessing/TextToTextProvider.php +++ b/lib/TaskProcessing/TextToTextProvider.php @@ -127,7 +127,7 @@ public function process( } try { - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { if ($preferStreaming) { $chunks = $this->openAiAPIService->createStreamedChatCompletion($userId, $model, $prompt, null, null, 1, $maxTokens); $time = microtime(true); diff --git a/lib/TaskProcessing/TopicsProvider.php b/lib/TaskProcessing/TopicsProvider.php index 21074b3b..a1b1de7d 100644 --- a/lib/TaskProcessing/TopicsProvider.php +++ b/lib/TaskProcessing/TopicsProvider.php @@ -136,7 +136,7 @@ public function process(?string $userId, array $input, callable $reportProgress) try { $completions = []; - if ($this->openAiAPIService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { + if ($this->openAiSettingsService->isUsingOpenAi() || $this->openAiSettingsService->getChatEndpointEnabled()) { $topicsSystemPrompt = 'Extract topics from the following text. Detect the language of the text. Use the same language as the text. Output only the topics, comma separated.'; foreach ($prompts as $p) { diff --git a/tests/unit/Providers/OpenAiProviderTest.php b/tests/unit/Providers/OpenAiProviderTest.php index d739eb65..f74a61cf 100644 --- a/tests/unit/Providers/OpenAiProviderTest.php +++ b/tests/unit/Providers/OpenAiProviderTest.php @@ -838,6 +838,7 @@ public function testAudioToAudioTranslateProvider(): void { public function testTextToSpeechProvider(): void { $TTSProvider = new TextToSpeechProvider( $this->openAiApiService, + $this->openAiSettingsService, $l10n = $this->createMock(\OCP\IL10N::class), $this->createMock(\Psr\Log\LoggerInterface::class), \OCP\Server::get(IAppConfig::class), @@ -885,6 +886,7 @@ public function testTextToSpeechProvider(): void { public function testTextToImageProvider(): void { $TextToImageProvider = new TextToImageProvider( $this->openAiApiService, + $this->openAiSettingsService, $this->createMock(\OCP\IL10N::class), $this->createMock(\Psr\Log\LoggerInterface::class), \OCP\Server::get(IClientService::class), diff --git a/tests/unit/Service/ServiceOverrideTest.php b/tests/unit/Service/ServiceOverrideTest.php index a7d8cfa7..8da76d00 100644 --- a/tests/unit/Service/ServiceOverrideTest.php +++ b/tests/unit/Service/ServiceOverrideTest.php @@ -132,6 +132,7 @@ public function testTextToSpeechProvider(): void { $TTSProvider = new TextToSpeechProvider( $this->openAiApiService, + $this->openAiSettingsService, $l10n = $this->createMock(\OCP\IL10N::class), $this->createMock(\Psr\Log\LoggerInterface::class), \OCP\Server::get(IAppConfig::class), @@ -174,6 +175,7 @@ public function testTextToImageProvider(): void { $TextToImageProvider = new TextToImageProvider( $this->openAiApiService, + $this->openAiSettingsService, $this->createMock(\OCP\IL10N::class), $this->createMock(\Psr\Log\LoggerInterface::class), \OCP\Server::get(IClientService::class),