feat(sync): use incremental session index after pull sync

After pull-sync imports new messages, generate session index incrementally
instead of full regeneration. This preserves existing sessions and their
summaries, only processing newly imported (unindexed) messages.

Adds generateIncremental method across the full stack:
- core: already had generateIncrementalSessionIndex
- node-runtime: expose generateIncrementalIndex
- CLI: add POST /_web/sessions/:id/generate-incremental-index route
- Electron: add session:generateIncremental IPC handler
- Frontend: add generateIncremental to SessionIndexAdapter interface
This commit is contained in:
digua authored and digua committed 2026-05-23 16:28:50 +08:00
1 parent fcfa5d0167
commit a5bbff34e4
10 files changed
+59 -6

No files matched your search

@@ -11,6 +11,15 @@ export function registerSessionIndexRoutes(server: FastifyInstance, adapter: Ses
return { sessionCount }
})
server.post<{
Params: { id: string }
Body: { gapThreshold?: number }
}>('/_web/sessions/:id/generate-incremental-index', async (request) => {
const gapThreshold = (request.body as any)?.gapThreshold ?? 1800
const sessionCount = sessionIndexService.generateIncrementalIndex(adapter, request.params.id, gapThreshold)
return { sessionCount }
})
server.post<{ Params: { id: string } }>('/_web/sessions/:id/clear-index', async (request) => {
sessionIndexService.clearIndex(adapter, request.params.id)
return { success: true }
+9
View File
@@ -803,6 +803,15 @@ export function registerChatHandlers(ctx: IpcContext): void {
}
})
ipcMain.handle('session:generateIncremental', async (_, sessionId: string, gapThreshold?: number) => {
try {
return await worker.generateIncrementalSessions(sessionId, gapThreshold)
} catch (error) {
console.error('Failed to generate incremental session index:', error)
throw error
}
})
/**
* 检查是否已生成会话索引
*/
+4 -3
View File
@@ -240,9 +240,10 @@ export const sessionApi = {
return ipcRenderer.invoke('session:generate', sessionId, gapThreshold)
},
/**
* 检查是否已生成会话索引
*/
generateIncremental: (sessionId: string, gapThreshold?: number): Promise<number> => {
return ipcRenderer.invoke('session:generateIncremental', sessionId, gapThreshold)
},
hasIndex: (sessionId: string): Promise<boolean> => {
return ipcRenderer.invoke('session:hasIndex', sessionId)
},
+1
View File
@@ -1107,6 +1107,7 @@ interface ChatSessionItem {
interface SessionApi {
generate: (sessionId: string, gapThreshold?: number) => Promise<number>
generateIncremental: (sessionId: string, gapThreshold?: number) => Promise<number>
hasIndex: (sessionId: string) => Promise<boolean>
getStats: (sessionId: string) => Promise<SessionStats>
clear: (sessionId: string) => Promise<boolean>
+8 -1
View File
@@ -24,7 +24,14 @@ export {
export type { MembersPaginatedDTO } from './member-service'
// Session index service
export { generateIndex, clearIndex, getFtsStatus, searchFts, rebuildFts } from './session-index-service'
export {
generateIndex,
generateIncrementalIndex,
clearIndex,
getFtsStatus,
searchFts,
rebuildFts,
} from './session-index-service'
// Summary service
export { generateSummary, generateAllSummaries } from './summary-service'
@@ -6,6 +6,7 @@
import {
generateSessionIndex as coreGenerateSessionIndex,
generateIncrementalSessionIndex as coreGenerateIncrementalSessionIndex,
clearSessionIndex as coreClearSessionIndex,
} from '@openchatlab/core'
import { hasFtsTable, searchByFts, rebuildFtsIndex } from '../fts'
@@ -16,6 +17,15 @@ export function generateIndex(adapter: SessionRuntimeAdapter, sessionId: string,
return coreGenerateSessionIndex(db, gapThreshold)
}
export function generateIncrementalIndex(
adapter: SessionRuntimeAdapter,
sessionId: string,
gapThreshold: number = 1800
): number {
const db = adapter.ensureWritable(sessionId)
return coreGenerateIncrementalSessionIndex(db, gapThreshold)
}
export function clearIndex(adapter: SessionRuntimeAdapter, sessionId: string): void {
const db = adapter.ensureWritable(sessionId)
coreClearSessionIndex(db)
+4
View File
@@ -16,6 +16,10 @@ export class ElectronSessionIndexAdapter implements SessionIndexAdapter {
return window.sessionApi.generate(sessionId, gapThreshold)
}
generateIncremental(sessionId: string, gapThreshold?: number): Promise<number> {
return window.sessionApi.generateIncremental(sessionId, gapThreshold)
}
hasIndex(sessionId: string): Promise<boolean> {
return window.sessionApi.hasIndex(sessionId)
}
+11
View File
@@ -33,6 +33,17 @@ export class FetchSessionIndexAdapter implements SessionIndexAdapter {
return result.sessionCount
}
async generateIncremental(sessionId: string, gapThreshold: number = 1800): Promise<number> {
const resp = await fetch(`/_web/sessions/${sessionId}/generate-incremental-index`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gapThreshold }),
})
if (!resp.ok) throw new Error(`Failed to generate incremental session index: ${resp.status}`)
const result = (await resp.json()) as { sessionCount: number }
return result.sessionCount
}
async hasIndex(sessionId: string): Promise<boolean> {
const stats = await this.getStats(sessionId)
return stats.hasIndex
+1
View File
@@ -39,6 +39,7 @@ export interface CanGenerateInfo {
export interface SessionIndexAdapter {
generate(sessionId: string, gapThreshold?: number): Promise<number>
generateIncremental(sessionId: string, gapThreshold?: number): Promise<number>
hasIndex(sessionId: string): Promise<boolean>
getStats(sessionId: string): Promise<SessionStats>
clear(sessionId: string): Promise<boolean>
+2 -2
View File
@@ -526,7 +526,7 @@ export const useApiServerStore = defineStore('apiServer', () => {
const indexService = useSessionIndexService()
const threshold = getSessionGapThreshold()
for (const sess of targets) {
indexService.generate(sess.targetSessionId, threshold).catch(() => {})
indexService.generateIncremental(sess.targetSessionId, threshold).catch(() => {})
}
}
@@ -536,7 +536,7 @@ export const useApiServerStore = defineStore('apiServer', () => {
for (const ds of dataSources.value) {
for (const sess of ds.sessions) {
if (sess.targetSessionId) {
indexService.generate(sess.targetSessionId, threshold).catch(() => {})
indexService.generateIncremental(sess.targetSessionId, threshold).catch(() => {})
}
}
}