From 55df81d2979d161d2c7c9326d8ed450b913ab5f6 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 24 Aug 2023 20:30:00 +0300 Subject: [PATCH 1/3] prepare text processing and speech-to-text files --- appinfo/routes.php | 6 +++ lib/AppInfo/Application.php | 5 +++ lib/Controller/SpeechToTextController.php | 34 ++++++++++++++++ lib/Controller/TextProcessingController.php | 30 ++++++++++++++ lib/Migration/Version1000Date202305221555.php | 27 +++++++++++++ lib/Service/SpeechToTextService.php | 13 +++++++ lib/Service/TextProcessingService.php | 13 +++++++ lib/SpeechToText/SpeechToTextProvider.php | 33 ++++++++++++++++ lib/TextProcessing/ExAppTaskType.php | 39 +++++++++++++++++++ lib/TextProcessing/TextProcessingProvider.php | 30 ++++++++++++++ 10 files changed, 230 insertions(+) create mode 100644 lib/Controller/SpeechToTextController.php create mode 100644 lib/Controller/TextProcessingController.php create mode 100644 lib/Service/SpeechToTextService.php create mode 100644 lib/Service/TextProcessingService.php create mode 100644 lib/SpeechToText/SpeechToTextProvider.php create mode 100644 lib/TextProcessing/ExAppTaskType.php create mode 100644 lib/TextProcessing/TextProcessingProvider.php diff --git a/appinfo/routes.php b/appinfo/routes.php index 26931ae2..5695f3cb 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -46,5 +46,11 @@ // Talk bots ['name' => 'talkBot#registerExAppTalkBot', 'url' => '/api/v1/talk_bot', 'verb' => 'POST'], //['name' => 'talkBot#unregisterExAppTalkBot', 'url' => '/api/v1/talk_bot/{id}', 'verb' => 'DELETE'], + + // SpeechToText + ['name' => 'speechToText#registerProvider', 'url' => '/api/v1/speech_to_text', 'verb' => 'POST'], + + // TextProcessing + ['name' => 'textProcessing#registerProvider', 'url' => '/api/v1/text_processing', 'verb' => 'POST'], ], ]; diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index bea6a9bd..b5a34e2f 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -14,6 +14,8 @@ use OCA\AppEcosystemV2\Profiler\AEDataCollector; use OCA\AppEcosystemV2\PublicCapabilities; +use OCA\AppEcosystemV2\SpeechToText\SpeechToTextProvider; +use OCA\AppEcosystemV2\TextProcessing\TextProcessingProvider; use OCA\DAV\Events\SabrePluginAuthInitEvent; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\App; @@ -47,6 +49,9 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class); $context->registerNotifierService(ExAppNotifier::class); $context->registerNotifierService(ExAppAdminNotifier::class); + + $context->registerTextProcessingProvider(TextProcessingProvider::class); + $context->registerSpeechToTextProvider(SpeechToTextProvider::class); } public function boot(IBootContext $context): void { diff --git a/lib/Controller/SpeechToTextController.php b/lib/Controller/SpeechToTextController.php new file mode 100644 index 00000000..f532fab4 --- /dev/null +++ b/lib/Controller/SpeechToTextController.php @@ -0,0 +1,34 @@ +request = $request; + $this->service = $service; + $this->speechToTextService = $speechToTextService; + } + + public function registerProvider(): Response { + return new DataResponse(); + } +} diff --git a/lib/Controller/TextProcessingController.php b/lib/Controller/TextProcessingController.php new file mode 100644 index 00000000..fe4c5f34 --- /dev/null +++ b/lib/Controller/TextProcessingController.php @@ -0,0 +1,30 @@ +request = $request; + $this->service = $service; + } + + public function registerProvider(): Response { + return new DataResponse(); + } +} diff --git a/lib/Migration/Version1000Date202305221555.php b/lib/Migration/Version1000Date202305221555.php index 71d2c916..59f894c9 100644 --- a/lib/Migration/Version1000Date202305221555.php +++ b/lib/Migration/Version1000Date202305221555.php @@ -293,6 +293,33 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'scope_group'], 'ex_apps_scopes__idx'); } + if (!$schema->hasTable('ex_apps_text_processing')) { + $table = $schema->createTable('ex_apps_text_processing'); + + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('appid', Types::STRING, [ + 'notnull' => true, + 'length' => 32, + ]); + } + // TODO: Add required field for both TextProcessingProvider and SpeechToText + + if (!$schema->hasTable('ex_apps_speech_to_text')) { + $table = $schema->createTable('ex_apps_speech_to_text'); + + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('appid', Types::STRING, [ + 'notnull' => true, + 'length' => 32, + ]); + } + return $schema; } } diff --git a/lib/Service/SpeechToTextService.php b/lib/Service/SpeechToTextService.php new file mode 100644 index 00000000..733af187 --- /dev/null +++ b/lib/Service/SpeechToTextService.php @@ -0,0 +1,13 @@ +l10n = $l10n; + $this->service = $service; + } + + public function getName(): string { + return $this->l10n->t('AppEcosystemV2 speech-to-text provider'); + } + + public function transcribeFile(File $file): string { + // TODO: Pass request to ExApp with file params + + } +} diff --git a/lib/TextProcessing/ExAppTaskType.php b/lib/TextProcessing/ExAppTaskType.php new file mode 100644 index 00000000..9c383c5d --- /dev/null +++ b/lib/TextProcessing/ExAppTaskType.php @@ -0,0 +1,39 @@ +l10n = $l10n; + $this->textProcessingService = $textProcessingService; + $this->exFilesActionsMenuService = $exFilesActionsMenuService; + } + + public function getName(): string { + return $this->l10n->t('AppEcosystemV2 ExApp task'); + } + + public function getDescription(): string { + $fileActions = $this->exFilesActionsMenuService->getRegisteredFileActions(); + $availableExAppProviders = join(', ', array_unique(array_map(function (ExFilesActionsMenu $fileActionMenu) { + return $fileActionMenu->getAppid(); + }, $fileActions))); + return $this->l10n->t(sprintf('Prompt to registered ExApp TextProcessing provider. Registered ExApps TextProcessing providers: %s. Prompt mast start with [appid]: Your prompt text', $availableExAppProviders)); + } +} diff --git a/lib/TextProcessing/TextProcessingProvider.php b/lib/TextProcessing/TextProcessingProvider.php new file mode 100644 index 00000000..f963d925 --- /dev/null +++ b/lib/TextProcessing/TextProcessingProvider.php @@ -0,0 +1,30 @@ +l10n = $l10n; + } + + public function getName(): string { + return $this->l10n->t('AppEcosystemV2 text processing provider'); + } + + public function getTaskType(): string { + return ExAppTaskType::class; + } + + public function process(string $prompt): string { + // TODO: Pass prompt to registered ExApp + + return ''; + } +} From 797ba457705e7f1019e736ef84b1a21f02c8560d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 29 Aug 2023 22:18:13 +0300 Subject: [PATCH 2/3] speech-to-text and text-processing draft --- appinfo/routes.php | 7 +- docs/tech_details/api/speechtotext.rst | 47 ++++ docs/tech_details/api/textprocessing.rst | 90 +++++++ lib/AppInfo/Application.php | 29 ++- lib/Controller/SpeechToTextController.php | 54 ++++- lib/Controller/TextProcessingController.php | 60 ++++- lib/Db/ExAppSpeechToTextProvider.php | 63 +++++ lib/Db/ExAppSpeechToTextProviderMapper.php | 66 +++++ lib/Db/ExAppTextProcessingProvider.php | 77 ++++++ lib/Db/ExAppTextProcessingProviderMapper.php | 66 +++++ lib/Db/ExAppTextProcessingTaskType.php | 61 +++++ lib/Db/ExAppTextProcessingTaskTypeMapper.php | 52 ++++ lib/Migration/Version1000Date202305221555.php | 70 +++++- lib/Service/SpeechToTextService.php | 141 ++++++++++- lib/Service/TextProcessingService.php | 228 +++++++++++++++++- lib/SpeechToText/SpeechToTextProvider.php | 33 --- lib/TextProcessing/ExAppTaskType.php | 39 --- lib/TextProcessing/TextProcessingProvider.php | 30 --- 18 files changed, 1096 insertions(+), 117 deletions(-) create mode 100644 docs/tech_details/api/speechtotext.rst create mode 100644 docs/tech_details/api/textprocessing.rst create mode 100644 lib/Db/ExAppSpeechToTextProvider.php create mode 100644 lib/Db/ExAppSpeechToTextProviderMapper.php create mode 100644 lib/Db/ExAppTextProcessingProvider.php create mode 100644 lib/Db/ExAppTextProcessingProviderMapper.php create mode 100644 lib/Db/ExAppTextProcessingTaskType.php create mode 100644 lib/Db/ExAppTextProcessingTaskTypeMapper.php delete mode 100644 lib/SpeechToText/SpeechToTextProvider.php delete mode 100644 lib/TextProcessing/ExAppTaskType.php delete mode 100644 lib/TextProcessing/TextProcessingProvider.php diff --git a/appinfo/routes.php b/appinfo/routes.php index d38d6a0d..5e17a322 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -47,10 +47,13 @@ ['name' => 'talkBot#registerExAppTalkBot', 'url' => '/api/v1/talk_bot', 'verb' => 'POST'], ['name' => 'talkBot#unregisterExAppTalkBot', 'url' => '/api/v1/talk_bot', 'verb' => 'DELETE'], - // SpeechToText + // Speech-To-Text ['name' => 'speechToText#registerProvider', 'url' => '/api/v1/speech_to_text', 'verb' => 'POST'], + ['name' => 'speechToText#unregisterProvider', 'url' => '/api/v1/speech_to_text', 'verb' => 'DELETE'], - // TextProcessing + // Text-Processing ['name' => 'textProcessing#registerProvider', 'url' => '/api/v1/text_processing', 'verb' => 'POST'], + ['name' => 'textProcessing#unregisterProvider', 'url' => '/api/v1/text_processing', 'verb' => 'DELETE'], + ['name' => 'textProcessing#registerProvider', 'url' => '/api/v1/text_processing/task_type', 'verb' => 'POST'], ], ]; diff --git a/docs/tech_details/api/speechtotext.rst b/docs/tech_details/api/speechtotext.rst new file mode 100644 index 00000000..a9c5a86c --- /dev/null +++ b/docs/tech_details/api/speechtotext.rst @@ -0,0 +1,47 @@ +============== +Speech-To-Text +============== + +AppEcosystemV2 provides a Speech-To-Text (STT) service +that can be used to register ExApp as a custom STT model and transcribe audio files via it. + +Registering ExApp STT provider (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/speech_to_text`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_provider_name", + "display_name": "Provider Display Name", + "action_handler_route": "/handler_route_on_ex_app", + } + +Response +******** + +On successful registration response with status code 200 is returned. + +Unregistering ExApp STT provider (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/speech_to_text`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_provider_name", + } + +Response +******** + +On successful unregister response with status code 200 is returned. + diff --git a/docs/tech_details/api/textprocessing.rst b/docs/tech_details/api/textprocessing.rst new file mode 100644 index 00000000..7088e887 --- /dev/null +++ b/docs/tech_details/api/textprocessing.rst @@ -0,0 +1,90 @@ +=============== +Text-Processing +=============== + +AppEcosystemV2 provides a text-processing service +that can be used to register ExApps providers and to process passed through text. + +Registering text-processing provider (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/text_processing`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_provider_name", + "display_name": "Provider Display Name", + "description": "Provider Description", + "action_handler_route": "/handler_route_on_ex_app", + "action_type": "unique_task_type_name", + } + +Response +******** + +On successful registration response with status code 200 is returned. + +Unregistering text-processing provider (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/text_processing`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_provider_name", + } + +Response +******** + +On successful unregister response with status code 200 is returned. + + +Registering Text-Processing task type (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/text_processing/task_type`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_task_type_name", + "display_name": "Task Type Display Name", + "description": "Task Type Description", + } + +Response +******** + +On successful registration response with status code 200 is returned. + +Unregistering Text-Processing task type (OCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/text_processing/task_type`` + +Request data +************ + +.. code-block:: json + + { + "name": "unique_task_type_name", + } + +Response +******** + +On successful unregister response with status code 200 is returned. + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index b5a34e2f..4b51b695 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -14,8 +14,8 @@ use OCA\AppEcosystemV2\Profiler\AEDataCollector; use OCA\AppEcosystemV2\PublicCapabilities; -use OCA\AppEcosystemV2\SpeechToText\SpeechToTextProvider; -use OCA\AppEcosystemV2\TextProcessing\TextProcessingProvider; +use OCA\AppEcosystemV2\Service\SpeechToTextService; +use OCA\AppEcosystemV2\Service\TextProcessingService; use OCA\DAV\Events\SabrePluginAuthInitEvent; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\App; @@ -50,8 +50,18 @@ public function register(IRegistrationContext $context): void { $context->registerNotifierService(ExAppNotifier::class); $context->registerNotifierService(ExAppAdminNotifier::class); - $context->registerTextProcessingProvider(TextProcessingProvider::class); - $context->registerSpeechToTextProvider(SpeechToTextProvider::class); + // Dynamic anonymous providers registration + $container = $this->getContainer(); + try { + /** @var TextProcessingService $textProcessingService */ + $textProcessingService = $container->get(TextProcessingService::class); + $textProcessingService->registerExAppTextProcessingProviders($container, $context); + + /** @var SpeechToTextService $speechToTextService */ + $speechToTextService = $container->get(SpeechToTextService::class); + $speechToTextService->registerExAppSpeechToTextProviders($container, $context); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface) { + } } public function boot(IBootContext $context): void { @@ -68,9 +78,12 @@ public function boot(IBootContext $context): void { public function registerDavAuth(): void { $container = $this->getContainer(); - $dispatcher = $container->query(IEventDispatcher::class); - $dispatcher->addListener('OCA\DAV\Connector\Sabre::addPlugin', function (SabrePluginEvent $event) use ($container) { - $event->getServer()->addPlugin($container->query(DavPlugin::class)); - }); + try { + $dispatcher = $container->get(IEventDispatcher::class); + $dispatcher->addListener('OCA\DAV\Connector\Sabre::addPlugin', function (SabrePluginEvent $event) use ($container) { + $event->getServer()->addPlugin($container->get(DavPlugin::class)); + }); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface) { + } } } diff --git a/lib/Controller/SpeechToTextController.php b/lib/Controller/SpeechToTextController.php index f532fab4..d70b531c 100644 --- a/lib/Controller/SpeechToTextController.php +++ b/lib/Controller/SpeechToTextController.php @@ -5,9 +5,14 @@ namespace OCA\AppEcosystemV2\Controller; use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\SpeechToTextService; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -28,7 +33,54 @@ public function __construct( $this->speechToTextService = $speechToTextService; } - public function registerProvider(): Response { + /** + * @NoAdminRequired + * @PublicPage + * + * @param string $name + * @param string $displayName + * @param string $actionHandlerRoute + * + * @throws OCSBadRequestException + * @return Response + */ + #[NoCSRFRequired] + #[PublicPage] + #[AppEcosystemAuth] + public function registerProvider(string $name, string $displayName, string $actionHandlerRoute): Response { + $appId = $this->request->getHeader('EX-APP-ID'); + $exApp = $this->service->getExApp($appId); + + $provider = $this->speechToTextService->registerSpeechToTextProvider($exApp, $name, $displayName, $actionHandlerRoute); + + if ($provider === null) { + throw new OCSBadRequestException('Failed to register STT provider'); + } + + return new DataResponse(); + } + + /** + * @NoAdminRequired + * @PublicPage + * + * @param string $name + * + * @throws OCSBadRequestException + * @return Response + */ + #[NoCSRFRequired] + #[PublicPage] + #[AppEcosystemAuth] + public function unregisterProvider(string $name): Response { + $appId = $this->request->getHeader('EX-APP-ID'); + $exApp = $this->service->getExApp($appId); + $unregistered = $this->speechToTextService->unregisterSpeechToTextProvider($exApp, $name); + + if ($unregistered === null) { + throw new OCSBadRequestException('Failed to unregister STT provider'); + } + return new DataResponse(); } } diff --git a/lib/Controller/TextProcessingController.php b/lib/Controller/TextProcessingController.php index fe4c5f34..872bb1a2 100644 --- a/lib/Controller/TextProcessingController.php +++ b/lib/Controller/TextProcessingController.php @@ -5,26 +5,84 @@ namespace OCA\AppEcosystemV2\Controller; use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\TextProcessingService; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCSController; use OCP\IRequest; class TextProcessingController extends OCSController { protected $request; private AppEcosystemV2Service $service; + private TextProcessingService $textProcessingService; public function __construct( IRequest $request, AppEcosystemV2Service $service, + TextProcessingService $textProcessingService, ) { parent::__construct(Application::APP_ID, $request); $this->request = $request; $this->service = $service; + $this->textProcessingService = $textProcessingService; } - public function registerProvider(): Response { + /** + * @NoAdminRequired + * @PublicPage + * + * @param string $name + * @param string $displayName + * @param string $description + * @param string $actionHandlerRoute + * @param string $actionType + * + * @throws OCSBadRequestException + * @return Response + */ + #[NoCSRFRequired] + #[PublicPage] + #[AppEcosystemAuth] + public function registerProvider(string $name, string $displayName, string $description, string $actionHandlerRoute, string $actionType): Response { + $appId = $this->request->getHeader('EX-APP-ID'); + $exApp = $this->service->getExApp($appId); + + $provider = $this->textProcessingService->registerTextProcesingProvider($exApp, $name, $displayName, $description, $actionHandlerRoute); + + if ($provider === null) { + throw new OCSBadRequestException('Failed to register text processing provider'); + } + + return new DataResponse(); + } + + /** + * @NoAdminRequired + * @PublicPage + * + * @param string $name + * + * @throws OCSBadRequestException + * @return Response + */ + #[NoCSRFRequired] + #[PublicPage] + #[AppEcosystemAuth] + public function unregisterProvider(string $name): Response { + $appId = $this->request->getHeader('EX-APP-ID'); + $exApp = $this->service->getExApp($appId); + $unregistered = $this->textProcessingService->unregisterTextProcessingProvider($exApp, $name); + + if ($unregistered === null) { + throw new OCSBadRequestException('Failed to unregister text processing provider'); + } + return new DataResponse(); } } diff --git a/lib/Db/ExAppSpeechToTextProvider.php b/lib/Db/ExAppSpeechToTextProvider.php new file mode 100644 index 00000000..5e99b139 --- /dev/null +++ b/lib/Db/ExAppSpeechToTextProvider.php @@ -0,0 +1,63 @@ +addType('appid', 'string'); + $this->addType('name', 'string'); + $this->addType('displayName', 'string'); + $this->addType('actionHandlerRoute', 'string'); + + if (isset($params['id'])) { + $this->setId($params['id']); + } + if (isset($params['appid'])) { + $this->setAppid($params['appid']); + } + if (isset($params['name'])) { + $this->setName($params['name']); + } + if (isset($params['display_name'])) { + $this->setDisplayName($params['display_name']); + } + if (isset($params['action_handler_route'])) { + $this->setActionHandlerRoute($params['action_handler_route']); + } + } + + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'appid' => $this->getAppid(), + 'name' => $this->getName(), + 'display_name' => $this->getDisplayName(), + 'action_handler_route' => $this->getActionHandlerRoute(), + ]; + } +} diff --git a/lib/Db/ExAppSpeechToTextProviderMapper.php b/lib/Db/ExAppSpeechToTextProviderMapper.php new file mode 100644 index 00000000..9ef6cbb0 --- /dev/null +++ b/lib/Db/ExAppSpeechToTextProviderMapper.php @@ -0,0 +1,66 @@ + + */ +class ExAppSpeechToTextProviderMapper extends QBMapper { + public function __construct(IDBConnection $db) { + parent::__construct($db, 'ex_apps_speech_to_text'); + } + + /** + * @throws Exception + */ + public function findAll(int $limit = null, int $offset = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->tableName) + ->setMaxResults($limit) + ->setFirstResult($offset); + return $this->findEntities($qb); + } + + /** + * @param string $appId + * + * @throws Exception + * @return ExAppSpeechToTextProvider[] + */ + public function findByAppid(string $appId): array { + $qb = $this->db->getQueryBuilder(); + return $this->findEntities($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ); + } + + /** + * @param string $appId + * @param string $name + * + * @throws DoesNotExistException + * @throws Exception + * @throws MultipleObjectsReturnedException + * + * @return ExAppSpeechToTextProvider + */ + public function findByAppidName(string $appId, string $name): ExAppSpeechToTextProvider { + $qb = $this->db->getQueryBuilder(); + return $this->findEntity($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ->andWhere($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) + ); + } +} diff --git a/lib/Db/ExAppTextProcessingProvider.php b/lib/Db/ExAppTextProcessingProvider.php new file mode 100644 index 00000000..47146b09 --- /dev/null +++ b/lib/Db/ExAppTextProcessingProvider.php @@ -0,0 +1,77 @@ +addType('appid', 'string'); + $this->addType('name', 'string'); + $this->addType('displayName', 'string'); + $this->addType('description', 'string'); + $this->addType('actionHandlerRoute', 'string'); + $this->addType('actionType', 'string'); + + if (isset($params['id'])) { + $this->setId($params['id']); + } + if (isset($params['appid'])) { + $this->setAppid($params['appid']); + } + if (isset($params['name'])) { + $this->setName($params['name']); + } + if (isset($params['display_name'])) { + $this->setDisplayName($params['display_name']); + } + if (isset($params['description'])) { + $this->setDescription($params['description']); + } + if (isset($params['action_handler_route'])) { + $this->setActionHandlerRoute($params['action_handler_route']); + } + if (isset($params['action_type'])) { + $this->setActionType($params['action_type']); + } + } + + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'appid' => $this->getAppid(), + 'name' => $this->getName(), + 'display_name' => $this->getDisplayName(), + 'description' => $this->getDescription(), + 'action_handler_route' => $this->getActionHandlerRoute(), + 'action_type' => $this->getActionType(), + ]; + } +} diff --git a/lib/Db/ExAppTextProcessingProviderMapper.php b/lib/Db/ExAppTextProcessingProviderMapper.php new file mode 100644 index 00000000..ec6018a3 --- /dev/null +++ b/lib/Db/ExAppTextProcessingProviderMapper.php @@ -0,0 +1,66 @@ + + */ +class ExAppTextProcessingProviderMapper extends QBMapper { + public function __construct(IDBConnection $db) { + parent::__construct($db, 'ex_apps_text_processing'); + } + + /** + * @throws Exception + */ + public function findAll(int $limit = null, int $offset = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->tableName) + ->setMaxResults($limit) + ->setFirstResult($offset); + return $this->findEntities($qb); + } + + /** + * @param string $appId + * + * @throws Exception + * @return ExAppTextProcessingProvider[] + */ + public function findByAppid(string $appId): array { + $qb = $this->db->getQueryBuilder(); + return $this->findEntities($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ); + } + + /** + * @param string $appId + * @param string $name + * + * @throws DoesNotExistException + * @throws Exception + * @throws MultipleObjectsReturnedException + * + * @return ExAppTextProcessingProvider + */ + public function findByAppidName(string $appId, string $name): ExAppTextProcessingProvider { + $qb = $this->db->getQueryBuilder(); + return $this->findEntity($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ->andWhere($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) + ); + } +} diff --git a/lib/Db/ExAppTextProcessingTaskType.php b/lib/Db/ExAppTextProcessingTaskType.php new file mode 100644 index 00000000..772271fc --- /dev/null +++ b/lib/Db/ExAppTextProcessingTaskType.php @@ -0,0 +1,61 @@ +addType('appid', 'string'); + $this->addType('name', 'string'); + $this->addType('displayName', 'string'); + $this->addType('description', 'string'); + + if (isset($params['id'])) { + $this->setId($params['id']); + } + if (isset($params['appid'])) { + $this->setAppid($params['appid']); + } + if (isset($params['name'])) { + $this->setName($params['name']); + } + if (isset($params['display_name'])) { + $this->setDisplayName($params['display_name']); + } + if (isset($params['description'])) { + $this->setDescription($params['description']); + } + } + + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'appid' => $this->getAppid(), + 'name' => $this->getName(), + 'display_name' => $this->getDisplayName(), + 'description' => $this->getDescription(), + ]; + } +} diff --git a/lib/Db/ExAppTextProcessingTaskTypeMapper.php b/lib/Db/ExAppTextProcessingTaskTypeMapper.php new file mode 100644 index 00000000..ba08b382 --- /dev/null +++ b/lib/Db/ExAppTextProcessingTaskTypeMapper.php @@ -0,0 +1,52 @@ + + */ +class ExAppTextProcessingTaskTypeMapper extends QBMapper { + public function __construct(IDBConnection $db) { + parent::__construct($db, 'ex_apps_tp_task_types'); + } + + /** + * @throws Exception + */ + public function findAll(int $limit = null, int $offset = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->tableName) + ->setMaxResults($limit) + ->setFirstResult($offset); + return $this->findEntities($qb); + } + + /** + * @param string $appId + * @param string $name + * + * @throws DoesNotExistException + * @throws Exception + * @throws MultipleObjectsReturnedException + * + * @return ExAppTextProcessingTaskType + */ + public function findByAppidName(string $appId, string $name): ExAppTextProcessingTaskType { + $qb = $this->db->getQueryBuilder(); + return $this->findEntity($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ->andWhere($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) + ); + } +} diff --git a/lib/Migration/Version1000Date202305221555.php b/lib/Migration/Version1000Date202305221555.php index 59f894c9..6eaf3b3e 100644 --- a/lib/Migration/Version1000Date202305221555.php +++ b/lib/Migration/Version1000Date202305221555.php @@ -293,6 +293,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'scope_group'], 'ex_apps_scopes__idx'); } + // Speech-To-Text and Text-processing APIs tables if (!$schema->hasTable('ex_apps_text_processing')) { $table = $schema->createTable('ex_apps_text_processing'); @@ -304,8 +305,59 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt 'notnull' => true, 'length' => 32, ]); + $table->addColumn('name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('display_name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('description', Types::STRING, [ + 'notnull' => true, + 'length' => 128, + ]); + // ExApp route to forward the action + $table->addColumn('action_handler_route', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('action_type', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + + $table->setPrimaryKey(['id'], 'ex_apps_text_processing_id'); + $table->addUniqueIndex(['appid', 'name'], 'text_processing_appid_name'); + } + + if (!$schema->hasTable('ex_apps_tp_task_types')) { + $table = $schema->createTable('ex_apps_tp_task_types'); + + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('appid', Types::STRING, [ + 'notnull' => true, + 'length' => 32, + ]); + $table->addColumn('name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('display_name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('description', Types::STRING, [ + 'notnull' => true, + 'length' => 128, + ]); + + $table->setPrimaryKey(['id'], 'ex_apps_task_types_id'); + $table->addUniqueIndex(['name'], 'speech_to_text_types_name'); } - // TODO: Add required field for both TextProcessingProvider and SpeechToText if (!$schema->hasTable('ex_apps_speech_to_text')) { $table = $schema->createTable('ex_apps_speech_to_text'); @@ -318,6 +370,22 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt 'notnull' => true, 'length' => 32, ]); + $table->addColumn('name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('display_name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + // ExApp route to forward the action + $table->addColumn('action_handler_route', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + + $table->setPrimaryKey(['id'], 'ex_apps_speech_to_text_id'); + $table->addUniqueIndex(['appid', 'name'], 'speech_to_text_appid_name'); } return $schema; diff --git a/lib/Service/SpeechToTextService.php b/lib/Service/SpeechToTextService.php index 733af187..ccd70b68 100644 --- a/lib/Service/SpeechToTextService.php +++ b/lib/Service/SpeechToTextService.php @@ -4,10 +4,149 @@ namespace OCA\AppEcosystemV2\Service; +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\Db\ExAppSpeechToTextProvider; +use OCA\AppEcosystemV2\Db\ExAppSpeechToTextProviderMapper; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\Http; +use OCP\AppFramework\IAppContainer; +use OCP\DB\Exception; +use OCP\Files\File; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\SpeechToText\ISpeechToTextProvider; +use Psr\Log\LoggerInterface; + class SpeechToTextService { - public function __construct( + private ICache $cache; + private ExAppSpeechToTextProviderMapper $speechToTextProviderMapper; + private AppEcosystemV2Service $service; + private LoggerInterface $logger; + public function __construct( + ICacheFactory $cacheFactory, + AppEcosystemV2Service $service, + ExAppSpeechToTextProviderMapper $speechToTextProviderMapper, + LoggerInterface $logger, ) { + $this->service = $service; + $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_speech_to_text'); + $this->speechToTextProviderMapper = $speechToTextProviderMapper; + $this->logger = $logger; + } + + public function getSpeechToTextProviders(): array { + $cacheKey = '/ex_app_speech_to_text_providers'; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return array_map(function ($cachedEntry) { + return $cachedEntry instanceof ExAppSpeechToTextProvider ? $cachedEntry : new ExAppSpeechToTextProvider($cachedEntry); + }, $cached); + } + + $providers = $this->speechToTextProviderMapper->findAll(); + $this->cache->set($cacheKey, $providers); + return $providers; + } + + public function getExAppSpeechToTextProvider(ExApp $exApp, string $name): ?ExAppSpeechToTextProvider { + $cacheKey = '/ex_app_speech_to_text_providers/' . $exApp->getAppid() . '/' . $name; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExAppSpeechToTextProvider ? $cached : new ExAppSpeechToTextProvider($cached); + } + + $provider = $this->speechToTextProviderMapper->findByAppidName($exApp->getAppid(), $name); + $this->cache->set($cacheKey, $provider); + return $provider; + } + + public function registerSpeechToTextProvider(ExApp $exApp, string $name, string $displayName, string $actionHandlerRoute): ?ExAppSpeechToTextProvider { + $provider = new ExAppSpeechToTextProvider([ + 'appid' => $exApp->getAppid(), + 'name' => $name, + 'display_name' => $displayName, + 'action_handler_route' => $actionHandlerRoute, + ]); + try { + $this->speechToTextProviderMapper->insert($provider); + $this->cache->remove('/ex_app_speech_to_text_providers'); + return $provider; + } catch (Exception $e) { + $this->logger->error('Failed to register SpeechToText provider', ['exception' => $e]); + return null; + } + } + + public function unregisterSpeechToTextProvider(ExApp $exApp, string $name): ?ExAppSpeechToTextProvider { + $provider = $this->getExAppSpeechToTextProvider($exApp, $name); + if ($provider === null) { + return null; + } + try { + $this->speechToTextProviderMapper->delete($provider); + $this->cache->remove('/ex_app_speech_to_text_providers'); + return $provider; + } catch (Exception $e) { + $this->logger->error('Failed to unregister STT provider', ['exception' => $e]); + return null; + } + } + + /** + * Register ExApp anonymous providers implementations of ISpeechToTextProvider + * so that they can be used as regular providers in DI container + * + * @param IAppContainer $container + * @param IRegistrationContext $context + * + * @return void + */ + public function registerExAppSpeechToTextProviders(IAppContainer $container, IRegistrationContext &$context): void { + $exAppsProviders = $this->getSpeechToTextProviders(); + /** @var ExAppSpeechToTextProvider $exAppProvider */ + foreach ($exAppsProviders as $exAppProvider) { + $sttProvider = $this->getAnonymousExAppProvider($exAppProvider); + $class = get_class($sttProvider) . $exAppProvider->getAppid() . $exAppProvider->getName(); + $container->getServer()->registerService($class, function () use ($sttProvider) { + return $sttProvider; + }); + $context->registerSpeechToTextProvider($class); + } + } + + private function getAnonymousExAppProvider(ExAppSpeechToTextProvider $provider): ?ISpeechToTextProvider { + return new class ($this->service, $provider) implements ISpeechToTextProvider { + private AppEcosystemV2Service $service; + private ExAppSpeechToTextProvider $sttProvider; + + public function __construct( + AppEcosystemV2Service $service, + ExAppSpeechToTextProvider $sttProvider, + ) { + $this->service = $service; + $this->sttProvider = $sttProvider; + } + + public function getName(): string { + return $this->sttProvider->getDisplayName(); + } + + public function transcribeFile(File $file): string { + $route = $this->sttProvider->getActionHandlerRoute(); + $exApp = $this->service->getExApp($this->sttProvider->getAppid()); + + $response = $this->service->requestToExApp(null, null, $exApp, $route, 'POST', [ + 'fileid' => $file->getId(), + ]); + + if ($response->getStatusCode() !== Http::STATUS_OK) { + throw new \Exception('Failed to transcribe file'); + } + return $response->getBody(); + } + }; } } diff --git a/lib/Service/TextProcessingService.php b/lib/Service/TextProcessingService.php index 6eceb79c..4f07c9b1 100644 --- a/lib/Service/TextProcessingService.php +++ b/lib/Service/TextProcessingService.php @@ -4,10 +4,236 @@ namespace OCA\AppEcosystemV2\Service; +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\Db\ExAppTextProcessingProvider; +use OCA\AppEcosystemV2\Db\ExAppTextProcessingProviderMapper; +use OCA\AppEcosystemV2\Db\ExAppTextProcessingTaskType; +use OCA\AppEcosystemV2\Db\ExAppTextProcessingTaskTypeMapper; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\IAppContainer; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\ITaskType; + class TextProcessingService { - public function __construct( + private ICache $cache; + private ExAppTextProcessingProviderMapper $textProcessingProviderMapper; + private AppEcosystemV2Service $service; + private ExAppTextProcessingTaskTypeMapper $textProcessingTaskTypeMapper; + public function __construct( + AppEcosystemV2Service $service, + ICacheFactory $cacheFactory, + ExAppTextProcessingProviderMapper $textProcessingProviderMapper, + ExAppTextProcessingTaskTypeMapper $textProcessingTaskTypeMapper, ) { + $this->service = $service; + $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_text_processing'); + $this->textProcessingProviderMapper = $textProcessingProviderMapper; + $this->textProcessingTaskTypeMapper = $textProcessingTaskTypeMapper; + } + + public function getTextProcessingProviders(): array { + $cacheKey = '/ex_app_text_processing_providers'; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return array_map(function ($cachedEntry) { + return $cachedEntry instanceof ExAppTextProcessingProvider ? $cachedEntry : new ExAppTextProcessingProvider($cachedEntry); + }, $cached); + } + + $providers = $this->textProcessingProviderMapper->findAll(); + $this->cache->set($cacheKey, $providers); + return $providers; + } + + public function getExAppTextProcessingProvider(ExApp $exApp, string $name): ?ExAppTextProcessingProvider { + $cacheKey = '/ex_app_text_processing_providers/' . $exApp->getAppid() . '/' . $name; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExAppTextProcessingProvider ? $cached : new ExAppTextProcessingProvider($cached); + } + + $provider = $this->textProcessingProviderMapper->findByAppidName($exApp->getAppid(), $name); + $this->cache->set($cacheKey, $provider); + return $provider; + } + + public function registerTextProcessingProvider( + ExApp $exApp, + string $name, + string $displayName, + string $description, + string $actionHandlerRoute, + string $actionType + ): ?ExAppTextProcessingProvider { + $provider = $this->textProcessingProviderMapper->findByAppidName($exApp->getAppid(), $name); + if ($provider !== null) { + return null; + } + + $provider = new ExAppTextProcessingProvider([ + 'appid' => $exApp->getAppid(), + 'name' => $name, + 'display_name' => $displayName, + 'description' => $description, + 'action_handler_route' => $actionHandlerRoute, + 'action_type' => $actionType, + ]); + $this->textProcessingProviderMapper->insert($provider); + + $this->cache->remove('/ex_app_text_processing_providers'); + $this->cache->remove('/ex_app_text_processing_providers/' . $exApp->getAppid() . '/' . $name); + + return $provider; + } + + /** + * Register dynamic text processing providers anonymous classes. + * For each text processing provider register anonymous class for IProvider and ITaskType in DI container. + * + * @param IAppContainer $container + * @param IRegistrationContext $context + * + * @return void + */ + public function registerExAppTextProcessingProviders(IAppContainer $container, IRegistrationContext &$context): void { + $exAppsProviders = $this->getTextProcessingProviders(); + /** @var ExAppTextProcessingProvider $exAppProvider */ + foreach ($exAppsProviders as $exAppProvider) { + $exApp = $this->service->getExApp($exAppProvider->getAppid()); + $tpTaskType = $this->getExAppTextProcessingTaskType($exApp, $exAppProvider->getActionType()); + $taskType = $this->getAnonymousTaskType($exAppProvider, $tpTaskType); + $taskTypeClassName = get_class($taskType) . $tpTaskType->getAppid() . $tpTaskType->getName(); + $container->getServer()->registerService($taskTypeClassName, function () use ($taskType) { + return $taskType; + }); + + $provider = $this->getAnonymousExAppProvider($exAppProvider, $taskTypeClassName); + $className = get_class($provider) . $exAppProvider->getAppid() . $exAppProvider->getName(); + $container->getServer()->registerService($className, function () use ($provider) { + return $provider; + }); + $context->registerTextProcessingProvider($className); + } + } + + /** + * + * + * @param ExAppTextProcessingProvider $provider + * @param string $taskTypeClassName + * + * @return IProvider + */ + private function getAnonymousExAppProvider(ExAppTextProcessingProvider $provider, string $taskTypeClassName): IProvider { + return new class ($this->service, $provider, $taskTypeClassName) implements IProvider { + private AppEcosystemV2Service $service; + private ExAppTextProcessingProvider $provider; + private string $taskTypeClassName; + + public function __construct( + AppEcosystemV2Service $service, + ExAppTextProcessingProvider $provider, + string $taskTypeClassName, + ) { + $this->service = $service; + $this->provider = $provider; + $this->taskTypeClassName = $taskTypeClassName; + } + + public function getName(): string { + return $this->provider->getDisplayName(); + } + + public function process(string $prompt): string { + $exApp = $this->service->getExApp($this->provider->getAppid()); + $route = $this->provider->getActionHandlerRoute(); + + $response = $this->service->requestToExApp(null, null, $exApp, $route, 'POST', [ + 'prompt' => $prompt, + ]); + + if ($response->getStatusCode() !== 200) { + throw new \Exception('Failed to process prompt'); + } + + return $response->getBody(); + } + + public function getTaskType(): string { + return $this->taskTypeClassName; + } + }; + } + + /** + * Build dynamic anonymous class implementing ITaskType + * for given ExAppTextProcessingProvider and ExAppTextProcessingTaskType data. + * + * @param ExAppTextProcessingProvider $provider + * @param ExAppTextProcessingTaskType $tpTaskType + * + * @return ITaskType + */ + private function getAnonymousTaskType(ExAppTextProcessingProvider $provider, ExAppTextProcessingTaskType $tpTaskType): ITaskType { + return new class ($tpTaskType) implements ITaskType { + private ExAppTextProcessingTaskType $tpTaskType; + public function __construct( + ExAppTextProcessingTaskType $tpTaskType, + ) { + $this->tpTaskType = $tpTaskType; + } + + public function getName(): string { + return $this->tpTaskType->getDisplayName(); + } + + public function getDescription(): string { + return $this->tpTaskType->getDescription(); + } + }; + } + + public function registerTextProcessingTaskType(ExApp $exApp, string $name, string $displayName, string $description): ?ExAppTextProcessingTaskType { + $taskType = $this->textProcessingTaskTypeMapper->findByAppidName($exApp->getAppid(), $name); + if ($taskType !== null) { + return null; + } + + $taskType = new ExAppTextProcessingTaskType([ + 'appid' => $exApp->getAppid(), + 'name' => $name, + 'display_name' => $displayName, + 'description' => $description, + ]); + $this->textProcessingTaskTypeMapper->insert($taskType); + $this->cache->set('/ex_app_text_processing_task_types/' . $exApp->getAppid() . '/' . $name, $taskType); + return $taskType; + } + + public function unregisterTextProcessingTaskType(ExApp $exApp, string $name): ?ExAppTextProcessingTaskType { + $taskType = $this->getExAppTextProcessingTaskType($exApp, $name); + if ($taskType === null) { + return null; + } + + $this->textProcessingTaskTypeMapper->delete($taskType); + $this->cache->remove('/ex_app_text_processing_task_types/' . $exApp->getAppid() . '/' . $name); + return $taskType; + } + + public function getExAppTextProcessingTaskType(ExApp $exApp, string $name): ?ExAppTextProcessingTaskType { + $cacheKey = '/ex_app_text_processing_task_types/' . $exApp->getAppid() . '/' . $name; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExAppTextProcessingTaskType ? $cached : new ExAppTextProcessingTaskType($cached); + } + $taskType = $this->textProcessingTaskTypeMapper->findByAppidName($exApp->getAppid(), $name); + $this->cache->set($cacheKey, $taskType); + return $taskType; } } diff --git a/lib/SpeechToText/SpeechToTextProvider.php b/lib/SpeechToText/SpeechToTextProvider.php deleted file mode 100644 index ef491c87..00000000 --- a/lib/SpeechToText/SpeechToTextProvider.php +++ /dev/null @@ -1,33 +0,0 @@ -l10n = $l10n; - $this->service = $service; - } - - public function getName(): string { - return $this->l10n->t('AppEcosystemV2 speech-to-text provider'); - } - - public function transcribeFile(File $file): string { - // TODO: Pass request to ExApp with file params - - } -} diff --git a/lib/TextProcessing/ExAppTaskType.php b/lib/TextProcessing/ExAppTaskType.php deleted file mode 100644 index 9c383c5d..00000000 --- a/lib/TextProcessing/ExAppTaskType.php +++ /dev/null @@ -1,39 +0,0 @@ -l10n = $l10n; - $this->textProcessingService = $textProcessingService; - $this->exFilesActionsMenuService = $exFilesActionsMenuService; - } - - public function getName(): string { - return $this->l10n->t('AppEcosystemV2 ExApp task'); - } - - public function getDescription(): string { - $fileActions = $this->exFilesActionsMenuService->getRegisteredFileActions(); - $availableExAppProviders = join(', ', array_unique(array_map(function (ExFilesActionsMenu $fileActionMenu) { - return $fileActionMenu->getAppid(); - }, $fileActions))); - return $this->l10n->t(sprintf('Prompt to registered ExApp TextProcessing provider. Registered ExApps TextProcessing providers: %s. Prompt mast start with [appid]: Your prompt text', $availableExAppProviders)); - } -} diff --git a/lib/TextProcessing/TextProcessingProvider.php b/lib/TextProcessing/TextProcessingProvider.php deleted file mode 100644 index f963d925..00000000 --- a/lib/TextProcessing/TextProcessingProvider.php +++ /dev/null @@ -1,30 +0,0 @@ -l10n = $l10n; - } - - public function getName(): string { - return $this->l10n->t('AppEcosystemV2 text processing provider'); - } - - public function getTaskType(): string { - return ExAppTaskType::class; - } - - public function process(string $prompt): string { - // TODO: Pass prompt to registered ExApp - - return ''; - } -} From e93dfe36f9b28aa97707495427d02c2b22d7b4c6 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Sun, 22 Oct 2023 14:39:37 +0300 Subject: [PATCH 3/3] update after renaming, do not use server container for registration Signed-off-by: Andrey Borysenko --- docs/tech_details/api/speechtotext.rst | 6 ++-- docs/tech_details/api/textprocessing.rst | 10 +++--- lib/AppInfo/Application.php | 8 ++--- lib/Controller/SpeechToTextController.php | 18 +++++------ lib/Controller/TextProcessingController.php | 10 +++--- lib/Db/ExAppSpeechToTextProvider.php | 4 +-- lib/Db/ExAppSpeechToTextProviderMapper.php | 2 +- lib/Db/ExAppTextProcessingProvider.php | 4 +-- lib/Db/ExAppTextProcessingProviderMapper.php | 2 +- lib/Db/ExAppTextProcessingTaskType.php | 2 +- lib/Db/ExAppTextProcessingTaskTypeMapper.php | 2 +- lib/Service/SpeechToTextService.php | 24 +++++++------- lib/Service/TextProcessingService.php | 34 ++++++++++---------- 13 files changed, 63 insertions(+), 63 deletions(-) diff --git a/docs/tech_details/api/speechtotext.rst b/docs/tech_details/api/speechtotext.rst index a9c5a86c..9d534221 100644 --- a/docs/tech_details/api/speechtotext.rst +++ b/docs/tech_details/api/speechtotext.rst @@ -2,13 +2,13 @@ Speech-To-Text ============== -AppEcosystemV2 provides a Speech-To-Text (STT) service +AppAPI provides a Speech-To-Text (STT) service that can be used to register ExApp as a custom STT model and transcribe audio files via it. Registering ExApp STT provider (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/speech_to_text`` +OCS endpoint: ``POST /apps/app_api/api/v1/speech_to_text`` Request data ************ @@ -29,7 +29,7 @@ On successful registration response with status code 200 is returned. Unregistering ExApp STT provider (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/speech_to_text`` +OCS endpoint: ``DELETE /apps/app_api/api/v1/speech_to_text`` Request data ************ diff --git a/docs/tech_details/api/textprocessing.rst b/docs/tech_details/api/textprocessing.rst index 7088e887..f0354fb7 100644 --- a/docs/tech_details/api/textprocessing.rst +++ b/docs/tech_details/api/textprocessing.rst @@ -2,13 +2,13 @@ Text-Processing =============== -AppEcosystemV2 provides a text-processing service +AppAPI provides a text-processing service that can be used to register ExApps providers and to process passed through text. Registering text-processing provider (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/text_processing`` +OCS endpoint: ``POST /apps/app_api/api/v1/text_processing`` Request data ************ @@ -31,7 +31,7 @@ On successful registration response with status code 200 is returned. Unregistering text-processing provider (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/text_processing`` +OCS endpoint: ``DELETE /apps/app_api/api/v1/text_processing`` Request data ************ @@ -51,7 +51,7 @@ On successful unregister response with status code 200 is returned. Registering Text-Processing task type (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``POST /apps/app_ecosystem_v2/api/v1/text_processing/task_type`` +OCS endpoint: ``POST /apps/app_api/api/v1/text_processing/task_type`` Request data ************ @@ -72,7 +72,7 @@ On successful registration response with status code 200 is returned. Unregistering Text-Processing task type (OCS) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OCS endpoint: ``DELETE /apps/app_ecosystem_v2/api/v1/text_processing/task_type`` +OCS endpoint: ``DELETE /apps/app_api/api/v1/text_processing/task_type`` Request data ************ diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 687ee9c7..77b80937 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -15,8 +15,8 @@ use OCA\AppAPI\Profiler\AppAPIDataCollector; use OCA\AppAPI\PublicCapabilities; -use OCA\AppEcosystemV2\Service\SpeechToTextService; -use OCA\AppEcosystemV2\Service\TextProcessingService; +use OCA\AppAPI\Service\SpeechToTextService; +use OCA\AppAPI\Service\TextProcessingService; use OCA\DAV\Events\SabrePluginAuthInitEvent; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\App; @@ -64,11 +64,11 @@ public function register(IRegistrationContext $context): void { try { /** @var TextProcessingService $textProcessingService */ $textProcessingService = $container->get(TextProcessingService::class); - $textProcessingService->registerExAppTextProcessingProviders($container, $context); + $textProcessingService->registerExAppTextProcessingProviders($context); /** @var SpeechToTextService $speechToTextService */ $speechToTextService = $container->get(SpeechToTextService::class); - $speechToTextService->registerExAppSpeechToTextProviders($container, $context); + $speechToTextService->registerExAppSpeechToTextProviders($context); } catch (NotFoundExceptionInterface|ContainerExceptionInterface) { } } diff --git a/lib/Controller/SpeechToTextController.php b/lib/Controller/SpeechToTextController.php index d70b531c..2656cf77 100644 --- a/lib/Controller/SpeechToTextController.php +++ b/lib/Controller/SpeechToTextController.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Controller; +namespace OCA\AppAPI\Controller; -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\SpeechToTextService; +use OCA\AppAPI\AppInfo\Application; +use OCA\AppAPI\Attribute\AppAPIAuth; +use OCA\AppAPI\Service\AppAPIService; +use OCA\AppAPI\Service\SpeechToTextService; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; @@ -18,12 +18,12 @@ class SpeechToTextController extends OCSController { protected $request; - private AppEcosystemV2Service $service; + private AppAPIService $service; private SpeechToTextService $speechToTextService; public function __construct( IRequest $request, - AppEcosystemV2Service $service, + AppAPIService $service, SpeechToTextService $speechToTextService, ) { parent::__construct(Application::APP_ID, $request); @@ -46,7 +46,7 @@ public function __construct( */ #[NoCSRFRequired] #[PublicPage] - #[AppEcosystemAuth] + #[AppAPIAuth] public function registerProvider(string $name, string $displayName, string $actionHandlerRoute): Response { $appId = $this->request->getHeader('EX-APP-ID'); $exApp = $this->service->getExApp($appId); @@ -71,7 +71,7 @@ public function registerProvider(string $name, string $displayName, string $acti */ #[NoCSRFRequired] #[PublicPage] - #[AppEcosystemAuth] + #[AppAPIAuth] public function unregisterProvider(string $name): Response { $appId = $this->request->getHeader('EX-APP-ID'); $exApp = $this->service->getExApp($appId); diff --git a/lib/Controller/TextProcessingController.php b/lib/Controller/TextProcessingController.php index 872bb1a2..735f42d5 100644 --- a/lib/Controller/TextProcessingController.php +++ b/lib/Controller/TextProcessingController.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Controller; +namespace OCA\AppAPI\Controller; -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\TextProcessingService; +use OCA\AppAPI\AppInfo\Application; +use OCA\AppAPI\Attribute\AppEcosystemAuth; +use OCA\AppAPI\Service\AppAPIService; +use OCA\AppAPI\Service\TextProcessingService; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; diff --git a/lib/Db/ExAppSpeechToTextProvider.php b/lib/Db/ExAppSpeechToTextProvider.php index 5e99b139..ae9052d8 100644 --- a/lib/Db/ExAppSpeechToTextProvider.php +++ b/lib/Db/ExAppSpeechToTextProvider.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\Entity; /** * Class ExAppSpeechToTextProvider * - * @package OCA\AppEcosystemV2\Db + * @package OCA\AppAPI\Db * * @method string getAppid() * @method string getName() diff --git a/lib/Db/ExAppSpeechToTextProviderMapper.php b/lib/Db/ExAppSpeechToTextProviderMapper.php index 9ef6cbb0..5b0d4a1d 100644 --- a/lib/Db/ExAppSpeechToTextProviderMapper.php +++ b/lib/Db/ExAppSpeechToTextProviderMapper.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; diff --git a/lib/Db/ExAppTextProcessingProvider.php b/lib/Db/ExAppTextProcessingProvider.php index 47146b09..7d9d08f4 100644 --- a/lib/Db/ExAppTextProcessingProvider.php +++ b/lib/Db/ExAppTextProcessingProvider.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\Entity; /** * Class ExAppTextProcessingProvider * - * @package OCA\AppEcosystemV2\Db + * @package OCA\AppAPI\Db * * @method string getAppid() * @method string getName() diff --git a/lib/Db/ExAppTextProcessingProviderMapper.php b/lib/Db/ExAppTextProcessingProviderMapper.php index ec6018a3..38573d95 100644 --- a/lib/Db/ExAppTextProcessingProviderMapper.php +++ b/lib/Db/ExAppTextProcessingProviderMapper.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; diff --git a/lib/Db/ExAppTextProcessingTaskType.php b/lib/Db/ExAppTextProcessingTaskType.php index 772271fc..2a8ae291 100644 --- a/lib/Db/ExAppTextProcessingTaskType.php +++ b/lib/Db/ExAppTextProcessingTaskType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\Entity; diff --git a/lib/Db/ExAppTextProcessingTaskTypeMapper.php b/lib/Db/ExAppTextProcessingTaskTypeMapper.php index ba08b382..40b6edb7 100644 --- a/lib/Db/ExAppTextProcessingTaskTypeMapper.php +++ b/lib/Db/ExAppTextProcessingTaskTypeMapper.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Db; +namespace OCA\AppAPI\Db; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; diff --git a/lib/Service/SpeechToTextService.php b/lib/Service/SpeechToTextService.php index ccd70b68..0b720c20 100644 --- a/lib/Service/SpeechToTextService.php +++ b/lib/Service/SpeechToTextService.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Service; +namespace OCA\AppAPI\Service; -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Db\ExApp; -use OCA\AppEcosystemV2\Db\ExAppSpeechToTextProvider; -use OCA\AppEcosystemV2\Db\ExAppSpeechToTextProviderMapper; +use OCA\AppAPI\AppInfo\Application; +use OCA\AppAPI\Db\ExApp; +use OCA\AppAPI\Db\ExAppSpeechToTextProvider; +use OCA\AppAPI\Db\ExAppSpeechToTextProviderMapper; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Http; use OCP\AppFramework\IAppContainer; @@ -21,12 +21,12 @@ class SpeechToTextService { private ICache $cache; private ExAppSpeechToTextProviderMapper $speechToTextProviderMapper; - private AppEcosystemV2Service $service; + private AppAPIService $service; private LoggerInterface $logger; public function __construct( ICacheFactory $cacheFactory, - AppEcosystemV2Service $service, + AppAPIService $service, ExAppSpeechToTextProviderMapper $speechToTextProviderMapper, LoggerInterface $logger, ) { @@ -103,13 +103,13 @@ public function unregisterSpeechToTextProvider(ExApp $exApp, string $name): ?ExA * * @return void */ - public function registerExAppSpeechToTextProviders(IAppContainer $container, IRegistrationContext &$context): void { + public function registerExAppSpeechToTextProviders(IRegistrationContext &$context): void { $exAppsProviders = $this->getSpeechToTextProviders(); /** @var ExAppSpeechToTextProvider $exAppProvider */ foreach ($exAppsProviders as $exAppProvider) { $sttProvider = $this->getAnonymousExAppProvider($exAppProvider); - $class = get_class($sttProvider) . $exAppProvider->getAppid() . $exAppProvider->getName(); - $container->getServer()->registerService($class, function () use ($sttProvider) { + $class = '\\OCA\\AppAPI\\' . $exAppProvider->getAppid() . '_' . $exAppProvider->getName(); + $context->registerService($class, function () use ($sttProvider) { return $sttProvider; }); $context->registerSpeechToTextProvider($class); @@ -118,11 +118,11 @@ public function registerExAppSpeechToTextProviders(IAppContainer $container, IRe private function getAnonymousExAppProvider(ExAppSpeechToTextProvider $provider): ?ISpeechToTextProvider { return new class ($this->service, $provider) implements ISpeechToTextProvider { - private AppEcosystemV2Service $service; + private AppAPIService $service; private ExAppSpeechToTextProvider $sttProvider; public function __construct( - AppEcosystemV2Service $service, + AppAPIService $service, ExAppSpeechToTextProvider $sttProvider, ) { $this->service = $service; diff --git a/lib/Service/TextProcessingService.php b/lib/Service/TextProcessingService.php index 4f07c9b1..d648b14a 100644 --- a/lib/Service/TextProcessingService.php +++ b/lib/Service/TextProcessingService.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace OCA\AppEcosystemV2\Service; - -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Db\ExApp; -use OCA\AppEcosystemV2\Db\ExAppTextProcessingProvider; -use OCA\AppEcosystemV2\Db\ExAppTextProcessingProviderMapper; -use OCA\AppEcosystemV2\Db\ExAppTextProcessingTaskType; -use OCA\AppEcosystemV2\Db\ExAppTextProcessingTaskTypeMapper; +namespace OCA\AppAPI\Service; + +use OCA\AppAPI\AppInfo\Application; +use OCA\AppAPI\Db\ExApp; +use OCA\AppAPI\Db\ExAppTextProcessingProvider; +use OCA\AppAPI\Db\ExAppTextProcessingProviderMapper; +use OCA\AppAPI\Db\ExAppTextProcessingTaskType; +use OCA\AppAPI\Db\ExAppTextProcessingTaskTypeMapper; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\IAppContainer; use OCP\ICache; @@ -20,11 +20,11 @@ class TextProcessingService { private ICache $cache; private ExAppTextProcessingProviderMapper $textProcessingProviderMapper; - private AppEcosystemV2Service $service; + private AppAPIService $service; private ExAppTextProcessingTaskTypeMapper $textProcessingTaskTypeMapper; public function __construct( - AppEcosystemV2Service $service, + AppAPIService $service, ICacheFactory $cacheFactory, ExAppTextProcessingProviderMapper $textProcessingProviderMapper, ExAppTextProcessingTaskTypeMapper $textProcessingTaskTypeMapper, @@ -99,21 +99,21 @@ public function registerTextProcessingProvider( * * @return void */ - public function registerExAppTextProcessingProviders(IAppContainer $container, IRegistrationContext &$context): void { + public function registerExAppTextProcessingProviders(IRegistrationContext &$context): void { $exAppsProviders = $this->getTextProcessingProviders(); /** @var ExAppTextProcessingProvider $exAppProvider */ foreach ($exAppsProviders as $exAppProvider) { $exApp = $this->service->getExApp($exAppProvider->getAppid()); $tpTaskType = $this->getExAppTextProcessingTaskType($exApp, $exAppProvider->getActionType()); $taskType = $this->getAnonymousTaskType($exAppProvider, $tpTaskType); - $taskTypeClassName = get_class($taskType) . $tpTaskType->getAppid() . $tpTaskType->getName(); - $container->getServer()->registerService($taskTypeClassName, function () use ($taskType) { + $taskTypeClassName = '\\OCA\\AppAPI\\' . $tpTaskType->getAppid() . '_' . $tpTaskType->getName(); + $context->registerService($taskTypeClassName, function () use ($taskType) { return $taskType; }); $provider = $this->getAnonymousExAppProvider($exAppProvider, $taskTypeClassName); - $className = get_class($provider) . $exAppProvider->getAppid() . $exAppProvider->getName(); - $container->getServer()->registerService($className, function () use ($provider) { + $className = '\\OCA\\AppAPI\\' . $exAppProvider->getAppid() . '_' . $exAppProvider->getName(); + $context->registerService($className, function () use ($provider) { return $provider; }); $context->registerTextProcessingProvider($className); @@ -130,12 +130,12 @@ public function registerExAppTextProcessingProviders(IAppContainer $container, I */ private function getAnonymousExAppProvider(ExAppTextProcessingProvider $provider, string $taskTypeClassName): IProvider { return new class ($this->service, $provider, $taskTypeClassName) implements IProvider { - private AppEcosystemV2Service $service; + private AppAPIService $service; private ExAppTextProcessingProvider $provider; private string $taskTypeClassName; public function __construct( - AppEcosystemV2Service $service, + AppAPIService $service, ExAppTextProcessingProvider $provider, string $taskTypeClassName, ) {