Skip to content

Commit

Permalink
Text-Processing APIs implementation (#191)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
Co-authored-by: Alexander Piskun <bigcat88@icloud.com>
  • Loading branch information
andrey18106 and bigcat88 authored Jan 1, 2024
1 parent a8863df commit db2a3cf
Show file tree
Hide file tree
Showing 13 changed files with 626 additions and 1 deletion.
5 changes: 5 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,10 @@
['name' => 'speechToText#registerProvider', 'url' => '/api/v1/ai_provider/speech_to_text', 'verb' => 'POST'],
['name' => 'speechToText#unregisterProvider', 'url' => '/api/v1/ai_provider/speech_to_text', 'verb' => 'DELETE'],
['name' => 'speechToText#getProvider', 'url' => '/api/v1/ai_provider/speech_to_text', 'verb' => 'GET'],

// Text-Processing
['name' => 'textProcessing#registerProvider', 'url' => '/api/v1/ai_provider/text_processing', 'verb' => 'POST'],
['name' => 'textProcessing#unregisterProvider', 'url' => '/api/v1/ai_provider/text_processing', 'verb' => 'DELETE'],
['name' => 'textProcessing#getProvider', 'url' => '/api/v1/ai_provider/text_processing', 'verb' => 'GET'],
],
];
1 change: 1 addition & 0 deletions docs/tech_details/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ AppAPI Nextcloud APIs
notifications
talkbots
speechtotext
textprocessing
other_ocs
4 changes: 4 additions & 0 deletions docs/tech_details/api/speechtotext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Speech-To-Text

AppAPI provides a Speech-To-Text (STT) provider registration API for the ExApps.

.. note::

Available since Nextcloud 29.

Registering ExApp STT provider (OCS)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
72 changes: 72 additions & 0 deletions docs/tech_details/api/textprocessing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
===============
Text-Processing
===============

AppAPI provides a Text-Processing providers registration mechanism for ExApps.

.. note::

Available since Nextcloud 29.

Registering text-processing provider (OCS)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

OCS endpoint: ``POST /apps/app_api/api/v1/ai_provider/text_processing``

Request data
************

.. code-block:: json
{
"name": "unique_provider_name",
"display_name": "Provider Display Name",
"action_handler": "/handler_route_on_ex_app",
"task_type": "supported_task_type",
}
.. note::

``action_type`` is a class name of the Text-Processing task type that can be found in the list of supported task types.

Response
********

On successful registration response with status code 200 is returned.

Unregistering text-processing provider (OCS)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

OCS endpoint: ``DELETE /apps/app_api/api/v1/ai_provider/text_processing``

Request data
************

.. code-block:: json
{
"name": "unique_provider_name",
}
Response
********

On successful unregister response with status code 200 is returned.


Get list of supported Text-Processing task types (capabilities)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There are limited number of Task Types that can be used for Text-Processing.
You can get list of supported Text-Processing task types from OCS capabilities.

Response
********

.. code-block:: json
{
"text_processing": {
"task_types": ["free_prompt", "headline", "summary", "topics"]
}
}
5 changes: 5 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use OCA\AppAPI\Profiler\AppAPIDataCollector;
use OCA\AppAPI\PublicCapabilities;
use OCA\AppAPI\Service\SpeechToTextService;
use OCA\AppAPI\Service\TextProcessingService;
use OCA\AppAPI\Service\UI\TopMenuService;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
Expand Down Expand Up @@ -66,6 +67,10 @@ public function register(IRegistrationContext $context): void {
/** @var SpeechToTextService $speechToTextService */
$speechToTextService = $container->get(SpeechToTextService::class);
$speechToTextService->registerExAppSpeechToTextProviders($context, $container->getServer());

/** @var TextProcessingService $textProcessingService */
$textProcessingService = $container->get(TextProcessingService::class);
$textProcessingService->registerExAppTextProcessingProviders($context, $container->getServer());
} catch (NotFoundExceptionInterface|ContainerExceptionInterface) {
}
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use OCA\AppAPI\Service\AppAPIService;
use OCA\AppAPI\Service\ExAppScopesService;

use OCA\AppAPI\Service\TextProcessingService;
use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\IConfig;
Expand All @@ -29,6 +30,9 @@ public function getCapabilities(): array {
$capabilities = [
'loglevel' => intval($this->config->getSystemValue('loglevel', 2)),
'version' => $this->appManager->getAppVersion(Application::APP_ID),
'text_processing' => [
'task_types' => array_keys(TextProcessingService::TASK_TYPES),
]
];
$this->attachExAppScopes($capabilities);
return [
Expand Down
93 changes: 93 additions & 0 deletions lib/Controller/TextProcessingController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace OCA\AppAPI\Controller;

use OCA\AppAPI\AppInfo\Application;
use OCA\AppAPI\Attribute\AppAPIAuth;
use OCA\AppAPI\Service\TextProcessingService;
use OCP\AppFramework\Http;
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\OCSController;
use OCP\IConfig;
use OCP\IRequest;

class TextProcessingController extends OCSController {
protected $request;

public function __construct(
IRequest $request,
private readonly IConfig $config,
private readonly TextProcessingService $textProcessingService,
) {
parent::__construct(Application::APP_ID, $request);

$this->request = $request;
}

#[NoCSRFRequired]
#[PublicPage]
#[AppAPIAuth]
public function registerProvider(
string $name,
string $displayName,
string $actionHandler,
string $taskType
): DataResponse {
$ncVersion = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($ncVersion, '29.0', '<')) {
return new DataResponse([], Http::STATUS_NOT_IMPLEMENTED);
}

$provider = $this->textProcessingService->registerTextProcessingProvider(
$this->request->getHeader('EX-APP-ID'), $name, $displayName, $actionHandler, $taskType,
);

if ($provider === null) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
}

#[NoCSRFRequired]
#[PublicPage]
#[AppAPIAuth]
public function unregisterProvider(string $name): Response {
$ncVersion = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($ncVersion, '29.0', '<')) {
return new DataResponse([], Http::STATUS_NOT_IMPLEMENTED);
}

$unregistered = $this->textProcessingService->unregisterTextProcessingProvider(
$this->request->getHeader('EX-APP-ID'), $name
);

if ($unregistered === null) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}

return new DataResponse();
}

#[NoCSRFRequired]
#[PublicPage]
#[AppAPIAuth]
public function getProvider(string $name): DataResponse {
$ncVersion = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($ncVersion, '29.0', '<')) {
return new DataResponse([], Http::STATUS_NOT_IMPLEMENTED);
}
$result = $this->textProcessingService->getExAppTextProcessingProvider(
$this->request->getHeader('EX-APP-ID'), $name
);
if (!$result) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
return new DataResponse($result, Http::STATUS_OK);
}
}
69 changes: 69 additions & 0 deletions lib/Db/TextProcessing/TextProcessingProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace OCA\AppAPI\Db\TextProcessing;

use OCP\AppFramework\Db\Entity;

/**
* Class TextProcessingProvider
*
* @package OCA\AppAPI\Db\TextProcessing
*
* @method string getAppid()
* @method string getName()
* @method string getDisplayName()
* @method string getActionHandler()
* @method string getTaskType()
* @method void setAppid(string $appid)
* @method void setName(string $name)
* @method void setDisplayName(string $displayName)
* @method void setActionHandler(string $actionHandler)
* @method void setTaskType(string $taskType)
*/
class TextProcessingProvider extends Entity implements \JsonSerializable {
protected $appid;
protected $name;
protected $displayName;
protected $actionHandler;
protected $taskType;

public function __construct(array $params = []) {
$this->addType('appid', 'string');
$this->addType('name', 'string');
$this->addType('displayName', 'string');
$this->addType('actionHandler', 'string');
$this->addType('taskType', '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'])) {
$this->setActionHandler($params['action_handler']);
}
if (isset($params['task_type'])) {
$this->setTaskType($params['task_type']);
}
}

public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'appid' => $this->getAppid(),
'name' => $this->getName(),
'display_name' => $this->getDisplayName(),
'action_handler' => $this->getActionHandler(),
'task_type' => $this->getTaskType(),
];
}
}
72 changes: 72 additions & 0 deletions lib/Db/TextProcessing/TextProcessingProviderMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace OCA\AppAPI\Db\TextProcessing;

use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;

/**
* @template-extends QBMapper<TextProcessingProvider>
*/
class TextProcessingProviderMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'ex_text_processing');
}

/**
* @throws Exception
*/
public function findAllEnabled(): array {
$qb = $this->db->getQueryBuilder();
$result = $qb->select(
'ex_text_processing.appid',
'ex_text_processing.name',
'ex_text_processing.display_name',
'ex_text_processing.action_handler',
'ex_text_processing.task_type',
)
->from($this->tableName, 'ex_text_processing')
->innerJoin('ex_text_processing', 'ex_apps', 'exa', 'exa.appid = ex_text_processing.appid')
->where(
$qb->expr()->eq('exa.enabled', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT))
)->executeQuery();
return $result->fetchAll();
}

/**
* @param string $appId
* @param string $name
*
* @throws DoesNotExistException
* @throws Exception
* @throws MultipleObjectsReturnedException
*
* @return TextProcessingProvider
*/
public function findByAppidName(string $appId, string $name): TextProcessingProvider {
$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))
);
}

/**
* @throws Exception
*/
public function removeAllByAppId(string $appId): int {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->tableName)
->where(
$qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR))
);
return $qb->executeStatement();
}
}
Loading

0 comments on commit db2a3cf

Please sign in to comment.