From a6ed273a135b8820b74cfeecd71fb869a2a7d5b9 Mon Sep 17 00:00:00 2001 From: Viswamedha Nalabotu Date: Sun, 22 Mar 2026 19:45:05 +0000 Subject: [PATCH] Added error field for training file with UI message --- apps/knowledge/migrations/0001_initial.py | 1 + apps/knowledge/models.py | 1 + apps/knowledge/serializers.py | 6 +++--- apps/knowledge/tasks.py | 2 +- site/src/types/organization.ts | 1 + site/src/views/OrganizationManage.vue | 12 ++++++++---- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/knowledge/migrations/0001_initial.py b/apps/knowledge/migrations/0001_initial.py index 37e3e70..11f0d7f 100644 --- a/apps/knowledge/migrations/0001_initial.py +++ b/apps/knowledge/migrations/0001_initial.py @@ -28,6 +28,7 @@ class Migration(migrations.Migration): ('file_size', models.IntegerField()), ('file_type', models.CharField(max_length=50)), ('description', models.TextField(blank=True, default='')), + ('error_message', models.TextField(blank=True, default='')), ('status', models.CharField(choices=[('ingesting', 'Ingesting'), ('chunked', 'Chunked'), ('embedded', 'Embedded'), ('failed', 'Failed')], default='ingesting', max_length=20)), ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='training_files', to='accounts.organization')), ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='training_files', to='accounts.role')), diff --git a/apps/knowledge/models.py b/apps/knowledge/models.py index b6ec687..23f9289 100644 --- a/apps/knowledge/models.py +++ b/apps/knowledge/models.py @@ -29,6 +29,7 @@ class TrainingFile(IdentifierMixin, TimeStampMixin, Model): file_type = CharField(max_length=50) description = TextField(blank=True, default='') + error_message = TextField(blank=True, default='') status = CharField(max_length=20, choices=STATUS_CHOICES, default='ingesting') class Meta: diff --git a/apps/knowledge/serializers.py b/apps/knowledge/serializers.py index 2361dd3..4145001 100644 --- a/apps/knowledge/serializers.py +++ b/apps/knowledge/serializers.py @@ -14,12 +14,12 @@ class TrainingFileSerializer(ModelSerializer): model = TrainingFile fields = [ 'id', 'uuid', 'organization', 'role', 'scope', 'uploaded_by', 'file', 'file_url', - 'file_name', 'file_size', 'file_type', 'description', - 'status', 'created_at', 'updated_at' + 'file_name', 'file_size', 'file_type', 'description', + 'error_message', 'status', 'created_at', 'updated_at' ] read_only_fields = [ 'id', 'uuid', 'uploaded_by', 'file_size', 'file_type', - 'status', 'created_at', 'updated_at', + 'error_message', 'status', 'created_at', 'updated_at', 'organization', 'role', 'scope' ] diff --git a/apps/knowledge/tasks.py b/apps/knowledge/tasks.py index 59ca63c..9b65119 100644 --- a/apps/knowledge/tasks.py +++ b/apps/knowledge/tasks.py @@ -115,7 +115,7 @@ def ingest_training_file_task(self, file_uuid): except Exception as e: file_obj.status = 'failed' - file_obj.description = str(e) + file_obj.error_message = str(e) file_obj.save() raise e diff --git a/site/src/types/organization.ts b/site/src/types/organization.ts index a930766..f5cd62d 100644 --- a/site/src/types/organization.ts +++ b/site/src/types/organization.ts @@ -44,6 +44,7 @@ export interface TrainingFile { file_size: number file_type: string description: string + error_message: string is_processed: boolean status: 'ingesting' | 'chunked' | 'embedded' | 'failed' file_url: string diff --git a/site/src/views/OrganizationManage.vue b/site/src/views/OrganizationManage.vue index 182c1c4..e6abf33 100644 --- a/site/src/views/OrganizationManage.vue +++ b/site/src/views/OrganizationManage.vue @@ -18,6 +18,7 @@ import { Upload, Steps, Table, + Tooltip, } from 'ant-design-vue' import { apiClient, isAxiosError, API } from '../router/api' import { useUserStore } from '../stores/userStore' @@ -324,17 +325,20 @@ const trainingFileColumns = [ }, { title: 'Status', - dataIndex: 'status', key: 'status', - customRender: ({ value }: { value: string }) => { + customRender: ({ record }: { record: TrainingFile }) => { const statusMap: Record = { ingesting: { color: 'processing', label: 'Ingesting' }, chunked: { color: 'blue', label: 'Chunked' }, embedded: { color: 'success', label: 'Embedded' }, failed: { color: 'error', label: 'Failed' }, } - const status = statusMap[value] || { color: 'default', label: value } - return h(Tag, { color: status.color }, () => status.label) + const status = statusMap[record.status] || { color: 'default', label: record.status } + const tag = h(Tag, { color: status.color }, () => status.label) + if (record.status === 'failed' && record.error_message) { + return h(Tooltip, { title: record.error_message }, () => tag) + } + return tag }, }, {