diff options
11 files changed, 602 insertions, 15 deletions
diff --git a/apps/testing/composer/composer/autoload_classmap.php b/apps/testing/composer/composer/autoload_classmap.php index 6dce2e26361..bcb68d25bbd 100644 --- a/apps/testing/composer/composer/autoload_classmap.php +++ b/apps/testing/composer/composer/autoload_classmap.php @@ -22,4 +22,9 @@ return array( 'OCA\\Testing\\Provider\\FakeTextProcessingProviderSync' => $baseDir . '/../lib/Provider/FakeTextProcessingProviderSync.php', 'OCA\\Testing\\Provider\\FakeTranslationProvider' => $baseDir . '/../lib/Provider/FakeTranslationProvider.php', 'OCA\\Testing\\Settings\\DeclarativeSettingsForm' => $baseDir . '/../lib/Settings/DeclarativeSettingsForm.php', + 'OCA\\Testing\\TaskProcessing\\FakeContextWriteProvider' => $baseDir . '/../lib/TaskProcessing/FakeContextWriteProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTextToImageProvider' => $baseDir . '/../lib/TaskProcessing/FakeTextToImageProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTextToTextProvider' => $baseDir . '/../lib/TaskProcessing/FakeTextToTextProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTranscribeProvider' => $baseDir . '/../lib/TaskProcessing/FakeTranscribeProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTranslateProvider' => $baseDir . '/../lib/TaskProcessing/FakeTranslateProvider.php', ); diff --git a/apps/testing/composer/composer/autoload_static.php b/apps/testing/composer/composer/autoload_static.php index 3be58e04289..5a165d7d0c2 100644 --- a/apps/testing/composer/composer/autoload_static.php +++ b/apps/testing/composer/composer/autoload_static.php @@ -37,6 +37,11 @@ class ComposerStaticInitTesting 'OCA\\Testing\\Provider\\FakeTextProcessingProviderSync' => __DIR__ . '/..' . '/../lib/Provider/FakeTextProcessingProviderSync.php', 'OCA\\Testing\\Provider\\FakeTranslationProvider' => __DIR__ . '/..' . '/../lib/Provider/FakeTranslationProvider.php', 'OCA\\Testing\\Settings\\DeclarativeSettingsForm' => __DIR__ . '/..' . '/../lib/Settings/DeclarativeSettingsForm.php', + 'OCA\\Testing\\TaskProcessing\\FakeContextWriteProvider' => __DIR__ . '/..' . '/../lib/TaskProcessing/FakeContextWriteProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTextToImageProvider' => __DIR__ . '/..' . '/../lib/TaskProcessing/FakeTextToImageProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTextToTextProvider' => __DIR__ . '/..' . '/../lib/TaskProcessing/FakeTextToTextProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTranscribeProvider' => __DIR__ . '/..' . '/../lib/TaskProcessing/FakeTranscribeProvider.php', + 'OCA\\Testing\\TaskProcessing\\FakeTranslateProvider' => __DIR__ . '/..' . '/../lib/TaskProcessing/FakeTranslateProvider.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/apps/testing/lib/AppInfo/Application.php b/apps/testing/lib/AppInfo/Application.php index f6f9777e81c..02e37a4511b 100644 --- a/apps/testing/lib/AppInfo/Application.php +++ b/apps/testing/lib/AppInfo/Application.php @@ -15,6 +15,11 @@ use OCA\Testing\Provider\FakeTextProcessingProvider; use OCA\Testing\Provider\FakeTextProcessingProviderSync; use OCA\Testing\Provider\FakeTranslationProvider; use OCA\Testing\Settings\DeclarativeSettingsForm; +use OCA\Testing\TaskProcessing\FakeContextWriteProvider; +use OCA\Testing\TaskProcessing\FakeTextToImageProvider; +use OCA\Testing\TaskProcessing\FakeTextToTextProvider; +use OCA\Testing\TaskProcessing\FakeTranscribeProvider; +use OCA\Testing\TaskProcessing\FakeTranslateProvider; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; @@ -24,8 +29,10 @@ use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; class Application extends App implements IBootstrap { + public const APP_ID = 'testing'; + public function __construct(array $urlParams = []) { - parent::__construct('testing', $urlParams); + parent::__construct(self::APP_ID, $urlParams); } public function register(IRegistrationContext $context): void { @@ -34,6 +41,12 @@ class Application extends App implements IBootstrap { $context->registerTextProcessingProvider(FakeTextProcessingProviderSync::class); $context->registerTextToImageProvider(FakeText2ImageProvider::class); + $context->registerTaskProcessingProvider(FakeTextToTextProvider::class); + $context->registerTaskProcessingProvider(FakeTextToImageProvider::class); + $context->registerTaskProcessingProvider(FakeTranslateProvider::class); + $context->registerTaskProcessingProvider(FakeTranscribeProvider::class); + $context->registerTaskProcessingProvider(FakeContextWriteProvider::class); + $context->registerDeclarativeSettings(DeclarativeSettingsForm::class); $context->registerEventListener(DeclarativeSettingsRegisterFormEvent::class, RegisterDeclarativeSettingsListener::class); $context->registerEventListener(DeclarativeSettingsGetValueEvent::class, GetDeclarativeSettingsValueListener::class); @@ -43,7 +56,7 @@ class Application extends App implements IBootstrap { public function boot(IBootContext $context): void { $server = $context->getServerContainer(); $config = $server->getConfig(); - if ($config->getAppValue('testing', 'enable_alt_user_backend', 'no') === 'yes') { + if ($config->getAppValue(self::APP_ID, 'enable_alt_user_backend', 'no') === 'yes') { $userManager = $server->getUserManager(); // replace all user backends with this one diff --git a/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php b/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php new file mode 100644 index 00000000000..32aa1ebd7c9 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php @@ -0,0 +1,121 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\ContextWrite; +use RuntimeException; + +class FakeContextWriteProvider implements ISynchronousProvider { + + public function __construct() { + } + + public function getId(): string { + return Application::APP_ID . '-contextwrite'; + } + + public function getName(): string { + return 'Fake context write task processing provider'; + } + + public function getTaskTypeId(): string { + return ContextWrite::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 4321, + 'model' => 'model_2', + ]; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ( + !isset($input['style_input']) || !is_string($input['style_input']) + || !isset($input['source_input']) || !is_string($input['source_input']) + ) { + throw new RuntimeException('Invalid inputs'); + } + $writingStyle = $input['style_input']; + $sourceMaterial = $input['source_input']; + + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'unknown model'; + } + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + $fakeResult = 'This is a fake result: ' + . "\n\n- Style input: " . $writingStyle + . "\n- Source input: " . $sourceMaterial + . "\n- Model: " . $model + . "\n- Maximum number of words: " . $maxTokens; + + return ['output' => $fakeResult]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php b/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php new file mode 100644 index 00000000000..429b3af9d57 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php @@ -0,0 +1,99 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\TaskTypes\TextToImage; +use RuntimeException; + +class FakeTextToImageProvider implements ISynchronousProvider { + + public function __construct() { + } + + public function getId(): string { + return Application::APP_ID . '-text2image'; + } + + public function getName(): string { + return 'Fake text2image task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToImage::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return [ + 'numberOfImages' => 1, + ]; + } + + public function getOptionalInputShape(): array { + return [ + 'size' => new ShapeDescriptor( + 'Size', + 'Optional. The size of the generated images. Must be in 256x256 format.', + EShapeType::Text + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid prompt'); + } + $prompt = $input['input']; + + $nbImages = 1; + if (isset($input['numberOfImages']) && is_int($input['numberOfImages'])) { + $nbImages = $input['numberOfImages']; + } + + $fakeContent = file_get_contents(__DIR__ . '/../../img/logo.png'); + + $output = ['images' => []]; + foreach (range(1, $nbImages) as $i) { + $output['images'][] = $fakeContent; + } + /** @var array<string, list<numeric|string>|numeric|string> $output */ + return $output; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php b/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php new file mode 100644 index 00000000000..9854cd3e609 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php @@ -0,0 +1,113 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\TextToText; +use RuntimeException; + +class FakeTextToTextProvider implements ISynchronousProvider { + + public function __construct() { + } + + public function getId(): string { + return Application::APP_ID . '-text2text'; + } + + public function getName(): string { + return 'Fake text2text task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToText::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 1234, + 'model' => 'model_2', + ]; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'unknown model'; + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid prompt'); + } + $prompt = $input['input']; + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + return [ + 'output' => 'This is a fake result: ' . "\n\n- Prompt: " . $prompt . "\n- Model: " . $model . "\n- Maximum number of words: " . $maxTokens, + ]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php b/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php new file mode 100644 index 00000000000..5401c5e16f8 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php @@ -0,0 +1,80 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\Files\File; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\TaskTypes\AudioToText; +use RuntimeException; + +class FakeTranscribeProvider implements ISynchronousProvider { + + public function __construct( + ) { + } + + public function getId(): string { + return Application::APP_ID . '-audio2text'; + } + + public function getName(): string { + return 'Fake audio2text task processing provider'; + } + + public function getTaskTypeId(): string { + return AudioToText::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if (!isset($input['input']) || !$input['input'] instanceof File || !$input['input']->isReadable()) { + throw new RuntimeException('Invalid input file'); + } + $inputFile = $input['input']; + $transcription = 'Fake transcription result'; + + return ['output' => $transcription]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php b/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php new file mode 100644 index 00000000000..064f54f2650 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php @@ -0,0 +1,146 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\L10N\IFactory; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\TextToTextTranslate; +use RuntimeException; + +class FakeTranslateProvider implements ISynchronousProvider { + + public function __construct( + private IFactory $l10nFactory, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-translate'; + } + + public function getName(): string { + return 'Fake translate task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToTextTranslate::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + $coreL = $this->l10nFactory->getLanguages(); + $languages = array_merge($coreL['commonLanguages'], $coreL['otherLanguages']); + $languageEnumValues = array_map(static function (array $language) { + return new ShapeEnumValue($language['name'], $language['code']); + }, $languages); + $detectLanguageEnumValue = new ShapeEnumValue('Detect language', 'detect_language'); + return [ + 'origin_language' => array_merge([$detectLanguageEnumValue], $languageEnumValues), + 'target_language' => $languageEnumValues, + ]; + } + + public function getInputShapeDefaults(): array { + return [ + 'origin_language' => 'detect_language', + ]; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 200, + 'model' => 'model_3', + ]; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + private function getCoreLanguagesByCode(): array { + $coreL = $this->l10nFactory->getLanguages(); + $coreLanguages = array_reduce(array_merge($coreL['commonLanguages'], $coreL['otherLanguages']), function ($carry, $val) { + $carry[$val['code']] = $val['name']; + return $carry; + }); + return $coreLanguages; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'model_3'; + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid input text'); + } + $inputText = $input['input']; + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + $coreLanguages = $this->getCoreLanguagesByCode(); + + $toLanguage = $coreLanguages[$input['target_language']] ?? $input['target_language']; + if ($input['origin_language'] !== 'detect_language') { + $fromLanguage = $coreLanguages[$input['origin_language']] ?? $input['origin_language']; + $prompt = 'Fake translation from ' . $fromLanguage . ' to ' . $toLanguage . ': ' . $inputText; + } else { + $prompt = 'Fake Translation to ' . $toLanguage . ': ' . $inputText; + } + + $fakeResult = $prompt . "\n\nModel: " . $model . "\nMax tokens: " . $maxTokens; + + return ['output' => $fakeResult]; + } +} diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index a804115a631..37533df088f 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -649,24 +649,24 @@ class Manager implements IManager { return $this->providers; } - public function getPreferredProvider(string $taskType) { + public function getPreferredProvider(string $taskTypeId) { try { $preferences = json_decode($this->config->getAppValue('core', 'ai.taskprocessing_provider_preferences', 'null'), associative: true, flags: JSON_THROW_ON_ERROR); $providers = $this->getProviders(); - if (isset($preferences[$taskType])) { - $provider = current(array_values(array_filter($providers, fn ($provider) => $provider->getId() === $preferences[$taskType]))); + if (isset($preferences[$taskTypeId])) { + $provider = current(array_values(array_filter($providers, fn ($provider) => $provider->getId() === $preferences[$taskTypeId]))); if ($provider !== false) { return $provider; } } // By default, use the first available provider foreach ($providers as $provider) { - if ($provider->getTaskTypeId() === $taskType) { + if ($provider->getTaskTypeId() === $taskTypeId) { return $provider; } } } catch (\JsonException $e) { - $this->logger->warning('Failed to parse provider preferences while getting preferred provider for task type ' . $taskType, ['exception' => $e]); + $this->logger->warning('Failed to parse provider preferences while getting preferred provider for task type ' . $taskTypeId, ['exception' => $e]); } throw new \OCP\TaskProcessing\Exception\Exception('No matching provider found'); } @@ -674,14 +674,14 @@ class Manager implements IManager { public function getAvailableTaskTypes(): array { if ($this->availableTaskTypes === null) { $taskTypes = $this->_getTaskTypes(); - $providers = $this->getProviders(); $availableTaskTypes = []; - foreach ($providers as $provider) { - if (!isset($taskTypes[$provider->getTaskTypeId()])) { + foreach ($taskTypes as $taskType) { + try { + $provider = $this->getPreferredProvider($taskType->getId()); + } catch (\OCP\TaskProcessing\Exception\Exception $e) { continue; } - $taskType = $taskTypes[$provider->getTaskTypeId()]; try { $availableTaskTypes[$provider->getTaskTypeId()] = [ 'name' => $taskType->getName(), diff --git a/lib/private/TaskProcessing/SynchronousBackgroundJob.php b/lib/private/TaskProcessing/SynchronousBackgroundJob.php index 093882d4c1e..85a8fbc21f6 100644 --- a/lib/private/TaskProcessing/SynchronousBackgroundJob.php +++ b/lib/private/TaskProcessing/SynchronousBackgroundJob.php @@ -43,9 +43,14 @@ class SynchronousBackgroundJob extends QueuedJob { if (!$provider instanceof ISynchronousProvider) { continue; } - $taskType = $provider->getTaskTypeId(); + $taskTypeId = $provider->getTaskTypeId(); + // only use this provider if it is the preferred one + $preferredProvider = $this->taskProcessingManager->getPreferredProvider($taskTypeId); + if ($provider->getId() !== $preferredProvider->getId()) { + continue; + } try { - $task = $this->taskProcessingManager->getNextScheduledTask([$taskType]); + $task = $this->taskProcessingManager->getNextScheduledTask([$taskTypeId]); } catch (NotFoundException $e) { continue; } catch (Exception $e) { diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php index e3e6b3be09d..86788449aaf 100644 --- a/lib/public/TaskProcessing/IManager.php +++ b/lib/public/TaskProcessing/IManager.php @@ -38,12 +38,12 @@ interface IManager { public function getProviders(): array; /** - * @param string $taskType + * @param string $taskTypeId * @return IProvider * @throws Exception * @since 30.0.0 */ - public function getPreferredProvider(string $taskType); + public function getPreferredProvider(string $taskTypeId); /** * @return array<array-key,array{name: string, description: string, inputShape: ShapeDescriptor[], inputShapeEnumValues: ShapeEnumValue[][], inputShapeDefaults: array<array-key, numeric|string>, optionalInputShape: ShapeDescriptor[], optionalInputShapeEnumValues: ShapeEnumValue[][], optionalInputShapeDefaults: array<array-key, numeric|string>, outputShape: ShapeDescriptor[], outputShapeEnumValues: ShapeEnumValue[][], optionalOutputShape: ShapeDescriptor[], optionalOutputShapeEnumValues: ShapeEnumValue[][]}> |