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:
+39
-20
@@ -301,34 +301,53 @@ export const getSharedChatList = async (token: string = '', page: number = 1, fi
|
||||
};
|
||||
|
||||
export const getAllChats = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/x-ndjson',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.then((json) => {
|
||||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err;
|
||||
console.error(err);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
return res;
|
||||
const reader = res.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error('Response body is not readable');
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
const chats: object[] = [];
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
// Keep the last potentially incomplete line in the buffer
|
||||
buffer = lines.pop() ?? '';
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed) {
|
||||
chats.push(JSON.parse(trimmed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process any remaining data in the buffer
|
||||
const remaining = buffer.trim();
|
||||
if (remaining) {
|
||||
chats.push(JSON.parse(remaining));
|
||||
}
|
||||
|
||||
return chats;
|
||||
};
|
||||
|
||||
export const getChatListBySearchText = async (token: string, text: string, page: number = 1) => {
|
||||
|
||||
Reference in New Issue
Block a user