Skip to content

Commit

Permalink
add support for Enum field type, fix many issues
Browse files Browse the repository at this point in the history
Signed-off-by: Julien Veyssier <[email protected]>
  • Loading branch information
julien-nc committed Jul 24, 2024
1 parent 43877ee commit c8b6a0b
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 20 deletions.
10 changes: 10 additions & 0 deletions lib/Service/AssistantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ public function getAvailableTaskTypes(): array {
EShapeType::ListOfAudios,
),
],
'inputShapeEnumValues' => [],
'inputShapeDefaults' => [],
'outputShape' => [
'fileList' => new ShapeDescriptor(
'Output file list',
Expand All @@ -157,8 +159,12 @@ public function getAvailableTaskTypes(): array {
EShapeType::Image,
),
],
'outputShapeEnumValues' => [],
'optionalInputShape' => [],
'optionalInputShapeEnumValues' => [],
'optionalInputShapeDefaults' => [],
'optionalOutputShape' => [],
'optionalOutputShapeEnumValues' => [],
];
}
/** @var string $typeId */
Expand All @@ -179,8 +185,12 @@ public function getAvailableTaskTypes(): array {
'name' => $this->l10n->t('Chat with AI'),
'description' => $this->l10n->t('Chat with an AI model.'),
'inputShape' => [],
'inputShapeEnumValues' => [],
'inputShapeDefaults' => [],
'outputShape' => [],
'optionalInputShape' => [],
'optionalInputShapeEnumValues' => [],
'optionalInputShapeDefaults' => [],
'optionalOutputShape' => [],
'priority' => self::TASK_TYPE_PRIORITIES['chatty-llm'] ?? 1000,
];
Expand Down
8 changes: 7 additions & 1 deletion src/assistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export async function openAssistantForm({
} else if (finishedTask.status === TASK_STATUS_STRING.failed) {
showError(t('assistant', 'Your task has failed'))
console.error('[assistant] Task failed', finishedTask)
view.outputs = null
}
resolve(finishedTask)
view.loading = false
Expand All @@ -130,7 +131,7 @@ export async function openAssistantForm({
})
view.$on('load-task', (task) => {
if (!view.loading) {
console.debug('aaaaa loading task', task)
console.debug('[assistant] loading task', task)
view.selectedTaskTypeId = task.taskType
view.inputs = task.input
view.outputs = task.status === TASK_STATUS_STRING.successful ? task.output : null
Expand Down Expand Up @@ -364,12 +365,17 @@ export async function openAssistantTask(task) {
if (finishedTask.status === TASK_STATUS_STRING.successful) {
view.outputs = finishedTask?.output
view.selectedTaskId = finishedTask?.id
} else if (finishedTask.status === TASK_STATUS_STRING.failed) {
showError(t('assistant', 'Your task has failed'))
console.error('[assistant] Task failed', finishedTask)
view.outputs = null
}
// resolve(finishedTask)
view.loading = false
view.showSyncTaskRunning = false
}).catch(error => {
console.debug('[assistant] poll error', error)
view.outputs = null
})
})
.catch(error => {
Expand Down
36 changes: 30 additions & 6 deletions src/components/AssistantFormInputs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
:is-output="false"
:shape="selectedTaskType.inputShape"
:optional-shape="selectedTaskType.optionalInputShape ?? null"
:shape-options="selectedTaskType.inputShapeEnumValues ?? null"
:optional-shape-options="selectedTaskType.optionalInputShapeEnumValues ?? null"
:values="inputs"
:show-advanced="showAdvanced"
@update:show-advanced="$emit('update:show-advanced', $event)"
Expand All @@ -35,6 +37,10 @@ export default {
type: Object,
default: () => {},
},
selectedTaskId: {
type: [Number, null],
default: null,
},
selectedTaskType: {
type: [Object, null],
default: null,
Expand All @@ -60,17 +66,35 @@ export default {
},
},
mounted() {
console.debug('[assistant] mounted AssistantFormInputs', this.selectedTaskId, this.selectedTaskType)
// don't set the default values if there is a loaded task (initial or from history)
if (this.selectedTaskType && this.selectedTaskId === null) {
this.setDefaultValues()
}
},
methods: {
resetInputs() {
this.setDefaultValues()
},
setDefaultValues() {
console.debug('[assistant] set default values', this.selectedTaskType?.inputShapeDefaults, this.selectedTaskType?.optionalInputShapeDefaults)
const inputs = {}
/*
Object.keys(this.selectedTaskType.inputShape).forEach(key => {
inputs[key] = null
})
*/
// set default values
if (this.selectedTaskType.inputShapeDefaults) {
Object.keys(this.selectedTaskType.inputShapeDefaults).forEach(key => {
if (this.selectedTaskType.inputShapeDefaults[key]) {
inputs[key] = this.selectedTaskType.inputShapeDefaults[key]
}
})
}
if (this.selectedTaskType.optionalInputShapeDefaults) {
Object.keys(this.selectedTaskType.optionalInputShapeDefaults).forEach(key => {
if (this.selectedTaskType.optionalInputShapeDefaults[key]) {
inputs[key] = this.selectedTaskType.optionalInputShapeDefaults[key]
}
})
}
this.$emit('update:inputs', inputs)
// TODO do it with optional input shape as well
},
},
}
Expand Down
8 changes: 4 additions & 4 deletions src/components/AssistantTextProcessingForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
</span-->
<AssistantFormInputs v-if="selectedTaskType"
:inputs.sync="myInputs"
:selected-task-id="selectedTaskId"
:selected-task-type="selectedTaskType"
:show-advanced.sync="showAdvanced" />
<div v-if="hasOutput"
Expand All @@ -57,6 +58,8 @@
:is-output="true"
:shape="selectedTaskType.outputShape"
:optional-shape="selectedTaskType.optionalOutputShape ?? null"
:shape-options="selectedTaskType.outputShapeEnumValues ?? null"
:optional-shape-options="selectedTaskType.optionalOutputShapeEnumValues ?? null"
:values.sync="myOutputs"
:show-advanced.sync="showAdvanced" />
<NcNoteCard v-if="outputEqualsInput"
Expand Down Expand Up @@ -265,10 +268,6 @@ export default {
return this.selectedTaskType
},
canSubmit() {
if (this.selectedTaskType.id === 'speech-to-text') {
return (this.myInputs.sttMode === 'record' && this.myInputs.audioData !== null)
|| (this.myInputs.sttMode === 'choose' && this.myInputs.audioFilePath !== null)
}
// otherwise, check that none of the properties of myInputs are empty
console.debug('[assistant] canSubmit', this.myInputs)
if (Object.keys(this.myInputs).length === 0) {
Expand Down Expand Up @@ -400,6 +399,7 @@ export default {
this.myOutputs = null
},
onSyncSubmit() {
console.debug('[assistant] in form submit ---------', this.myInputs)
this.$emit('sync-submit', { inputs: this.myInputs, selectedTaskTypeId: this.mySelectedTaskTypeId })
},
onActionButtonClick(button) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/TaskList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
})
},
onTaskDelete(task) {
const url = generateOcsUrl('taskprocessing/tasks/{id}', { id: task.id })
const url = generateOcsUrl('taskprocessing/task/{id}', { id: task.id })
axios.delete(url).then(response => {
const index = this.tasks.findIndex(t => { return t.id === task.id })
if (index !== -1) {
Expand Down
5 changes: 0 additions & 5 deletions src/components/TaskListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,6 @@ export default {
return this.task.type === 'core:text2image'
},
mainName() {
if (this.task.taskType === 'copywriter') {
return this.task.input.sourceMaterial
} else if (this.task.taskType === 'speech-to-text') {
return t('assistant', 'Audio input')
}
return t('assistant', 'Input') + ': ' + this.textInputPreview
},
subName() {
Expand Down
97 changes: 97 additions & 0 deletions src/components/fields/EnumField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<template>
<div class="number-field">
<label :for="'input-' + fieldKey">
{{ field.description }}
</label>
<NcSelect
:id="'input-' + fieldKey"
class="enum-field"
:value="selectValue"
:options="options"
:clearable="true"
label="name"
:label-outside="true"
:title="field.name"
:placeholder="field.placeholder ?? (field.description || t('assistant','Choose a value'))"
:no-wrap="false"
@input="onUpdateValue" />
</div>
</template>
<script>
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
export default {
name: 'EnumField',
components: {
NcSelect,
},
props: {
fieldKey: {
type: String,
required: true,
},
value: {
type: [String, null],
default: null,
},
field: {
type: Object,
required: true,
},
options: {
type: Array,
required: true,
},
},
emits: [
'update:value',
],
data() {
return {
}
},
computed: {
isValid() {
return this.value === null || this.value === '' || typeof this.value === 'number'
},
selectValue() {
return this.options.find(option => option.value === this.value)
},
},
watch: {
},
mounted() {
},
methods: {
onUpdateValue(newValue) {
if (newValue === null) {
this.$emit('update:value', undefined)
} else {
this.$emit('update:value', newValue.value)
}
},
},
}
</script>
<style lang="scss">
.number-field {
display: flex;
flex-direction: column;
align-items: start;
.number-input-field {
width: 300px !important;
margin-top: 0 !important;
}
}
</style>
8 changes: 8 additions & 0 deletions src/components/fields/TaskTypeField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:field-key="fieldKey"
:value="value"
:field="field"
:options="options ?? undefined"
:is-output="isOutput"
@update:value="$emit('update:value', $event)" />
</template>
Expand All @@ -12,6 +13,7 @@
import TextField from './TextField.vue'
import NumberField from './NumberField.vue'
import MediaField from './MediaField.vue'
import EnumField from './EnumField.vue'
import ListOfMediaField from './ListOfMediaField.vue'
import ListOfTextsField from './ListOfTextsField.vue'
Expand All @@ -36,6 +38,10 @@ export default {
type: Object,
required: true,
},
options: {
type: [Array, null],
default: null,
},
isOutput: {
type: Boolean,
required: true,
Expand Down Expand Up @@ -79,6 +85,8 @@ export default {
return ListOfMediaField
} else if (this.field.type === SHAPE_TYPE_NAMES.ListOfTexts) {
return ListOfTextsField
} else if (this.field.type === SHAPE_TYPE_NAMES.Enum) {
return EnumField
}
return TextField
},
Expand Down
36 changes: 34 additions & 2 deletions src/components/fields/TaskTypeFields.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
:field-key="key"
:field="field"
:value="values[key] ?? null"
:options="getInputFieldOptions(field, key)"
:is-output="isOutput"
@update:value="onValueChange(key, $event)" />
<!--NcButton v-if="hasOptionalShape"
Expand All @@ -22,6 +23,7 @@
:field-key="key"
:field="field"
:value="values[key] ?? null"
:options="getOptionalInputFieldOptions(field, key)"
:is-output="isOutput"
@update:value="onValueChange(key, $event)" />
</div>
Expand Down Expand Up @@ -51,6 +53,14 @@ export default {
type: [Object, Array, null],
default: () => {},
},
shapeOptions: {
type: [Object, Array, null],
default: null,
},
optionalShapeOptions: {
type: [Object, Array, null],
default: null,
},
isOutput: {
type: Boolean,
required: true,
Expand Down Expand Up @@ -91,11 +101,33 @@ export default {
},
methods: {
getInputFieldOptions(field, key) {
if (field.type === 'Enum'
&& this.shapeOptions !== null
&& !Array.isArray(this.shapeOptions)
&& this.shapeOptions[key]
) {
return this.shapeOptions[key]
}
return undefined
},
getOptionalInputFieldOptions(field, key) {
if (field.type === 'Enum'
&& this.optionalShapeOptions !== null
&& !Array.isArray(this.optionalShapeOptionsshapeOptions)
&& this.optionalShapeOptions[key]
) {
return this.optionalShapeOptions[key]
}
return undefined
},
onValueChange(key, value) {
this.$emit('update:values', {
const newValues = {
...this.values,
[key]: value,
})
}
console.debug('[assistant] field value change', newValues)
this.$emit('update:values', newValues)
},
},
}
Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const SHAPE_TYPE_NAMES = {
Audio: 'Audio',
Video: 'Video',
File: 'File',
Enum: 'Enum',
ListOfNumbers: 'ListOfNumbers',
ListOfTexts: 'ListOfTexts',
ListOfImages: 'ListOfImages',
Expand Down
Loading

0 comments on commit c8b6a0b

Please sign in to comment.