From 6fc741a0d3e9ce35d5c8826939e37000f1ca91a6 Mon Sep 17 00:00:00 2001 From: Lukas Schaefer Date: Wed, 28 Jan 2026 12:13:53 -0500 Subject: [PATCH] Expose task categories in getAvailableTaskTypes Signed-off-by: Lukas Schaefer --- lib/ResponseDefinitions.php | 1 + lib/Service/AssistantService.php | 43 +++++++++++++++++++++++++- openapi.json | 18 ++++++++++- src/components/TaskTypeSelect.vue | 50 +++++-------------------------- 4 files changed, 68 insertions(+), 44 deletions(-) diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index 7df97af5..be72deca 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -29,6 +29,7 @@ * outputShape: array, * optionalOutputShape: array, * priority: integer, + * category: array{id: string, name: string}, * } * * @psalm-type AssistantTaskProcessingTask = array{ diff --git a/lib/Service/AssistantService.php b/lib/Service/AssistantService.php index 7376546e..8bfa127a 100644 --- a/lib/Service/AssistantService.php +++ b/lib/Service/AssistantService.php @@ -1,7 +1,7 @@ l10n->t('Other'); + if (str_starts_with($typeId, 'chatty')) { + $categoryId = 'chat'; + $categoryName = $this->l10n->t('Chat with AI'); + } elseif (str_starts_with($typeId, 'context_chat')) { + $categoryId = 'context'; + $categoryName = $this->l10n->t('Context Chat'); + } elseif (str_contains($typeId, 'translate')) { + $categoryId = 'translate'; + $categoryName = $this->l10n->t('Translate'); + } elseif (str_starts_with($typeId, 'richdocuments')) { + $categoryId = 'generate'; + $categoryName = $this->l10n->t('Generate file'); + } elseif (str_contains($typeId, 'image') || str_contains($typeId, 'sticker')) { + $categoryId = 'image'; + $categoryName = $this->l10n->t('Work with images'); + } elseif (str_contains($typeId, 'audio') || str_contains($typeId, 'speech')) { + $categoryId = 'audio'; + $categoryName = $this->l10n->t('Work with audio'); + } elseif (str_contains($typeId, 'text')) { + $categoryId = 'text'; + $categoryName = $this->l10n->t('Work with text'); + } + + return [ + 'id' => $categoryId, + 'name' => $categoryName, + ]; + } + /** * @return array */ @@ -245,6 +283,7 @@ public function getAvailableTaskTypes(): array { 'description' => 'plop', 'id' => 'core:inputList', 'priority' => 0, + 'category' => $this->getCategory('core:inputList'), 'inputShape' => [ 'textList' => new ShapeDescriptor( 'Input text list', @@ -333,6 +372,7 @@ public function getAvailableTaskTypes(): array { 'id' => 'chatty-llm', 'name' => $this->l10n->t('Chat with AI'), 'description' => $this->l10n->t('Chat with an AI model.'), + 'category' => $this->getCategory('chatty-llm'), 'inputShape' => [], 'inputShapeEnumValues' => [], 'inputShapeDefaults' => [], @@ -350,6 +390,7 @@ public function getAvailableTaskTypes(): array { } $taskTypeArray['id'] = $typeId; $taskTypeArray['priority'] = self::TASK_TYPE_PRIORITIES[$typeId] ?? 1000; + $taskTypeArray['category'] = $this->getCategory($typeId); if ($typeId === TextToText::ID) { $taskTypeArray['name'] = $this->l10n->t('Generate text'); diff --git a/openapi.json b/openapi.json index 235a5539..851338be 100644 --- a/openapi.json +++ b/openapi.json @@ -432,7 +432,8 @@ "optionalInputShape", "outputShape", "optionalOutputShape", - "priority" + "priority", + "category" ], "properties": { "id": { @@ -471,6 +472,21 @@ "priority": { "type": "integer", "format": "int64" + }, + "category": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } } } } diff --git a/src/components/TaskTypeSelect.vue b/src/components/TaskTypeSelect.vue index e572f2d3..bf05d00d 100644 --- a/src/components/TaskTypeSelect.vue +++ b/src/components/TaskTypeSelect.vue @@ -140,7 +140,7 @@ export default { buttonTypes() { const taskTypes = {} for (const task of this.options) { - const type = this.getTaskCategory(task.id) + const type = task.category.id if (!taskTypes[type]) { taskTypes[type] = [] } @@ -153,7 +153,7 @@ export default { } result.push({ id: entry[0], - text: this.getTextForCategory(entry[0]), + text: entry[1][0].category.name, icon: this.getCategoryIcon(entry[0]), tasks: entry[1], }) @@ -162,7 +162,7 @@ export default { if (taskTypes.other) { result.push({ id: 'other', - text: this.getTextForCategory('other'), + text: taskTypes.other[0].category.name, icon: this.getCategoryIcon('other'), tasks: taskTypes.other, }) @@ -201,7 +201,11 @@ export default { return taskType.id === this.modelValue }, isCategorySelected(category) { - return category.id === this.getTaskCategory(this.modelValue || '') + if (!this.modelValue) { + return false + } + const selectedTask = this.options.find(task => task.id === this.modelValue) + return selectedTask && category.id === selectedTask.category.id }, onTaskSelected(taskType) { this.$emit('update:model-value', taskType.id) @@ -217,44 +221,6 @@ export default { this.categorySubmenu = null } }, - getTaskCategory(id) { - if (id.startsWith('chatty')) { - return 'chat' - } else if (id.startsWith('context_chat')) { - return 'context' - } else if (id.includes('translate')) { - return 'translate' - } else if (id.startsWith('richdocuments')) { - return 'generate' - } else if (id.includes('image') || id.includes('sticker')) { - return 'image' - } else if (id.includes('audio') || id.includes('speech')) { - return 'audio' - } else if (id.includes('text')) { - return 'text' - } - return 'other' - }, - getTextForCategory(category) { - switch (category) { - case 'chat': - return t('assistant', 'Chat with AI') - case 'context': - return t('assistant', 'Context Chat') - case 'text': - return t('assistant', 'Work with text') - case 'image': - return t('assistant', 'Work with images') - case 'translate': - return t('assistant', 'Translate') - case 'audio': - return t('assistant', 'Work with audio') - case 'generate': - return t('assistant', 'Generate file') - default: - return t('assistant', 'Other') - } - }, getCategoryIcon(category) { switch (category) { case 'chat':