perf: optimize message loading based on streaming state
Decreased message limit to 90 for historical views when not streaming. Add fetch buffer to preload additional messages for efficient syncing.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { AssistantMessage, Message, Part } from '@opencode-ai/sdk/v2';
|
import type { AssistantMessage, Message, Part } from '@opencode-ai/sdk/v2';
|
||||||
import { useSessionStore, MEMORY_LIMITS } from '@/stores/useSessionStore';
|
import { useSessionStore } from '@/stores/useSessionStore';
|
||||||
|
import { MEMORY_LIMITS } from '@/stores/types/sessionTypes';
|
||||||
import { opencodeClient } from '@/lib/opencode/client';
|
import { opencodeClient } from '@/lib/opencode/client';
|
||||||
import { readSessionCursor } from '@/lib/messageCursorPersistence';
|
import { readSessionCursor } from '@/lib/messageCursorPersistence';
|
||||||
import { extractTextFromPart } from '@/stores/utils/messageUtils';
|
import { extractTextFromPart } from '@/stores/utils/messageUtils';
|
||||||
@@ -124,7 +125,10 @@ export const useMessageSync = () => {
|
|||||||
|
|
||||||
const currentMessages = (messages.get(currentSessionId) || []) as SessionMessageRecord[];
|
const currentMessages = (messages.get(currentSessionId) || []) as SessionMessageRecord[];
|
||||||
|
|
||||||
const latestMessages = (await opencodeClient.getSessionMessages(currentSessionId)) as SessionMessageRecord[];
|
const memoryState = useSessionStore.getState().sessionMemoryState.get(currentSessionId);
|
||||||
|
const targetLimit = memoryState?.isStreaming ? MEMORY_LIMITS.VIEWPORT_MESSAGES : MEMORY_LIMITS.HISTORICAL_MESSAGES;
|
||||||
|
const fetchLimit = targetLimit + MEMORY_LIMITS.FETCH_BUFFER;
|
||||||
|
const latestMessages = (await opencodeClient.getSessionMessages(currentSessionId, fetchLimit)) as SessionMessageRecord[];
|
||||||
const cursorRecord = await readSessionCursor(currentSessionId);
|
const cursorRecord = await readSessionCursor(currentSessionId);
|
||||||
|
|
||||||
if (!latestMessages) return;
|
if (!latestMessages) return;
|
||||||
@@ -167,7 +171,7 @@ export const useMessageSync = () => {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (isUserMessageInfo(lastLocalMessage.info)) {
|
if (isUserMessageInfo(lastLocalMessage.info)) {
|
||||||
const messagesToLoad = latestMessages.slice(-MEMORY_LIMITS.VIEWPORT_MESSAGES);
|
const messagesToLoad = latestMessages.slice(-targetLimit);
|
||||||
console.log('[SYNC] Local user message missing by ID; merging latest messages for deduplication');
|
console.log('[SYNC] Local user message missing by ID; merging latest messages for deduplication');
|
||||||
const { syncMessages } = useSessionStore.getState();
|
const { syncMessages } = useSessionStore.getState();
|
||||||
syncMessages(currentSessionId, messagesToLoad);
|
syncMessages(currentSessionId, messagesToLoad);
|
||||||
@@ -181,7 +185,7 @@ export const useMessageSync = () => {
|
|||||||
if (cursorIndex !== -1) {
|
if (cursorIndex !== -1) {
|
||||||
if (cursorIndex < latestMessages.length - 1) {
|
if (cursorIndex < latestMessages.length - 1) {
|
||||||
const newMessages = latestMessages.slice(cursorIndex + 1);
|
const newMessages = latestMessages.slice(cursorIndex + 1);
|
||||||
const limited = newMessages.slice(-MEMORY_LIMITS.VIEWPORT_MESSAGES);
|
const limited = newMessages.slice(-targetLimit);
|
||||||
if (limited.length > 0) {
|
if (limited.length > 0) {
|
||||||
console.log(`[SYNC] Restoring ${limited.length} messages after cursor`);
|
console.log(`[SYNC] Restoring ${limited.length} messages after cursor`);
|
||||||
const { syncMessages } = useSessionStore.getState();
|
const { syncMessages } = useSessionStore.getState();
|
||||||
@@ -190,13 +194,13 @@ export const useMessageSync = () => {
|
|||||||
}
|
}
|
||||||
} else if (latestMessages.length > 0) {
|
} else if (latestMessages.length > 0) {
|
||||||
console.log('[SYNC] Cursor not found on server response, loading recent messages');
|
console.log('[SYNC] Cursor not found on server response, loading recent messages');
|
||||||
const messagesToLoad = latestMessages.slice(-MEMORY_LIMITS.VIEWPORT_MESSAGES);
|
const messagesToLoad = latestMessages.slice(-targetLimit);
|
||||||
const { syncMessages } = useSessionStore.getState();
|
const { syncMessages } = useSessionStore.getState();
|
||||||
syncMessages(currentSessionId, messagesToLoad);
|
syncMessages(currentSessionId, messagesToLoad);
|
||||||
}
|
}
|
||||||
} else if (latestMessages.length > 0) {
|
} else if (latestMessages.length > 0) {
|
||||||
|
|
||||||
const messagesToLoad = latestMessages.slice(-MEMORY_LIMITS.VIEWPORT_MESSAGES);
|
const messagesToLoad = latestMessages.slice(-targetLimit);
|
||||||
console.log(`[SYNC] Loading last ${messagesToLoad.length} messages`);
|
console.log(`[SYNC] Loading last ${messagesToLoad.length} messages`);
|
||||||
const { syncMessages } = useSessionStore.getState();
|
const { syncMessages } = useSessionStore.getState();
|
||||||
syncMessages(currentSessionId, messagesToLoad);
|
syncMessages(currentSessionId, messagesToLoad);
|
||||||
|
|||||||
@@ -354,10 +354,11 @@ class OpencodeService {
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSessionMessages(id: string): Promise<{ info: Message; parts: Part[] }[]> {
|
async getSessionMessages(id: string, limit?: number): Promise<{ info: Message; parts: Part[] }[]> {
|
||||||
const response = await this.client.session.messages({
|
const response = await this.client.session.messages({
|
||||||
sessionID: id,
|
sessionID: id,
|
||||||
...(this.currentDirectory ? { directory: this.currentDirectory } : {})
|
...(this.currentDirectory ? { directory: this.currentDirectory } : {}),
|
||||||
|
...(typeof limit === 'number' ? { limit } : {}),
|
||||||
});
|
});
|
||||||
return response.data || [];
|
return response.data || [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,8 +385,11 @@ export const useMessageStore = create<MessageStore>()(
|
|||||||
pendingAssistantHeaderSessions: new Set(),
|
pendingAssistantHeaderSessions: new Set(),
|
||||||
pendingUserMessageMetaBySession: new Map(),
|
pendingUserMessageMetaBySession: new Map(),
|
||||||
|
|
||||||
loadMessages: async (sessionId: string, limit: number = MEMORY_LIMITS.VIEWPORT_MESSAGES) => {
|
loadMessages: async (sessionId: string, limit: number = MEMORY_LIMITS.HISTORICAL_MESSAGES) => {
|
||||||
const allMessages = await executeWithSessionDirectory(sessionId, () => opencodeClient.getSessionMessages(sessionId));
|
const isStreaming = get().sessionMemoryState.get(sessionId)?.isStreaming;
|
||||||
|
const targetLimit = isStreaming ? MEMORY_LIMITS.VIEWPORT_MESSAGES : limit;
|
||||||
|
const fetchLimit = isStreaming ? undefined : targetLimit + MEMORY_LIMITS.FETCH_BUFFER;
|
||||||
|
const allMessages = await executeWithSessionDirectory(sessionId, () => opencodeClient.getSessionMessages(sessionId, fetchLimit));
|
||||||
|
|
||||||
// Filter out reverted messages first
|
// Filter out reverted messages first
|
||||||
const revertMessageId = getSessionRevertMessageId(sessionId);
|
const revertMessageId = getSessionRevertMessageId(sessionId);
|
||||||
@@ -401,7 +404,7 @@ export const useMessageStore = create<MessageStore>()(
|
|||||||
return isIdNewer(messageId, watermark);
|
return isIdNewer(messageId, watermark);
|
||||||
})
|
})
|
||||||
: messagesWithoutReverted;
|
: messagesWithoutReverted;
|
||||||
const messagesToKeep = afterWatermark.slice(-limit);
|
const messagesToKeep = afterWatermark.slice(-targetLimit);
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const newMessages = new Map(state.messages);
|
const newMessages = new Map(state.messages);
|
||||||
@@ -2418,28 +2421,28 @@ export const useMessageStore = create<MessageStore>()(
|
|||||||
const allMessages = await executeWithSessionDirectory(sessionId, () => opencodeClient.getSessionMessages(sessionId));
|
const allMessages = await executeWithSessionDirectory(sessionId, () => opencodeClient.getSessionMessages(sessionId));
|
||||||
|
|
||||||
if (direction === "up" && currentMessages.length > 0) {
|
if (direction === "up" && currentMessages.length > 0) {
|
||||||
|
const dedupedMessages = dedupeMessagesById(allMessages);
|
||||||
const firstCurrentMessage = currentMessages[0];
|
const firstCurrentMessage = currentMessages[0];
|
||||||
const indexInAll = allMessages.findIndex((m) => m.info.id === firstCurrentMessage.info.id);
|
const indexInAll = dedupedMessages.findIndex((message) => message.info.id === firstCurrentMessage.info.id);
|
||||||
|
|
||||||
if (indexInAll > 0) {
|
if (indexInAll > 0) {
|
||||||
|
|
||||||
const loadCount = Math.min(MEMORY_LIMITS.VIEWPORT_MESSAGES, indexInAll);
|
const loadCount = Math.min(MEMORY_LIMITS.VIEWPORT_MESSAGES, indexInAll);
|
||||||
const newMessages = allMessages.slice(indexInAll - loadCount, indexInAll);
|
const newMessages = dedupedMessages.slice(indexInAll - loadCount, indexInAll);
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const updatedMessages = [...newMessages, ...currentMessages];
|
const updatedMessages = [...newMessages, ...currentMessages];
|
||||||
const dedupedMessages = dedupeMessagesById(updatedMessages);
|
const mergedMessages = dedupeMessagesById(updatedMessages);
|
||||||
const newMessagesMap = new Map(state.messages);
|
const addedCount = Math.max(0, mergedMessages.length - currentMessages.length);
|
||||||
newMessagesMap.set(sessionId, dedupedMessages);
|
|
||||||
|
const newMessagesMap = new Map(state.messages);
|
||||||
|
newMessagesMap.set(sessionId, mergedMessages);
|
||||||
|
|
||||||
const addedCount = Math.max(0, dedupedMessages.length - currentMessages.length);
|
|
||||||
const newMemoryState = new Map(state.sessionMemoryState);
|
const newMemoryState = new Map(state.sessionMemoryState);
|
||||||
newMemoryState.set(sessionId, {
|
newMemoryState.set(sessionId, {
|
||||||
...memoryState,
|
...memoryState,
|
||||||
viewportAnchor: memoryState.viewportAnchor + addedCount,
|
viewportAnchor: memoryState.viewportAnchor + addedCount,
|
||||||
hasMoreAbove: indexInAll - loadCount > 0,
|
hasMoreAbove: indexInAll - loadCount > 0,
|
||||||
totalAvailableMessages: allMessages.length,
|
totalAvailableMessages: dedupedMessages.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -2448,12 +2451,12 @@ export const useMessageStore = create<MessageStore>()(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else if (indexInAll === 0) {
|
} else if (indexInAll === 0) {
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const newMemoryState = new Map(state.sessionMemoryState);
|
const newMemoryState = new Map(state.sessionMemoryState);
|
||||||
newMemoryState.set(sessionId, {
|
newMemoryState.set(sessionId, {
|
||||||
...memoryState,
|
...memoryState,
|
||||||
hasMoreAbove: false,
|
hasMoreAbove: false,
|
||||||
|
totalAvailableMessages: dedupedMessages.length,
|
||||||
});
|
});
|
||||||
return { sessionMemoryState: newMemoryState };
|
return { sessionMemoryState: newMemoryState };
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export interface SessionContextUsage {
|
|||||||
export const MEMORY_LIMITS = {
|
export const MEMORY_LIMITS = {
|
||||||
MAX_SESSIONS: 3,
|
MAX_SESSIONS: 3,
|
||||||
VIEWPORT_MESSAGES: 120,
|
VIEWPORT_MESSAGES: 120,
|
||||||
|
HISTORICAL_MESSAGES: 90,
|
||||||
|
FETCH_BUFFER: 20,
|
||||||
STREAMING_BUFFER: Infinity,
|
STREAMING_BUFFER: Infinity,
|
||||||
BACKGROUND_STREAMING_BUFFER: 120,
|
BACKGROUND_STREAMING_BUFFER: 120,
|
||||||
ZOMBIE_TIMEOUT: 10 * 60 * 1000,
|
ZOMBIE_TIMEOUT: 10 * 60 * 1000,
|
||||||
|
|||||||
Reference in New Issue
Block a user