feat: show file upload errors in knowledge base

pick 96d4f3c # chore: update data

When a file gets uploaded, the error not just shown in the toast
which is easy to miss, but the faulty file is kept in the knowledge
base and the error is visible there.
This commit is contained in:
guenhter 2026-07-21 16:11:49 +02:00
parent 86efecd9ad
commit e77fd72692
4 changed files with 64 additions and 9 deletions

View file

@ -161,6 +161,11 @@ async def process_uploaded_file(
{'status': 'completed'},
db=db_session,
)
await Files.update_file_metadata_by_id(
file_item.id,
{'status': 'completed'},
db=db_session,
)
else:
raise Exception(f'File type {content_type} is not supported for processing')
@ -204,12 +209,8 @@ async def process_uploaded_file(
# Keep the generic file status stream open until the
# KB-specific vector write and durable link both finish.
await Files.update_file_data_by_id(file_item.id, {'status': 'processing'}, db=db_session)
await process_file(
request,
ProcessFileForm(file_id=file_item.id, collection_name=knowledge_id),
user=user,
db=db_session,
)
await Files.update_file_metadata_by_id(file_item.id, {'status': 'processing'}, db=db_session)
knowledge_file = await Knowledges.add_file_to_knowledge_by_id(
knowledge_id=knowledge_id,
file_id=file_item.id,
@ -217,6 +218,12 @@ async def process_uploaded_file(
directory_id=file_metadata.get('directory_id'),
db=db_session,
)
await process_file(
request,
ProcessFileForm(file_id=file_item.id, collection_name=knowledge_id),
user=user,
db=db_session,
)
if not knowledge_file:
raise Exception(f'Failed to link file {file_item.id} to knowledge {knowledge_id}')
log.info(f'Linked file {file_item.id} to knowledge {knowledge_id}')
@ -234,6 +241,14 @@ async def process_uploaded_file(
},
db=db_session,
)
await Files.update_file_metadata_by_id(
file_item.id,
{
'status': 'failed',
'error': str(e.detail) if hasattr(e, 'detail') else str(e),
},
db=db_session,
)
try:
if db:

View file

@ -1944,6 +1944,8 @@ async def process_file(
if config.BYPASS_EMBEDDING_AND_RETRIEVAL:
await Files.update_file_data_by_id(file.id, {'status': 'completed'}, db=db)
await Files.update_file_metadata_by_id(file.id, {'status': 'completed'}, db=db)
await Files.update_file_hash_by_id(file.id, hash, db=db)
await publish_event(
request,
@ -1993,6 +1995,7 @@ async def process_file(
await Files.update_file_metadata_by_id(
file.id,
{
'status': 'completed',
'collection_name': collection_name,
},
db=session,
@ -2033,6 +2036,14 @@ async def process_file(
{'status': 'failed'},
db=session,
)
await Files.update_file_metadata_by_id(
file.id,
{
'status': 'failed',
'error': str(e.detail) if hasattr(e, 'detail') else str(e),
},
db=session,
)
# Clear the hash so the file can be re-uploaded after fixing the issue
await Files.update_file_hash_by_id(file.id, None, db=session)

View file

@ -366,6 +366,7 @@
console.warn('File upload warning:', uploadedFile.error);
toast.warning(uploadedFile.error);
fileItems = fileItems.filter((file) => file.id !== uploadedFile.id);
init();
} else {
toast.success($i18n.t('File added successfully.'));
init();
@ -454,6 +455,7 @@
console.warn('File upload warning:', uploadedFile.error);
toast.warning(uploadedFile.error);
fileItems = fileItems.filter((file) => file.id !== uploadedFile.id);
init();
} else {
toast.success($i18n.t('File added successfully.'));
init();

View file

@ -22,6 +22,7 @@
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
import Pencil from '$lib/components/icons/Pencil.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
import XMark from '$lib/components/icons/XMark.svelte';
import DirectoryRow from './DirectoryRow.svelte';
export let knowledge = null;
@ -89,7 +90,35 @@
}}
>
<div class="flex items-center">
{#if file?.status !== 'uploading'}
{#if file?.status === 'uploading'}
<Spinner className="size-3.5" />
{:else if file?.meta?.status === 'failed'}
{#if file?.meta?.error}
<Tooltip content={file.meta.error}>
<button
class="p-1 rounded-full transition"
type="button"
on:click={() => {
let fileId = file?.id ?? file?.tempId;
onClick(fileId);
}}
>
<XMark className="size-3.5 text-red-500" />
</button>
</Tooltip>
{:else}
<button
class="p-1 rounded-full transition"
type="button"
on:click={() => {
let fileId = file?.id ?? file?.tempId;
onClick(fileId);
}}
>
<XMark className="size-3.5 text-red-500" />
</button>
{/if}
{:else}
<button
class="p-1 rounded-full transition"
type="button"
@ -100,8 +129,6 @@
>
<DocumentPage className="size-3.5" />
</button>
{:else}
<Spinner className="size-3.5" />
{/if}
</div>