Skip to content

Commit edb9f2e

Browse files
julien-ncelzody
authored andcommitted
move task processing provider and task type from https://github.com/nextcloud/slide_deck_generator
Signed-off-by: Julien Veyssier <[email protected]>
1 parent 69982f6 commit edb9f2e

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
use OCA\Richdocuments\Preview\OpenDocument;
3333
use OCA\Richdocuments\Preview\Pdf;
3434
use OCA\Richdocuments\Reference\OfficeTargetReferenceProvider;
35+
use OCA\Richdocuments\TaskProcessing\SlideDeckGenerationProvider;
36+
use OCA\Richdocuments\TaskProcessing\SlideDeckGenerationTaskType;
3537
use OCA\Richdocuments\Template\CollaboraTemplateProvider;
3638
use OCA\Viewer\Event\LoadViewer;
3739
use OCP\AppFramework\App;
@@ -84,6 +86,8 @@ public function register(IRegistrationContext $context): void {
8486
$context->registerPreviewProvider(Pdf::class, Pdf::MIMETYPE_REGEX);
8587
$context->registerFileConversionProvider(ConversionProvider::class);
8688
$context->registerNotifierService(Notifier::class);
89+
$context->registerTaskProcessingProvider(SlideDeckGenerationProvider::class);
90+
$context->registerTaskProcessingTaskType(SlideDeckGenerationTaskType::class);
8791
}
8892

8993
public function boot(IBootContext $context): void {

lib/Service/SlideDeckService.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
4+
* SPDX-License-Identifier: AGPL-3.0-or-later
5+
*/
6+
7+
namespace OCA\Richdocuments\Service;
8+
9+
use OCA\Richdocuments\AppInfo\Application;
10+
use OCP\TaskProcessing\Exception\Exception;
11+
use OCP\TaskProcessing\Exception\NotFoundException;
12+
use OCP\TaskProcessing\Exception\PreConditionNotMetException;
13+
use OCP\TaskProcessing\Exception\UnauthorizedException;
14+
use OCP\TaskProcessing\Exception\ValidationException;
15+
use OCP\TaskProcessing\IManager;
16+
use OCP\TaskProcessing\Task;
17+
use OCP\TaskProcessing\TaskTypes\TextToText;
18+
use RuntimeException;
19+
20+
class SlideDeckService {
21+
public const PROMPT = <<<EOF
22+
Draft a presentation slide deck with headlines and a maximum of 5 bullet points per headline. Use the following JSON structure for your whole output and output only the JSON array, no introductory text:
23+
24+
```
25+
[{"headline": "Headline 1", points: ["Bullet point 1", "Bullet point 2"]}, {"headline": "Headline 2", points: ["Bullet point 1", "Bullet point 2"]}]
26+
```
27+
28+
Here is the presentation text:
29+
EOF;
30+
31+
public function __construct(
32+
private IManager $taskProcessingManager,
33+
) {
34+
}
35+
36+
public function generateSlideDeck(?string $userId, string $presentationText) {
37+
$prompt = self::PROMPT;
38+
$task = new Task(
39+
TextToText::ID,
40+
['input' => $prompt . "\n\n" . $presentationText],
41+
Application::APPNAME,
42+
$userId
43+
);
44+
try {
45+
$this->taskProcessingManager->scheduleTask($task);
46+
} catch (PreConditionNotMetException|UnauthorizedException|ValidationException|Exception $e) {
47+
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
48+
}
49+
while (true) {
50+
try {
51+
$task = $this->taskProcessingManager->getTask($task->getId());
52+
} catch (NotFoundException|Exception $e) {
53+
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
54+
}
55+
if (in_array($task->getStatus(), [Task::STATUS_SUCCESSFUL, Task::STATUS_FAILED, Task::STATUS_CANCELLED])) {
56+
break;
57+
}
58+
}
59+
if ($task->getStatus() !== Task::STATUS_SUCCESSFUL) {
60+
throw new RuntimeException('LLM backend Task with id ' . $task->getId() . ' failed or was cancelled');
61+
}
62+
63+
$output = $task->getOutput();
64+
if (isset($output['output'])) {
65+
throw new RuntimeException('LLM backend Task with id ' . $task->getId() . ' does not have output key');
66+
}
67+
68+
$headlines = json_decode($output['output'], associative: true);
69+
70+
return $output['output'];
71+
}
72+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
/**
3+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
4+
* SPDX-License-Identifier: AGPL-3.0-or-later
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace OCA\Richdocuments\TaskProcessing;
10+
11+
use OCA\Richdocuments\AppInfo\Application;
12+
use OCA\Richdocuments\Service\SlideDeckService;
13+
use OCP\IL10N;
14+
use OCP\TaskProcessing\ISynchronousProvider;
15+
16+
class SlideDeckGenerationProvider implements ISynchronousProvider {
17+
18+
public function __construct(
19+
private SlideDeckService $slideDeckService,
20+
private IL10N $l10n,
21+
) {
22+
}
23+
24+
public function getId(): string {
25+
return Application::APPNAME . '-slide_deck_generator';
26+
}
27+
28+
public function getName(): string {
29+
return $this->l10n->t('Nextcloud Assistant Slide Deck Generator');
30+
}
31+
32+
public function getTaskTypeId(): string {
33+
return SlideDeckGenerationTaskType::ID;
34+
}
35+
36+
public function getExpectedRuntime(): int {
37+
return 120;
38+
}
39+
40+
public function getInputShapeEnumValues(): array {
41+
return [];
42+
}
43+
44+
public function getInputShapeDefaults(): array {
45+
return [];
46+
}
47+
48+
public function getOptionalInputShape(): array {
49+
return [];
50+
}
51+
52+
public function getOptionalInputShapeEnumValues(): array {
53+
return [];
54+
}
55+
56+
public function getOptionalInputShapeDefaults(): array {
57+
return [];
58+
}
59+
60+
public function getOutputShapeEnumValues(): array {
61+
return [];
62+
}
63+
64+
public function getOptionalOutputShape(): array {
65+
return [];
66+
}
67+
68+
public function getOptionalOutputShapeEnumValues(): array {
69+
return [];
70+
}
71+
/**
72+
* @inheritDoc
73+
*/
74+
public function process(?string $userId, array $input, callable $reportProgress): array {
75+
if ($userId === null) {
76+
throw new \RuntimeException('User ID is required to process the prompt.');
77+
}
78+
79+
if (!isset($input['text']) || !is_string($input['text'])) {
80+
throw new \RuntimeException('Invalid input, expected "text" key with string value');
81+
}
82+
83+
$response = $this->slideDeckService->generateSlideDeck(
84+
$userId,
85+
$input['text'],
86+
);
87+
88+
return $response;
89+
}
90+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
4+
* SPDX-License-Identifier: AGPL-3.0-or-later
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace OCA\Richdocuments\TaskProcessing;
10+
11+
use OCA\Richdocuments\AppInfo\Application;
12+
use OCP\IL10N;
13+
use OCP\TaskProcessing\EShapeType;
14+
use OCP\TaskProcessing\ITaskType;
15+
use OCP\TaskProcessing\ShapeDescriptor;
16+
17+
class SlideDeckGenerationTaskType implements ITaskType {
18+
public const ID = Application::APPNAME . ':slide_deck_generation';
19+
20+
public function __construct(
21+
private IL10N $l,
22+
) {
23+
}
24+
25+
/**
26+
* @inheritDoc
27+
*/
28+
public function getName(): string {
29+
return $this->l->t('Generate Slide Deck');
30+
}
31+
32+
/**
33+
* @inheritDoc
34+
*/
35+
public function getDescription(): string {
36+
return $this->l->t('Generate a slide deck from a presentation script');
37+
}
38+
39+
/**
40+
* @return string
41+
*/
42+
public function getId(): string {
43+
return self::ID;
44+
}
45+
46+
/**
47+
* @return ShapeDescriptor[]
48+
*/
49+
public function getInputShape(): array {
50+
return [
51+
'text' => new ShapeDescriptor(
52+
$this->l->t('Presentation script'),
53+
$this->l->t('Write the text for your presentation here'),
54+
EShapeType::Text,
55+
),
56+
];
57+
}
58+
59+
/**
60+
* @return ShapeDescriptor[]
61+
*/
62+
public function getOutputShape(): array {
63+
return [
64+
'slide_deck' => new ShapeDescriptor(
65+
$this->l->t('Generated slide deck'),
66+
$this->l->t('The slide deck generated'),
67+
EShapeType::Text,
68+
),
69+
];
70+
}
71+
}

0 commit comments

Comments
 (0)