Browse Source

feat: edit file content support

Timothy J. Baek 7 months ago
parent
commit
8013c152d0

+ 1 - 1
backend/open_webui/apps/retrieval/main.py

@@ -709,7 +709,7 @@ def save_docs_to_vector_db(
             }
             }
             for idx, text in enumerate(texts)
             for idx, text in enumerate(texts)
         ]
         ]
-        
+
         VECTOR_DB_CLIENT.insert(
         VECTOR_DB_CLIENT.insert(
             collection_name=collection_name,
             collection_name=collection_name,
             items=items,
             items=items,

+ 50 - 4
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
 # RemoveFileFromKnowledge
 ############################
 ############################
 
 
 
 
-class KnowledgeFileIdForm(BaseModel):
-    file_id: str
-
-
 @router.post("/{id}/file/remove", response_model=Optional[KnowledgeFilesResponse])
 @router.post("/{id}/file/remove", response_model=Optional[KnowledgeFilesResponse])
 def remove_file_from_knowledge_by_id(
 def remove_file_from_knowledge_by_id(
     id: str,
     id: str,
@@ -224,6 +265,11 @@ def remove_file_from_knowledge_by_id(
         collection_name=knowledge.id, filter={"file_id": form_data.file_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)
     Files.delete_file_by_id(form_data.file_id)
 
 
     if knowledge:
     if knowledge:

+ 34 - 0
src/lib/apis/files/index.ts

@@ -92,6 +92,40 @@ export const getFileById = async (token: string, id: string) => {
 	return res;
 	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) => {
 export const getFileContentById = async (id: string) => {
 	let error = null;
 	let error = null;
 
 

+ 35 - 0
src/lib/apis/knowledge/index.ts

@@ -173,6 +173,41 @@ export const addFileToKnowledgeById = async (token: string, id: string, fileId:
 	return res;
 	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) => {
 export const removeFileFromKnowledgeById = async (token: string, id: string, fileId: string) => {
 	let error = null;
 	let error = null;
 
 

+ 27 - 1
src/lib/components/workspace/Knowledge/Collection.svelte

@@ -8,11 +8,12 @@
 	import { page } from '$app/stores';
 	import { page } from '$app/stores';
 	import { mobile, showSidebar } from '$lib/stores';
 	import { mobile, showSidebar } from '$lib/stores';
 
 
-	import { uploadFile } from '$lib/apis/files';
+	import { updateFileDataContentById, uploadFile } from '$lib/apis/files';
 	import {
 	import {
 		addFileToKnowledgeById,
 		addFileToKnowledgeById,
 		getKnowledgeById,
 		getKnowledgeById,
 		removeFileFromKnowledgeById,
 		removeFileFromKnowledgeById,
+		updateFileFromKnowledgeById,
 		updateKnowledgeById
 		updateKnowledgeById
 	} from '$lib/apis/knowledge';
 	} 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 = () => {
 	const changeDebounceHandler = () => {
 		console.log('debounce');
 		console.log('debounce');
 		if (debounceTimeout) {
 		if (debounceTimeout) {
@@ -420,6 +443,9 @@
 									<div>
 									<div>
 										<button
 										<button
 											class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
 											class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
+											on:click={() => {
+												updateFileContentHandler();
+											}}
 										>
 										>
 											{$i18n.t('Save')}
 											{$i18n.t('Save')}
 										</button>
 										</button>