perf: optimize/message performance (#3181)

* feat: Add message pause and resume functionality

- Implement pauseMessage and pauseMessages methods in useMessageOperations hook
- Update Inputbar to use new pauseMessages method for stopping message generation
- Remove deprecated pause-related code from ApiService and store
- Simplify message generation and pause logic across providers
- Enhance message state management with more granular control over streaming messages

* feat: Enhance topic management with sequence-based sorting and lazy loading

- Add sequence field to topics for better sorting
- Implement lazy loading mechanism for topic messages
- Modify Redux store to support per-topic loading states
- Update database schema to use sequence as an auto-incrementing primary key
- Optimize message initialization and retrieval process

* refactor(database): Enhance topic management with timestamps and upgrade logic

- Modify database schema to include createdAt and updatedAt for topics
- Add database hooks for automatic timestamp handling
- Refactor topic upgrade process to support new timestamp fields
- Remove redundant upgradesV6.ts file
- Update topic retrieval to use updatedAt for sorting
- Improve database consistency and tracking of topic modifications

* refactor: Streamline message state management and remove unused code

- Remove commented-out code in multiple components
- Delete initializeMessagesState thunk from messages store
- Simplify message sending and streaming logic
- Remove unnecessary console logs
- Optimize MessageStream component with memo
- Using loading to control message generation within a single session
- Lift the restriction on not being able to switch topics in message generation

* refactor(database): Remove version 6 database version and hooks

- Remove version 6 database schema definition
- Delete automatic timestamp hooks for topics
- Clean up unused database upgrade and hook code

* refactor(Messages): Optimize message state management and remove redundant code

- Remove duplicate imports and redundant code blocks
- Simplify message sending and streaming logic in messages store
- Enhance throttling mechanism for message updates
- Remove commented-out code and unused function parameters
- Improve error handling and loading state management
- Optimize message synchronization with database

* fix:console
This commit is contained in:
MyPrototypeWhat 2025-03-11 17:31:44 +08:00 committed by GitHub
parent a25c0e657b
commit c69c750144
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 171 additions and 186 deletions

View File

@ -1,7 +1,7 @@
import { FileType, KnowledgeItem, Topic, TranslateHistory } from '@renderer/types'
import { Dexie, type EntityTable } from 'dexie'
import { upgradeToV5, upgradeToV6 } from './upgrades'
import { upgradeToV5 } from './upgrades'
// Database declaration (move this to its own module also)
export const db = new Dexie('CherryStudio') as Dexie & {
files: EntityTable<FileType, 'id'>
@ -46,27 +46,4 @@ db.version(5)
})
.upgrade((tx) => upgradeToV5(tx))
db.version(6)
.stores({
files: 'id, name, origin_name, path, size, ext, type, created_at, count',
topics: '&id, messages, createdAt, updatedAt',
settings: '&id, value',
knowledge_notes: '&id, baseId, type, content, created_at, updated_at',
translate_history: '&id, sourceText, targetText, sourceLanguage, targetLanguage, createdAt'
})
.upgrade((tx) => upgradeToV6(tx))
// Add hooks for automatic timestamp handling
db.topics.hook('creating', (_, obj: any) => {
const now = new Date().toISOString()
obj.createdAt = now
obj.updatedAt = now
})
db.topics.hook('updating', (modifications: any) => {
if (typeof modifications === 'object') {
modifications.updatedAt = new Date().toISOString()
}
})
export default db

View File

@ -37,6 +37,7 @@ export async function upgradeToV5(tx: Transaction): Promise<void> {
}
}
// 为每个 topic 添加时间戳,兼容老数据,默认按照最新的时间戳来,不确定是否要加
export async function upgradeToV6(tx: Transaction): Promise<void> {
const topics = await tx.table('topics').toArray()

View File

@ -1,5 +1,6 @@
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import store from '@renderer/store'
import {
clearStreamMessage,
clearTopicMessages,
@ -13,6 +14,7 @@ import {
updateMessages
} from '@renderer/store/messages'
import type { Assistant, Message, Topic } from '@renderer/types'
import { abortCompletion } from '@renderer/utils/abortController'
import { useCallback } from 'react'
/**
* Hook
@ -149,6 +151,52 @@ export function useMessageOperations(topic: Topic) {
// */
// const getMessages = useCallback(() => messages, [messages])
/**
*
*/
const pauseMessage = useCallback(
async (messageId: string) => {
// 1. 调用 abort
abortCompletion(messageId)
// 2. 更新消息状态
await editMessage(messageId, { status: 'paused' })
// 3. 清理流式消息
clearStreamMessageAction(messageId)
},
[editMessage, clearStreamMessageAction]
)
const pauseMessages = useCallback(async () => {
// 从 store 获取当前 topic 的所有流式消息
const streamMessages = store.getState().messages.streamMessagesByTopic[topic.id]
if (streamMessages) {
// 获取所有流式消息的 askId
const askIds = new Set(
Object.values(streamMessages)
.map((msg) => msg.askId)
.filter(Boolean)
)
// 对每个 askId 执行暂停
for (const askId of askIds) {
await pauseMessage(askId)
}
}
}, [topic.id, pauseMessage])
/**
* /
*
*/
const resumeMessage = useCallback(
async (message: Message, assistant: Assistant) => {
return resendMessageAction(message, assistant)
},
[resendMessageAction]
)
return {
messages,
loading,
@ -163,6 +211,9 @@ export function useMessageOperations(topic: Topic) {
commitStreamMessage: commitStreamMessageAction,
clearStreamMessage: clearStreamMessageAction,
createNewContext,
clearTopicMessages: clearTopicMessagesAction
clearTopicMessages: clearTopicMessagesAction,
pauseMessage,
pauseMessages,
resumeMessage
}
}

View File

@ -22,15 +22,15 @@ import { useSidebarIconShow } from '@renderer/hooks/useSidebarIcon'
import { addAssistantMessagesToTopic, getDefaultTopic } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import FileManager from '@renderer/services/FileManager'
import { estimateTextTokens as estimateTxtTokens } from '@renderer/services/TokenService'
import { getUserMessage } from '@renderer/services/MessagesService'
import { estimateMessageUsage, estimateTextTokens as estimateTxtTokens } from '@renderer/services/TokenService'
import { translateText } from '@renderer/services/TranslateService'
import WebSearchService from '@renderer/services/WebSearchService'
import store, { useAppDispatch } from '@renderer/store'
import { useAppDispatch } from '@renderer/store'
import { sendMessage as _sendMessage } from '@renderer/store/messages'
import { setGenerating, setSearching } from '@renderer/store/runtime'
import { setSearching } from '@renderer/store/runtime'
import { Assistant, FileType, KnowledgeBase, MCPServer, Message, Model, Topic } from '@renderer/types'
import { classNames, delay, getFileExtension } from '@renderer/utils'
import { abortCompletion } from '@renderer/utils/abortController'
import { getFilesFromDropEvent } from '@renderer/utils/input'
import { documentExts, imageExts, textExts } from '@shared/config/constant'
import { Button, Popconfirm, Tooltip } from 'antd'
@ -84,7 +84,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
const containerRef = useRef(null)
const { searching } = useRuntime()
const { isBubbleStyle } = useMessageStyle()
const { loading } = useMessageOperations(topic)
const { loading, pauseMessages } = useMessageOperations(topic)
const dispatch = useAppDispatch()
const [spaceClickCount, setSpaceClickCount] = useState(0)
const spaceClickTimer = useRef<NodeJS.Timeout>()
@ -140,14 +140,27 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
try {
// Dispatch the sendMessage action with all options
const uploadedFiles = await FileManager.uploadFiles(files)
dispatch(
_sendMessage(text, assistant, topic, {
files: uploadedFiles,
knowledgeBaseIds: selectedKnowledgeBases?.map((base) => base.id),
mentionModels,
enabledMCPs
})
)
const userMessage = getUserMessage({ assistant, topic, type: 'text', content: text })
if (uploadedFiles) {
userMessage.files = uploadedFiles
}
const knowledgeBaseIds = selectedKnowledgeBases?.map((base) => base.id)
if (knowledgeBaseIds) {
userMessage.knowledgeBaseIds = knowledgeBaseIds
}
if (mentionModels) {
userMessage.mentions = mentionModels
}
if (enabledMCPs) {
userMessage.enabledMCPs = enabledMCPs
}
userMessage.usage = await estimateMessageUsage(userMessage)
currentMessageId.current = userMessage.id
dispatch(_sendMessage(userMessage, assistant, topic))
// Clear input
setText('')
@ -158,7 +171,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
} catch (error) {
console.error('Failed to send message:', error)
}
}, [inputEmpty, files, dispatch, text, assistant, selectedKnowledgeBases, mentionModels, enabledMCPs])
}, [inputEmpty, files, dispatch, text, assistant, topic, selectedKnowledgeBases, mentionModels, enabledMCPs, loading])
const translate = async () => {
if (isTranslating) {
@ -282,24 +295,23 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
clickAssistantToShowTopic && setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0)
}, [addTopic, assistant, clickAssistantToShowTopic, setActiveTopic, setModel])
const onPause = async () => {
await pauseMessages()
}
const clearTopic = async () => {
if (loading) {
onPause()
await onPause()
await delay(1)
}
EventEmitter.emit(EVENT_NAMES.CLEAR_MESSAGES)
}
const onPause = () => {
if (currentMessageId.current) {
abortCompletion(currentMessageId.current)
}
window.keyv.set(EVENT_NAMES.CHAT_COMPLETION_PAUSED, true)
store.dispatch(setGenerating(false))
}
const onNewContext = () => {
if (loading) return onPause()
if (loading) {
onPause()
return
}
EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT)
}

View File

@ -52,7 +52,6 @@ const MessageStream: React.FC<MessageStreamProps> = ({
// 在hooks调用后进行条件判断
const isStreaming = !!(streamMessage && streamMessage.id === _message.id)
const message = isStreaming ? streamMessage : regularMessage
console.log('isStreaming', isStreaming)
return (
<MessageStreamContainer>
<MessageItem

View File

@ -67,7 +67,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
}, [])
const onClearMessages = useCallback((topic: Topic) => {
window.keyv.set(EVENT_NAMES.CHAT_COMPLETION_PAUSED, true)
// window.keyv.set(EVENT_NAMES.CHAT_COMPLETION_PAUSED, true)
store.dispatch(setGenerating(false))
EventEmitter.emit(EVENT_NAMES.CLEAR_MESSAGES, topic)
}, [])

View File

@ -1,4 +1,5 @@
import { fetchSuggestions } from '@renderer/services/ApiService'
import { getUserMessage } from '@renderer/services/MessagesService'
import { useAppDispatch } from '@renderer/store'
import { sendMessage } from '@renderer/store/messages'
import { Assistant, Message, Suggestion } from '@renderer/types'
@ -6,6 +7,7 @@ import { last } from 'lodash'
import { FC, memo, useEffect, useState } from 'react'
import BeatLoader from 'react-spinners/BeatLoader'
import styled from 'styled-components'
interface Props {
assistant: Assistant
messages: Message[]
@ -22,7 +24,9 @@ const Suggestions: FC<Props> = ({ assistant, messages }) => {
const [loadingSuggestions, setLoadingSuggestions] = useState(false)
const handleSuggestionClick = async (content: string) => {
await dispatch(sendMessage(content, assistant, assistant.topics[0]))
const userMessage = getUserMessage({ assistant, topic: assistant.topics[0], type: 'text', content })
await dispatch(sendMessage(userMessage, assistant, assistant.topics[0]))
}
const suggestionsHandle = async () => {

View File

@ -323,10 +323,10 @@ export default class AnthropicProvider extends BaseProvider {
resolve()
})
.on('error', (error) => reject(error))
}).finally(cleanup)
})
}
await processStream(body)
await processStream(body).finally(cleanup)
}
public async translate(message: Message, assistant: Assistant, onResponse?: (text: string) => void) {

View File

@ -222,7 +222,7 @@ export default class GeminiProvider extends BaseProvider {
const { abortController, cleanup } = this.createAbortController(lastUserMessage?.id)
const { signal } = abortController
const userMessagesStream = await chat.sendMessageStream(messageContents.parts, { signal }).finally(cleanup)
const userMessagesStream = await chat.sendMessageStream(messageContents.parts, { signal })
let time_first_token_millsec = 0
const processStream = async (stream: GenerateContentStreamResult) => {
@ -275,8 +275,8 @@ export default class GeminiProvider extends BaseProvider {
parts: fcallParts
})
const newChat = geminiModel.startChat({ history })
const newStream = await newChat.sendMessageStream(fcRespParts, { signal }).finally(cleanup)
await processStream(newStream)
const newStream = await newChat.sendMessageStream(fcRespParts, { signal })
await processStream(newStream).finally(cleanup)
}
}
@ -298,7 +298,7 @@ export default class GeminiProvider extends BaseProvider {
}
}
await processStream(userMessagesStream)
await processStream(userMessagesStream).finally(cleanup)
}
async translate(message: Message, assistant: Assistant, onResponse?: (text: string) => void) {

View File

@ -428,7 +428,6 @@ export default class OpenAIProvider extends BaseProvider {
signal
}
)
.finally(cleanup)
await processStream(newStream)
}
@ -469,9 +468,8 @@ export default class OpenAIProvider extends BaseProvider {
signal
}
)
.finally(cleanup)
await processStream(stream)
await processStream(stream).finally(cleanup)
}
async translate(message: Message, assistant: Assistant, onResponse?: (text: string) => void) {

View File

@ -3,7 +3,6 @@ import i18n from '@renderer/i18n'
import store from '@renderer/store'
import { setGenerating } from '@renderer/store/runtime'
import { Assistant, Message, Model, Provider, Suggestion } from '@renderer/types'
import { addAbortController } from '@renderer/utils/abortController'
import { formatMessageError } from '@renderer/utils/error'
import { cloneDeep, findLast, isEmpty } from 'lodash'
@ -31,24 +30,15 @@ export async function fetchChatCompletion({
assistant: Assistant
onResponse: (message: Message) => void
}) {
window.keyv.set(EVENT_NAMES.CHAT_COMPLETION_PAUSED, false)
const provider = getAssistantProvider(assistant)
const webSearchProvider = WebSearchService.getWebSearchProvider()
const AI = new AiProvider(provider)
store.dispatch(setGenerating(true))
// store.dispatch(setGenerating(true))
onResponse({ ...message })
// onResponse({ ...message })
const pauseFn = (message: Message) => {
message.status = 'paused'
EventEmitter.emit(EVENT_NAMES.RECEIVE_MESSAGE, message)
store.dispatch(setGenerating(false))
onResponse({ ...message, status: 'paused' })
}
addAbortController(message.askId ?? message.id, pauseFn.bind(null, message))
// addAbortController(message.askId ?? message.id)
try {
let _messages: Message[] = []
@ -136,9 +126,6 @@ export async function fetchChatCompletion({
message.error = formatMessageError(error)
}
// Update message status
message.status = window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED) ? 'paused' : message.status
// Emit chat completion event
EventEmitter.emit(EVENT_NAMES.RECEIVE_MESSAGE, message)
onResponse(message)

View File

@ -3,10 +3,9 @@ import db from '@renderer/databases'
import { TopicManager } from '@renderer/hooks/useTopic'
import { fetchChatCompletion } from '@renderer/services/ApiService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { getAssistantMessage, getUserMessage, resetAssistantMessage } from '@renderer/services/MessagesService'
import { estimateMessageUsage } from '@renderer/services/TokenService'
import { getAssistantMessage, resetAssistantMessage } from '@renderer/services/MessagesService'
import type { AppDispatch, RootState } from '@renderer/store'
import type { Assistant, FileType, MCPServer, Message, Model, Topic } from '@renderer/types'
import type { Assistant, Message, Topic } from '@renderer/types'
import { clearTopicQueue, getTopicQueue, waitForTopicQueue } from '@renderer/utils/queue'
import { throttle } from 'lodash'
@ -205,24 +204,21 @@ export const {
clearStreamMessage
} = messagesSlice.actions
const handleResponseMessageUpdate = (message, topicId, dispatch, getState) => {
dispatch(setStreamMessage({ topicId, message }))
const handleResponseMessageUpdate = (message, topicId, dispatch) => {
// When message is complete, commit to messages and sync with DB
if (message.status !== 'pending') {
if (message.status === 'success') {
EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME)
}
if (message.status !== 'sending') {
dispatch(commitStreamMessage({ topicId, messageId: message.id }))
const state = getState()
const topicMessages = state.messages.messagesByTopic[topicId]
if (topicMessages) {
syncMessagesWithDB(topicId, topicMessages)
}
dispatch(setTopicLoading({ topicId, loading: false }))
}
}
// if (message.status !== 'pending') {
// if (message.status === 'success') {
// EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME)
// }
// if (message.status !== 'sending') {
// dispatch(commitStreamMessage({ topicId, messageId: message.id }))
// const state = getState()
// const topicMessages = state.messages.messagesByTopic[topicId]
// if (topicMessages) {
// syncMessagesWithDB(topicId, topicMessages)
// }
// }
// }
}
// Helper function to sync messages with database
@ -240,16 +236,12 @@ const syncMessagesWithDB = async (topicId: string, messages: Message[]) => {
// Modified sendMessage thunk
export const sendMessage =
(
content: string,
userMessage: Message,
assistant: Assistant,
topic: Topic,
options?: {
files?: FileType[]
knowledgeBaseIds?: string[]
mentionModels?: Model[]
resendUserMessage?: Message
resendAssistantMessage?: Message
enabledMCPs?: MCPServer[]
isMentionModel?: boolean
}
) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
@ -262,41 +254,11 @@ export const sendMessage =
dispatch(clearTopicMessages(topic.id))
}
// 判断是否重发消息
const isResend = !!options?.resendUserMessage
// 使用用户消息
let userMessage: Message
if (isResend) {
userMessage = options.resendUserMessage
} else {
// 创建新的用户消息
userMessage = getUserMessage({ assistant, topic, type: 'text', content })
if (options?.files) {
userMessage.files = options.files
}
if (options?.knowledgeBaseIds) {
userMessage.knowledgeBaseIds = options.knowledgeBaseIds
}
if (options?.mentionModels) {
userMessage.mentions = options.mentionModels
}
if (options?.enabledMCPs) {
userMessage.enabledMCPs = options.enabledMCPs
}
userMessage.usage = await estimateMessageUsage(userMessage)
}
EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE)
// 处理助手消息
// let assistantMessage: Message
let assistantMessages: Message[] = []
// 使用助手消息
if (isResend && options.resendAssistantMessage) {
if (options?.resendAssistantMessage) {
// 直接使用传入的助手消息,进行重置
const messageToReset = options.resendAssistantMessage
const { model, id } = messageToReset
@ -307,9 +269,9 @@ export const sendMessage =
assistantMessages.push(resetMessage)
} else {
// 不是重发情况
// 为每个被 mention 的模型创建一个助手消息
if (options?.mentionModels?.length) {
assistantMessages = options.mentionModels.map((m) => {
if (userMessage.mentions?.length) {
// 为每个被 mention 的模型创建一个助手消息
assistantMessages = userMessage.mentions.map((m) => {
const assistantMessage = getAssistantMessage({ assistant: { ...assistant, model: m }, topic })
assistantMessage.model = m
assistantMessage.askId = userMessage.id
@ -323,20 +285,16 @@ export const sendMessage =
assistantMessage.status = 'sending'
assistantMessages.push(assistantMessage)
}
}
// 如果不是重发
!options?.resendAssistantMessage &&
dispatch(
addMessage({
topicId: topic.id,
messages: !isResend ? [userMessage, ...assistantMessages] : assistantMessages
messages: !options?.isMentionModel ? [userMessage, ...assistantMessages] : assistantMessages
})
)
}
const queue = getTopicQueue(topic.id)
for (const assistantMessage of assistantMessages) {
// console.log('assistantMessage', assistantMessage)
// Set as stream message instead of adding to messages
dispatch(setStreamMessage({ topicId: topic.id, message: assistantMessage }))
@ -348,10 +306,9 @@ export const sendMessage =
await syncMessagesWithDB(topic.id, currentTopicMessages)
}
// 保证请求有序,防止请求静态,限制并发数量
queue.add(async () => {
await queue.add(async () => {
try {
const state = getState()
const messages = state.messages.messagesByTopic[topic.id]
const messages = getState().messages.messagesByTopic[topic.id]
if (!messages) {
dispatch(clearTopicMessages(topic.id))
return
@ -369,7 +326,13 @@ export const sendMessage =
}
// 节流
const throttledDispatch = throttle(handleResponseMessageUpdate, 100, { trailing: true }) // 100ms的节流时间应足够平衡用户体验和性能
const throttledDispatch = throttle(
(topicId, message) => dispatch(setStreamMessage({ topicId, message })),
100,
{ trailing: true }
) // 100ms的节流时间应足够平衡用户体验和性能
let resultMessage: Message = { ...assistantMessage }
await fetchChatCompletion({
message: { ...assistantMessage },
@ -382,12 +345,27 @@ export const sendMessage =
assistant: assistantWithModel,
onResponse: async (msg) => {
// 允许在回调外维护一个最新的消息状态每次都更新这个对象但只通过节流函数分发到Redux
const updatedMsg = { ...msg, status: msg.status || 'pending', content: msg.content || '' }
const updateMessage = { ...msg, status: msg.status || 'pending', content: msg.content || '' }
resultMessage = {
...assistantMessage,
...updateMessage
}
// 创建节流函数限制Redux更新频率
// 使用节流函数更新Redux
throttledDispatch({ ...assistantMessage, ...updatedMsg }, topic.id, dispatch, getState)
throttledDispatch(topic.id, resultMessage)
}
})
if (resultMessage?.status === 'success') {
EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME)
}
if (resultMessage?.status !== 'sending') {
dispatch(commitStreamMessage({ topicId: topic.id, messageId: assistantMessage.id }))
const state = getState()
const topicMessages = state.messages.messagesByTopic[topic.id]
if (topicMessages) {
syncMessagesWithDB(topic.id, topicMessages)
}
}
} catch (error: any) {
console.error('Error in chat completion:', error)
dispatch(
@ -402,6 +380,9 @@ export const sendMessage =
}
})
}
// 等待所有请求完成,设置loading
await queue.onIdle()
dispatch(setTopicLoading({ topicId: topic.id, loading: false }))
} catch (error: any) {
console.error('Error in sendMessage:', error)
dispatch(setError(error.message))
@ -425,8 +406,7 @@ export const resendMessage =
const assistantMessage = topicMessages.find((m) => m.role === 'assistant' && m.askId === message.id)
return dispatch(
sendMessage(message.content, assistant, topic, {
resendUserMessage: message,
sendMessage(message, assistant, topic, {
resendAssistantMessage: assistantMessage
})
)
@ -434,55 +414,38 @@ export const resendMessage =
// 如果是助手消息,找到对应的用户消息
const userMessage = topicMessages.find((m) => m.id === message.askId && m.role === 'user')
console.log('topicMessages,topicMessages', topicMessages)
if (!userMessage) {
console.error('Cannot find original user message to resend')
return dispatch(setError('Cannot find original user message to resend'))
}
if (isMentionModel) {
// @
return dispatch(
sendMessage(userMessage.content, assistant, topic, {
resendUserMessage: userMessage
})
)
// @,追加助手消息
return dispatch(sendMessage(userMessage, assistant, topic, { isMentionModel }))
}
dispatch(
sendMessage(userMessage.content, assistant, topic, {
resendUserMessage: userMessage,
sendMessage(userMessage, assistant, topic, {
resendAssistantMessage: message
})
)
} catch (error: any) {
console.error('Error in resendMessage:', error)
dispatch(setError(error.message))
} finally {
dispatch(setTopicLoading({ topicId: topic.id, loading: false }))
}
}
// Modified loadTopicMessages thunk
export const loadTopicMessagesThunk = (topic: Topic) => async (dispatch: AppDispatch) => {
// 设置会话的loading状态
dispatch(setTopicLoading({ topicId: topic.id, loading: true }))
dispatch(setCurrentTopic(topic))
try {
// 设置会话的loading状态
dispatch(setTopicLoading({ topicId: topic.id, loading: true }))
dispatch(setCurrentTopic(topic))
try {
// 使用 getTopic 获取会话对象
const topicWithDB = await TopicManager.getTopic(topic.id)
if (topicWithDB) {
// 如果数据库中有会话,加载消息,保存会话
dispatch(loadTopicMessages({ topicId: topic.id, messages: topicWithDB.messages }))
}
// else {
// // 如果找不到,可以将当前会话设为 null
// dispatch(setCurrentTopic(null))
// }
} catch (error) {
console.error('Failed to get complete topic:', error)
dispatch(setCurrentTopic(null))
// 使用 getTopic 获取会话对象
const topicWithDB = await TopicManager.getTopic(topic.id)
if (topicWithDB) {
// 如果数据库中有会话,加载消息,保存会话
dispatch(loadTopicMessages({ topicId: topic.id, messages: topicWithDB.messages }))
}
} catch (error) {
dispatch(setError(error instanceof Error ? error.message : 'Failed to load messages'))
@ -491,7 +454,6 @@ export const loadTopicMessagesThunk = (topic: Topic) => async (dispatch: AppDisp
dispatch(setTopicLoading({ topicId: topic.id, loading: false }))
}
}
// Modified clearMessages thunk
export const clearTopicMessagesThunk = (topic: Topic) => async (dispatch: AppDispatch) => {
try {
@ -521,9 +483,6 @@ export const clearTopicMessagesThunk = (topic: Topic) => async (dispatch: AppDis
// 修改的 updateMessages thunk同时更新缓存
export const updateMessages = (topic: Topic, messages: Message[]) => async (dispatch: AppDispatch) => {
try {
// 设置会话的loading状态
dispatch(setTopicLoading({ topicId: topic.id, loading: true }))
// 更新数据库
await db.topics.update(topic.id, { messages })
@ -531,9 +490,6 @@ export const updateMessages = (topic: Topic, messages: Message[]) => async (disp
dispatch(loadTopicMessages({ topicId: topic.id, messages }))
} catch (error) {
dispatch(setError(error instanceof Error ? error.message : 'Failed to update messages'))
} finally {
// 清除会话的loading状态
dispatch(setTopicLoading({ topicId: topic.id, loading: false }))
}
}