Skip to content

Commit 3e28cfe

Browse files
authored
Merge pull request #450 from nextcloud/expose-categories
Expose task categories in getAvailableTaskTypes
2 parents 7240d6c + 6fc741a commit 3e28cfe

4 files changed

Lines changed: 68 additions & 44 deletions

File tree

lib/ResponseDefinitions.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* outputShape: array<string, AssistantShapeDescriptor>,
3030
* optionalOutputShape: array<string, AssistantShapeDescriptor>,
3131
* priority: integer,
32+
* category: array{id: string, name: string},
3233
* }
3334
*
3435
* @psalm-type AssistantTaskProcessingTask = array{

lib/Service/AssistantService.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
/**
4-
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
4+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
55
* SPDX-License-Identifier: AGPL-3.0-or-later
66
*/
77

@@ -233,6 +233,44 @@ public function isAudioChatAvailable(): bool {
233233
&& array_key_exists(TextToTextChat::ID, $availableTaskTypes);
234234
}
235235

236+
/**
237+
* Get category information for a task type
238+
*
239+
* @param string $typeId The task type ID
240+
* @return array{id: string, name: string}
241+
*/
242+
private function getCategory(string $typeId): array {
243+
$categoryId = 'other';
244+
$categoryName = $this->l10n->t('Other');
245+
if (str_starts_with($typeId, 'chatty')) {
246+
$categoryId = 'chat';
247+
$categoryName = $this->l10n->t('Chat with AI');
248+
} elseif (str_starts_with($typeId, 'context_chat')) {
249+
$categoryId = 'context';
250+
$categoryName = $this->l10n->t('Context Chat');
251+
} elseif (str_contains($typeId, 'translate')) {
252+
$categoryId = 'translate';
253+
$categoryName = $this->l10n->t('Translate');
254+
} elseif (str_starts_with($typeId, 'richdocuments')) {
255+
$categoryId = 'generate';
256+
$categoryName = $this->l10n->t('Generate file');
257+
} elseif (str_contains($typeId, 'image') || str_contains($typeId, 'sticker')) {
258+
$categoryId = 'image';
259+
$categoryName = $this->l10n->t('Work with images');
260+
} elseif (str_contains($typeId, 'audio') || str_contains($typeId, 'speech')) {
261+
$categoryId = 'audio';
262+
$categoryName = $this->l10n->t('Work with audio');
263+
} elseif (str_contains($typeId, 'text')) {
264+
$categoryId = 'text';
265+
$categoryName = $this->l10n->t('Work with text');
266+
}
267+
268+
return [
269+
'id' => $categoryId,
270+
'name' => $categoryName,
271+
];
272+
}
273+
236274
/**
237275
* @return array<AssistantTaskProcessingTaskType>
238276
*/
@@ -246,6 +284,7 @@ public function getAvailableTaskTypes(): array {
246284
'description' => 'plop',
247285
'id' => 'core:inputList',
248286
'priority' => 0,
287+
'category' => $this->getCategory('core:inputList'),
249288
'inputShape' => [
250289
'textList' => new ShapeDescriptor(
251290
'Input text list',
@@ -334,6 +373,7 @@ public function getAvailableTaskTypes(): array {
334373
'id' => 'chatty-llm',
335374
'name' => $this->l10n->t('Chat with AI'),
336375
'description' => $this->l10n->t('Chat with an AI model.'),
376+
'category' => $this->getCategory('chatty-llm'),
337377
'inputShape' => [],
338378
'inputShapeEnumValues' => [],
339379
'inputShapeDefaults' => [],
@@ -351,6 +391,7 @@ public function getAvailableTaskTypes(): array {
351391
}
352392
$taskTypeArray['id'] = $typeId;
353393
$taskTypeArray['priority'] = self::TASK_TYPE_PRIORITIES[$typeId] ?? 1000;
394+
$taskTypeArray['category'] = $this->getCategory($typeId);
354395

355396
if ($typeId === TextToText::ID) {
356397
$taskTypeArray['name'] = $this->l10n->t('Generate text');

openapi.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@
432432
"optionalInputShape",
433433
"outputShape",
434434
"optionalOutputShape",
435-
"priority"
435+
"priority",
436+
"category"
436437
],
437438
"properties": {
438439
"id": {
@@ -471,6 +472,21 @@
471472
"priority": {
472473
"type": "integer",
473474
"format": "int64"
475+
},
476+
"category": {
477+
"type": "object",
478+
"required": [
479+
"id",
480+
"name"
481+
],
482+
"properties": {
483+
"id": {
484+
"type": "string"
485+
},
486+
"name": {
487+
"type": "string"
488+
}
489+
}
474490
}
475491
}
476492
}

src/components/TaskTypeSelect.vue

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export default {
140140
buttonTypes() {
141141
const taskTypes = {}
142142
for (const task of this.options) {
143-
const type = this.getTaskCategory(task.id)
143+
const type = task.category.id
144144
if (!taskTypes[type]) {
145145
taskTypes[type] = []
146146
}
@@ -153,7 +153,7 @@ export default {
153153
}
154154
result.push({
155155
id: entry[0],
156-
text: this.getTextForCategory(entry[0]),
156+
text: entry[1][0].category.name,
157157
icon: this.getCategoryIcon(entry[0]),
158158
tasks: entry[1],
159159
})
@@ -162,7 +162,7 @@ export default {
162162
if (taskTypes.other) {
163163
result.push({
164164
id: 'other',
165-
text: this.getTextForCategory('other'),
165+
text: taskTypes.other[0].category.name,
166166
icon: this.getCategoryIcon('other'),
167167
tasks: taskTypes.other,
168168
})
@@ -201,7 +201,11 @@ export default {
201201
return taskType.id === this.modelValue
202202
},
203203
isCategorySelected(category) {
204-
return category.id === this.getTaskCategory(this.modelValue || '')
204+
if (!this.modelValue) {
205+
return false
206+
}
207+
const selectedTask = this.options.find(task => task.id === this.modelValue)
208+
return selectedTask && category.id === selectedTask.category.id
205209
},
206210
onTaskSelected(taskType) {
207211
this.$emit('update:model-value', taskType.id)
@@ -217,44 +221,6 @@ export default {
217221
this.categorySubmenu = null
218222
}
219223
},
220-
getTaskCategory(id) {
221-
if (id.startsWith('chatty')) {
222-
return 'chat'
223-
} else if (id.startsWith('context_chat')) {
224-
return 'context'
225-
} else if (id.includes('translate')) {
226-
return 'translate'
227-
} else if (id.startsWith('richdocuments')) {
228-
return 'generate'
229-
} else if (id.includes('image') || id.includes('sticker')) {
230-
return 'image'
231-
} else if (id.includes('audio') || id.includes('speech')) {
232-
return 'audio'
233-
} else if (id.includes('text')) {
234-
return 'text'
235-
}
236-
return 'other'
237-
},
238-
getTextForCategory(category) {
239-
switch (category) {
240-
case 'chat':
241-
return t('assistant', 'Chat with AI')
242-
case 'context':
243-
return t('assistant', 'Context Chat')
244-
case 'text':
245-
return t('assistant', 'Work with text')
246-
case 'image':
247-
return t('assistant', 'Work with images')
248-
case 'translate':
249-
return t('assistant', 'Translate')
250-
case 'audio':
251-
return t('assistant', 'Work with audio')
252-
case 'generate':
253-
return t('assistant', 'Generate file')
254-
default:
255-
return t('assistant', 'Other')
256-
}
257-
},
258224
getCategoryIcon(category) {
259225
switch (category) {
260226
case 'chat':

0 commit comments

Comments
 (0)