Add virtualized chat pagination (#1080)

This commit is contained in:
ekko
2026-05-28 09:34:30 +08:00
committed by GitHub
parent 21bb8385f2
commit a6b3bec29b
16 changed files with 692 additions and 161 deletions
@@ -452,7 +452,7 @@ export function updateSessionStats(id: string): void {
export function getSessionDetailPaginated(
id: string,
offset = 0,
limit = 500,
limit = 300,
): PaginatedSessionDetailResult | null {
if (!isSqliteAvailable()) {
return null
@@ -182,10 +182,13 @@ groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
return
}
const messages = chatServer.getStorage().getMessages(ctx.params.roomId)
const offset = ctx.query.offset ? Math.max(0, parseInt(ctx.query.offset as string, 10) || 0) : 0
const limit = ctx.query.limit ? Math.max(1, parseInt(ctx.query.limit as string, 10) || 300) : 300
const messages = chatServer.getStorage().getMessages(ctx.params.roomId, limit, offset)
const total = chatServer.getStorage().getMessageCount(ctx.params.roomId)
const agents = chatServer.getStorage().getRoomAgents(ctx.params.roomId)
const members = chatServer.getStorage().getRoomMembers(ctx.params.roomId)
ctx.body = { room, messages, agents, members }
ctx.body = { room, messages, agents, members, total, offset, limit, hasMore: offset + messages.length < total }
})
// List rooms
@@ -390,16 +390,23 @@ class ChatStorage {
// ─── Messages ─────────────────────────────────────────────
getMessages(roomId: string, limit = 500): ChatMessage[] {
getMessages(roomId: string, limit = 300, offset = 0): ChatMessage[] {
const rows = (this.db()?.prepare(
'SELECT id, roomId, senderId, senderName, content, timestamp, role, tool_call_id, tool_calls, tool_name, finish_reason, reasoning, reasoning_details, reasoning_content FROM gc_messages WHERE roomId = ? ORDER BY timestamp DESC LIMIT ?'
).all(roomId, limit) || []) as any[]
'SELECT id, roomId, senderId, senderName, content, timestamp, role, tool_call_id, tool_calls, tool_name, finish_reason, reasoning, reasoning_details, reasoning_content FROM gc_messages WHERE roomId = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?'
).all(roomId, limit, offset) || []) as any[]
return sortGroupMessages(rows.map(row => ({
...row,
tool_calls: parseJsonArray(row.tool_calls),
})))
}
getMessageCount(roomId: string): number {
const row = this.db()?.prepare(
'SELECT COUNT(*) as total FROM gc_messages WHERE roomId = ?'
).get(roomId) as { total: number } | undefined
return row?.total || 0
}
getMessage(messageId: string): ChatMessage | null {
const row = this.db()?.prepare(
'SELECT id, roomId, senderId, senderName, content, timestamp, role, tool_call_id, tool_calls, tool_name, finish_reason, reasoning, reasoning_details, reasoning_content FROM gc_messages WHERE id = ?'
@@ -68,6 +68,10 @@ export async function loadSessionStateFromDb(sid: string, _sessionMap: Map<strin
logger.info('[chat-run-socket] loaded session %s from DB (%d messages)', sid, messages.length)
return {
messages,
messageTotal: actualDetail?.total || messages.length,
messageLoadedCount: actualDetail?.messages.length || messages.length,
messagePageLimit: actualDetail?.limit,
hasMoreBefore: actualDetail?.hasMore || false,
isWorking: false,
events: [],
inputTokens,
@@ -334,6 +334,10 @@ export class ChatRunSocket {
socket.emit('resumed', {
session_id: sid,
messages: state.messages,
messageTotal: state.messageTotal,
messageLoadedCount: state.messageLoadedCount,
messagePageLimit: state.messagePageLimit,
hasMoreBefore: state.hasMoreBefore,
isWorking: state.isWorking,
isAborting: state.isAborting || false,
events: state.isWorking ? state.events : [],
@@ -43,6 +43,10 @@ export interface QueuedRun {
export interface SessionState {
messages: SessionMessage[]
messageTotal?: number
messageLoadedCount?: number
messagePageLimit?: number
hasMoreBefore?: boolean
isWorking: boolean
events: Array<{ event: string; data: any }>
abortController?: AbortController