From 151a08d0dd32ad9b1c5c78dd2ecf18073c62e4bf Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Tue, 11 Mar 2025 20:44:06 +0800 Subject: [PATCH] feat(Topic): Implement auto-renaming topic with enhanced logic - Add `autoRenameTopic` function in useTopic hook - Support automatic topic naming with or without AI summary - Integrate with store and event system for dynamic topic renaming - Remove local implementation of auto-rename in Messages component --- src/renderer/src/hooks/useTopic.ts | 36 +++++++++++++- .../src/pages/home/Messages/Messages.tsx | 49 ++----------------- src/renderer/src/store/messages.ts | 6 ++- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/renderer/src/hooks/useTopic.ts b/src/renderer/src/hooks/useTopic.ts index 3db8d62c..d3cab75a 100644 --- a/src/renderer/src/hooks/useTopic.ts +++ b/src/renderer/src/hooks/useTopic.ts @@ -1,20 +1,25 @@ import db from '@renderer/databases' +import i18n from '@renderer/i18n' import { deleteMessageFiles } from '@renderer/services/MessagesService' import store from '@renderer/store' +import { updateTopic } from '@renderer/store/assistants' import { prepareTopicMessages } from '@renderer/store/messages' import { Assistant, Topic } from '@renderer/types' -import { find } from 'lodash' +import { find, isEmpty } from 'lodash' import { useEffect, useState } from 'react' import { useAssistant } from './useAssistant' +import { getStoreSetting } from './useSettings' let _activeTopic: Topic +let _setActiveTopic: (topic: Topic) => void export function useActiveTopic(_assistant: Assistant, topic?: Topic) { const { assistant } = useAssistant(_assistant.id) const [activeTopic, setActiveTopic] = useState(topic || _activeTopic || assistant?.topics[0]) _activeTopic = activeTopic + _setActiveTopic = setActiveTopic useEffect(() => { if (activeTopic) { @@ -48,6 +53,35 @@ export async function getTopicById(topicId: string) { return { ...topic, messages } as Topic } +export const autoRenameTopic = async (assistant: Assistant, topicId: string) => { + const topic = await getTopicById(topicId) + const enableTopicNaming = getStoreSetting('enableTopicNaming') + + if (isEmpty(topic.messages)) { + return + } + + if (!enableTopicNaming) { + const topicName = topic.messages[0]?.content.substring(0, 50) + if (topicName) { + const data = { ...topic, name: topicName } as Topic + _setActiveTopic(data) + store.dispatch(updateTopic({ assistantId: assistant.id, topic: data })) + } + return + } + + if (topic && topic.name === i18n.t('chat.default.topic.name') && topic.messages.length >= 2) { + const { fetchMessagesSummary } = await import('@renderer/services/ApiService') + const summaryText = await fetchMessagesSummary({ messages: topic.messages, assistant }) + if (summaryText) { + const data = { ...topic, name: summaryText } + _setActiveTopic(data) + store.dispatch(updateTopic({ assistantId: assistant.id, topic: data })) + } + } +} + // Convert class to object with functions since class only has static methods // 只有静态方法,没必要用class,可以export {} export const TopicManager = { diff --git a/src/renderer/src/pages/home/Messages/Messages.tsx b/src/renderer/src/pages/home/Messages/Messages.tsx index cb9fae58..f563e303 100644 --- a/src/renderer/src/pages/home/Messages/Messages.tsx +++ b/src/renderer/src/pages/home/Messages/Messages.tsx @@ -5,8 +5,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant' import { useMessageOperations } from '@renderer/hooks/useMessageOperations' import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' -import { getTopic } from '@renderer/hooks/useTopic' -import { fetchMessagesSummary } from '@renderer/services/ApiService' +import { autoRenameTopic, getTopic } from '@renderer/hooks/useTopic' import { getDefaultTopic } from '@renderer/services/AssistantService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { getContextCount, getGroupedMessages, getUserMessage } from '@renderer/services/MessagesService' @@ -19,7 +18,7 @@ import { removeSpecialCharactersForFileName, runAsyncFunction } from '@renderer/utils' -import { flatten, isEmpty, last, take } from 'lodash' +import { flatten, last, take } from 'lodash' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import InfiniteScroll from 'react-infinite-scroll-component' @@ -39,7 +38,7 @@ interface MessagesProps { const Messages: React.FC = ({ assistant, topic, setActiveTopic }) => { const { t } = useTranslation() - const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings() + const { showTopics, topicPosition, showAssistants } = useSettings() const { updateTopic, addTopic } = useAssistant(assistant.id) const dispatch = useAppDispatch() const containerRef = useRef(null) @@ -67,36 +66,6 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) setTimeout(() => containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'auto' }), 50) }, []) - const autoRenameTopic = useCallback(async () => { - const _topic = getTopic(assistant, topic.id) - - if (isEmpty(messages)) { - return - } - - const filteredMessages = messages.filter((m) => m.status === 'success') - - if (!enableTopicNaming) { - const topicName = filteredMessages[0]?.content.substring(0, 50) - if (topicName) { - const data = { ..._topic, name: topicName } as Topic - setActiveTopic(data) - updateTopic(data) - } - return - } - - if (_topic && _topic.name === t('chat.default.topic.name') && filteredMessages.length >= 2) { - const summaryText = await fetchMessagesSummary({ messages: filteredMessages, assistant }) - if (summaryText) { - const data = { ..._topic, name: summaryText } - setActiveTopic(data) - updateTopic(data) - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [assistant, topic.id, enableTopicNaming, t, setActiveTopic, updateTopic, messages]) - useEffect(() => { const unsubscribes = [ EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, () => { @@ -157,7 +126,7 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) await db.topics.add({ id: newTopic.id, messages: branchMessages }) addTopic(newTopic) setActiveTopic(newTopic) - autoRenameTopic() + autoRenameTopic(assistant, newTopic.id) // 由于复制了消息,消息中附带的文件的总数变了,需要更新 const filesArr = branchMessages.map((m) => m.files) @@ -171,17 +140,9 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) ] return () => unsubscribes.forEach((unsub) => unsub()) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [assistant, dispatch, scrollToBottom, topic, updateTopic]) - useEffect(() => { - const unsubscribes = [EventEmitter.on(EVENT_NAMES.AI_AUTO_RENAME, autoRenameTopic)] - return () => { - for (const unsub of unsubscribes) { - unsub() - } - } - }, [autoRenameTopic]) - useEffect(() => { runAsyncFunction(async () => { EventEmitter.emit(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, { diff --git a/src/renderer/src/store/messages.ts b/src/renderer/src/store/messages.ts index 8d60814f..54365d4b 100644 --- a/src/renderer/src/store/messages.ts +++ b/src/renderer/src/store/messages.ts @@ -1,6 +1,6 @@ import { createAsyncThunk, createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit' import db from '@renderer/databases' -import { TopicManager } from '@renderer/hooks/useTopic' +import { autoRenameTopic, TopicManager } from '@renderer/hooks/useTopic' import { fetchChatCompletion } from '@renderer/services/ApiService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { getAssistantMessage, resetAssistantMessage } from '@renderer/services/MessagesService' @@ -205,6 +205,7 @@ export const { } = messagesSlice.actions const handleResponseMessageUpdate = ( + assistant: Assistant, message: Message, topicId: string, dispatch: AppDispatch, @@ -214,7 +215,7 @@ const handleResponseMessageUpdate = ( if (message.status !== 'pending') { // When message is complete, commit to messages and sync with DB if (message.status === 'success') { - EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME) + autoRenameTopic(assistant, topicId) } if (message.status !== 'sending') { dispatch(commitStreamMessage({ topicId, messageId: message.id })) @@ -347,6 +348,7 @@ export const sendMessage = // 创建节流函数,限制Redux更新频率 // 使用节流函数更新Redux throttledDispatch( + assistant, { ...assistantMessage, ...updateMessage