diff --git a/backend/open_webui/apps/retrieval/main.py b/backend/open_webui/apps/retrieval/main.py index 8052d1123..bbc12f22f 100644 --- a/backend/open_webui/apps/retrieval/main.py +++ b/backend/open_webui/apps/retrieval/main.py @@ -709,7 +709,7 @@ def save_docs_to_vector_db( } for idx, text in enumerate(texts) ] - + VECTOR_DB_CLIENT.insert( collection_name=collection_name, items=items, diff --git a/backend/open_webui/apps/webui/routers/knowledge.py b/backend/open_webui/apps/webui/routers/knowledge.py index 1fb23b40b..821f02ed1 100644 --- a/backend/open_webui/apps/webui/routers/knowledge.py +++ b/backend/open_webui/apps/webui/routers/knowledge.py @@ -196,15 +196,56 @@ def add_file_to_knowledge_by_id( ) +@router.post("/{id}/file/update", response_model=Optional[KnowledgeFilesResponse]) +def update_file_from_knowledge_by_id( + id: str, + form_data: KnowledgeFileIdForm, + user=Depends(get_admin_user), +): + knowledge = Knowledges.get_knowledge_by_id(id=id) + file = Files.get_file_by_id(form_data.file_id) + if not file: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.NOT_FOUND, + ) + + # Remove content from the vector database + VECTOR_DB_CLIENT.delete( + collection_name=knowledge.id, filter={"file_id": form_data.file_id} + ) + + # Add content to the vector database + try: + process_file(ProcessFileForm(file_id=form_data.file_id, collection_name=id)) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e), + ) + + if knowledge: + data = knowledge.data or {} + file_ids = data.get("file_ids", []) + + files = Files.get_files_by_ids(file_ids) + + return KnowledgeFilesResponse( + **knowledge.model_dump(), + files=files, + ) + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.NOT_FOUND, + ) + + ############################ # RemoveFileFromKnowledge ############################ -class KnowledgeFileIdForm(BaseModel): - file_id: str - - @router.post("/{id}/file/remove", response_model=Optional[KnowledgeFilesResponse]) def remove_file_from_knowledge_by_id( id: str, @@ -224,6 +265,11 @@ def remove_file_from_knowledge_by_id( collection_name=knowledge.id, filter={"file_id": form_data.file_id} ) + result = VECTOR_DB_CLIENT.query( + collection_name=knowledge.id, + filter={"file_id": form_data.file_id}, + ) + Files.delete_file_by_id(form_data.file_id) if knowledge: diff --git a/src/lib/apis/files/index.ts b/src/lib/apis/files/index.ts index 630a9e7c5..b76143471 100644 --- a/src/lib/apis/files/index.ts +++ b/src/lib/apis/files/index.ts @@ -92,6 +92,40 @@ export const getFileById = async (token: string, id: string) => { return res; }; +export const updateFileDataContentById = async (token: string, id: string, content: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}/data/content/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + content: content + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err.detail; + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const getFileContentById = async (id: string) => { let error = null; diff --git a/src/lib/apis/knowledge/index.ts b/src/lib/apis/knowledge/index.ts index a0ba83a0e..9923624a6 100644 --- a/src/lib/apis/knowledge/index.ts +++ b/src/lib/apis/knowledge/index.ts @@ -173,6 +173,41 @@ export const addFileToKnowledgeById = async (token: string, id: string, fileId: return res; }; +export const updateFileFromKnowledgeById = async (token: string, id: string, fileId: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/file/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + file_id: fileId + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err.detail; + + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const removeFileFromKnowledgeById = async (token: string, id: string, fileId: string) => { let error = null; diff --git a/src/lib/components/workspace/Knowledge/Collection.svelte b/src/lib/components/workspace/Knowledge/Collection.svelte index 9942a83a2..441e0b392 100644 --- a/src/lib/components/workspace/Knowledge/Collection.svelte +++ b/src/lib/components/workspace/Knowledge/Collection.svelte @@ -8,11 +8,12 @@ import { page } from '$app/stores'; import { mobile, showSidebar } from '$lib/stores'; - import { uploadFile } from '$lib/apis/files'; + import { updateFileDataContentById, uploadFile } from '$lib/apis/files'; import { addFileToKnowledgeById, getKnowledgeById, removeFileFromKnowledgeById, + updateFileFromKnowledgeById, updateKnowledgeById } from '$lib/apis/knowledge'; @@ -135,6 +136,28 @@ } }; + const updateFileContentHandler = async () => { + const fileId = selectedFile.id; + const content = selectedFile.data.content; + + const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => { + toast.error(e); + }); + + const updatedKnowledge = await updateFileFromKnowledgeById( + localStorage.token, + id, + fileId + ).catch((e) => { + toast.error(e); + }); + + if (res && updatedKnowledge) { + knowledge = updatedKnowledge; + toast.success($i18n.t('File content updated successfully.')); + } + }; + const changeDebounceHandler = () => { console.log('debounce'); if (debounceTimeout) { @@ -420,6 +443,9 @@