This commit is contained in:
Timothy Jaeryang Baek
2026-08-24 17:12:56 -04:00
parent 23b3a69bc2
commit 363ad352fe
3 changed files with 44 additions and 27 deletions

View File

@@ -19,6 +19,9 @@ ENABLE_MEMORY_SYSTEM_CONTEXT=true
# Set to true to add compact row/column stats to parsed CSV retrieval context. # Set to true to add compact row/column stats to parsed CSV retrieval context.
ENABLE_RAG_CSV_SUMMARY=false ENABLE_RAG_CSV_SUMMARY=false
# Set to true to preserve backing file records, storage blobs, and per-file vectors when files are removed from knowledge bases.
ENABLE_KNOWLEDGE_FILE_RETENTION=false
# Set to false to disable workspace Tools and Functions. # Set to false to disable workspace Tools and Functions.
ENABLE_PLUGINS=true ENABLE_PLUGINS=true

View File

@@ -973,6 +973,8 @@ RAG_FILE_MAX_COUNT = int(os.getenv('RAG_FILE_MAX_COUNT')) if os.getenv('RAG_FILE
RAG_FILE_MAX_SIZE = int(os.getenv('RAG_FILE_MAX_SIZE')) if os.getenv('RAG_FILE_MAX_SIZE') else None RAG_FILE_MAX_SIZE = int(os.getenv('RAG_FILE_MAX_SIZE')) if os.getenv('RAG_FILE_MAX_SIZE') else None
ENABLE_KNOWLEDGE_FILE_RETENTION = os.getenv('ENABLE_KNOWLEDGE_FILE_RETENTION', 'False').lower() == 'true'
RAG_FILE_CONTENT_SEARCH_MAX_CHARS = int(os.getenv('RAG_FILE_CONTENT_SEARCH_MAX_CHARS', str(64 * 1024 * 1024))) RAG_FILE_CONTENT_SEARCH_MAX_CHARS = int(os.getenv('RAG_FILE_CONTENT_SEARCH_MAX_CHARS', str(64 * 1024 * 1024)))
FILE_IMAGE_COMPRESSION_WIDTH = ( FILE_IMAGE_COMPRESSION_WIDTH = (

View File

@@ -11,7 +11,11 @@ from urllib.parse import quote
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, RAG_EMBEDDING_CONTENT_PREFIX from open_webui.config import (
BYPASS_ADMIN_ACCESS_CONTROL,
ENABLE_KNOWLEDGE_FILE_RETENTION,
RAG_EMBEDDING_CONTENT_PREFIX,
)
from open_webui.constants import ERROR_MESSAGES from open_webui.constants import ERROR_MESSAGES
from open_webui.events import EVENTS, publish_event from open_webui.events import EVENTS, publish_event
from open_webui.internal.db import get_async_session from open_webui.internal.db import get_async_session
@@ -56,6 +60,25 @@ router = APIRouter()
PAGE_ITEM_COUNT = 30 PAGE_ITEM_COUNT = 30
async def delete_file_resource(file: FileModel, db: AsyncSession) -> bool:
try:
file_collection = f'file-{file.id}'
if await ASYNC_VECTOR_DB_CLIENT.has_collection(collection_name=file_collection):
await ASYNC_VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection)
except Exception as e:
log.debug('This was most likely caused by bypassing embedding processing')
log.debug(e)
result = await Files.delete_file_by_id(file.id, db=db)
if result and file.path:
try:
await asyncio.to_thread(Storage.delete_file, file.path)
except Exception as e:
log.debug(e)
return result
############################ ############################
# Knowledge Base Embedding # Knowledge Base Embedding
############################ ############################
@@ -1569,7 +1592,7 @@ async def remove_file_from_knowledge_by_id(
request: Request, request: Request,
id: str, id: str,
form_data: KnowledgeFileIdForm, form_data: KnowledgeFileIdForm,
delete_file: bool = Query(True), delete_file: bool = Query(not ENABLE_KNOWLEDGE_FILE_RETENTION),
user=Depends(get_verified_user), user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session), db: AsyncSession = Depends(get_async_session),
): ):
@@ -1630,18 +1653,7 @@ async def remove_file_from_knowledge_by_id(
# Anyone with write permission or higher can delete files # Anyone with write permission or higher can delete files
if delete_file and (file.user_id == user.id or user.role == 'admin'): if delete_file and (file.user_id == user.id or user.role == 'admin'):
try: await delete_file_resource(file, db)
# Remove the file's collection from vector database
file_collection = f'file-{form_data.file_id}'
if await ASYNC_VECTOR_DB_CLIENT.has_collection(collection_name=file_collection):
await ASYNC_VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection)
except Exception as e:
log.debug('This was most likely caused by bypassing embedding processing')
log.debug(e)
pass
# Delete file from database
await Files.delete_file_by_id(form_data.file_id, db=db)
if knowledge: if knowledge:
response = KnowledgeFilesResponse( response = KnowledgeFilesResponse(
@@ -1786,12 +1798,18 @@ async def reset_knowledge_by_id(
detail=ERROR_MESSAGES.ACCESS_PROHIBITED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
) )
files = await Knowledges.get_files_by_id(id, db=db) if not ENABLE_KNOWLEDGE_FILE_RETENTION else []
try: try:
await ASYNC_VECTOR_DB_CLIENT.delete_collection(collection_name=id) await ASYNC_VECTOR_DB_CLIENT.delete_collection(collection_name=id)
except Exception as e: except Exception as e:
log.debug(e) log.debug(e)
pass pass
for file in files:
if file.user_id == user.id or user.role == 'admin':
await delete_file_resource(file, db)
knowledge = await Knowledges.reset_knowledge_by_id(id=id, include_directories=include_directories, db=db) knowledge = await Knowledges.reset_knowledge_by_id(id=id, include_directories=include_directories, db=db)
if knowledge: if knowledge:
await publish_event( await publish_event(
@@ -1966,19 +1984,13 @@ async def sync_knowledge_cleanup(
except Exception: except Exception:
pass pass
try: linked_knowledges = await Knowledges.get_knowledges_by_file_id(file_id, db=db)
collection_name = f'file-{file_id}' if (
if await ASYNC_VECTOR_DB_CLIENT.has_collection(collection_name): not ENABLE_KNOWLEDGE_FILE_RETENTION
await ASYNC_VECTOR_DB_CLIENT.delete_collection(collection_name) and not linked_knowledges
except Exception: and (file.user_id == user.id or user.role == 'admin')
pass ):
await delete_file_resource(file, db)
if file.user_id == user.id or user.role == 'admin':
await Files.delete_file_by_id(file_id, db=db)
try:
await asyncio.to_thread(Storage.delete_file, file.path)
except Exception:
pass
# ── Remove orphaned directories (children before parents) ── # ── Remove orphaned directories (children before parents) ──
for dir_id in reversed(form_data.dir_ids): for dir_id in reversed(form_data.dir_ids):