fix: stream GET /chats/all to prevent OOM on large chat histories (#24461)
Convert the /chats/all endpoint from loading all user chats into memory at once to a streaming NDJSON response that fetches chats in batches of 100. This prevents Out-of-Memory crashes for users with large chat histories. Backend: Added async generator that paginates through chats with short-lived DB sessions per batch (critical for SQLite lock release). Frontend: Updated getAllChats to consume the NDJSON stream via ReadableStream reader, accumulating results for the export file. Ref: open-webui#22206
This commit is contained in:
@@ -676,11 +676,46 @@ async def get_user_pinned_chats(user=Depends(get_verified_user), db: AsyncSessio
|
||||
# GetChats
|
||||
############################
|
||||
|
||||
CHAT_EXPORT_BATCH_SIZE = 100
|
||||
|
||||
@router.get('/all', response_model=list[ChatResponse])
|
||||
async def get_user_chats(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
result = await Chats.get_chats_by_user_id(user.id, db=db)
|
||||
return [ChatResponse(**chat.model_dump()) for chat in result.items]
|
||||
|
||||
async def generate_chat_export_ndjson(user_id: str):
|
||||
"""
|
||||
Async generator that streams all user chats as NDJSON (one JSON object per line).
|
||||
|
||||
Uses short-lived DB sessions per batch to avoid holding locks for the
|
||||
entire duration, which is critical for SQLite environments.
|
||||
"""
|
||||
skip = 0
|
||||
|
||||
while True:
|
||||
result = await Chats.get_chats_by_user_id(
|
||||
user_id,
|
||||
skip=skip,
|
||||
limit=CHAT_EXPORT_BATCH_SIZE,
|
||||
db=None,
|
||||
)
|
||||
if not result.items:
|
||||
break
|
||||
|
||||
for chat in result.items:
|
||||
try:
|
||||
yield ChatResponse(**chat.model_dump()).model_dump_json() + '\n'
|
||||
except Exception as e:
|
||||
log.exception(f'Error serializing chat {chat.id}: {e}')
|
||||
|
||||
if len(result.items) < CHAT_EXPORT_BATCH_SIZE:
|
||||
break
|
||||
|
||||
skip += CHAT_EXPORT_BATCH_SIZE
|
||||
|
||||
|
||||
@router.get('/all')
|
||||
async def get_user_chats(user=Depends(get_verified_user)):
|
||||
return StreamingResponse(
|
||||
generate_chat_export_ndjson(user.id),
|
||||
media_type='application/x-ndjson',
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
|
||||
Reference in New Issue
Block a user