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
This commit is contained in:
parent
74567d5e17
commit
151a08d0dd
@ -1,20 +1,25 @@
|
|||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
|
import i18n from '@renderer/i18n'
|
||||||
import { deleteMessageFiles } from '@renderer/services/MessagesService'
|
import { deleteMessageFiles } from '@renderer/services/MessagesService'
|
||||||
import store from '@renderer/store'
|
import store from '@renderer/store'
|
||||||
|
import { updateTopic } from '@renderer/store/assistants'
|
||||||
import { prepareTopicMessages } from '@renderer/store/messages'
|
import { prepareTopicMessages } from '@renderer/store/messages'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { find } from 'lodash'
|
import { find, isEmpty } from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { useAssistant } from './useAssistant'
|
import { useAssistant } from './useAssistant'
|
||||||
|
import { getStoreSetting } from './useSettings'
|
||||||
|
|
||||||
let _activeTopic: Topic
|
let _activeTopic: Topic
|
||||||
|
let _setActiveTopic: (topic: Topic) => void
|
||||||
|
|
||||||
export function useActiveTopic(_assistant: Assistant, topic?: Topic) {
|
export function useActiveTopic(_assistant: Assistant, topic?: Topic) {
|
||||||
const { assistant } = useAssistant(_assistant.id)
|
const { assistant } = useAssistant(_assistant.id)
|
||||||
const [activeTopic, setActiveTopic] = useState(topic || _activeTopic || assistant?.topics[0])
|
const [activeTopic, setActiveTopic] = useState(topic || _activeTopic || assistant?.topics[0])
|
||||||
|
|
||||||
_activeTopic = activeTopic
|
_activeTopic = activeTopic
|
||||||
|
_setActiveTopic = setActiveTopic
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTopic) {
|
if (activeTopic) {
|
||||||
@ -48,6 +53,35 @@ export async function getTopicById(topicId: string) {
|
|||||||
return { ...topic, messages } as Topic
|
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
|
// Convert class to object with functions since class only has static methods
|
||||||
// 只有静态方法,没必要用class,可以export {}
|
// 只有静态方法,没必要用class,可以export {}
|
||||||
export const TopicManager = {
|
export const TopicManager = {
|
||||||
|
|||||||
@ -5,8 +5,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
|
|||||||
import { useMessageOperations } from '@renderer/hooks/useMessageOperations'
|
import { useMessageOperations } from '@renderer/hooks/useMessageOperations'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
||||||
import { getTopic } from '@renderer/hooks/useTopic'
|
import { autoRenameTopic, getTopic } from '@renderer/hooks/useTopic'
|
||||||
import { fetchMessagesSummary } from '@renderer/services/ApiService'
|
|
||||||
import { getDefaultTopic } from '@renderer/services/AssistantService'
|
import { getDefaultTopic } from '@renderer/services/AssistantService'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { getContextCount, getGroupedMessages, getUserMessage } from '@renderer/services/MessagesService'
|
import { getContextCount, getGroupedMessages, getUserMessage } from '@renderer/services/MessagesService'
|
||||||
@ -19,7 +18,7 @@ import {
|
|||||||
removeSpecialCharactersForFileName,
|
removeSpecialCharactersForFileName,
|
||||||
runAsyncFunction
|
runAsyncFunction
|
||||||
} from '@renderer/utils'
|
} 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 { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||||
@ -39,7 +38,7 @@ interface MessagesProps {
|
|||||||
|
|
||||||
const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic }) => {
|
const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings()
|
const { showTopics, topicPosition, showAssistants } = useSettings()
|
||||||
const { updateTopic, addTopic } = useAssistant(assistant.id)
|
const { updateTopic, addTopic } = useAssistant(assistant.id)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
@ -67,36 +66,6 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
|
|||||||
setTimeout(() => containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'auto' }), 50)
|
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(() => {
|
useEffect(() => {
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, () => {
|
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, () => {
|
||||||
@ -157,7 +126,7 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
|
|||||||
await db.topics.add({ id: newTopic.id, messages: branchMessages })
|
await db.topics.add({ id: newTopic.id, messages: branchMessages })
|
||||||
addTopic(newTopic)
|
addTopic(newTopic)
|
||||||
setActiveTopic(newTopic)
|
setActiveTopic(newTopic)
|
||||||
autoRenameTopic()
|
autoRenameTopic(assistant, newTopic.id)
|
||||||
|
|
||||||
// 由于复制了消息,消息中附带的文件的总数变了,需要更新
|
// 由于复制了消息,消息中附带的文件的总数变了,需要更新
|
||||||
const filesArr = branchMessages.map((m) => m.files)
|
const filesArr = branchMessages.map((m) => m.files)
|
||||||
@ -171,17 +140,9 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
|
|||||||
]
|
]
|
||||||
|
|
||||||
return () => unsubscribes.forEach((unsub) => unsub())
|
return () => unsubscribes.forEach((unsub) => unsub())
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [assistant, dispatch, scrollToBottom, topic, updateTopic])
|
}, [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(() => {
|
useEffect(() => {
|
||||||
runAsyncFunction(async () => {
|
runAsyncFunction(async () => {
|
||||||
EventEmitter.emit(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, {
|
EventEmitter.emit(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createAsyncThunk, createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'
|
import { createAsyncThunk, createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'
|
||||||
import db from '@renderer/databases'
|
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 { fetchChatCompletion } from '@renderer/services/ApiService'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { getAssistantMessage, resetAssistantMessage } from '@renderer/services/MessagesService'
|
import { getAssistantMessage, resetAssistantMessage } from '@renderer/services/MessagesService'
|
||||||
@ -205,6 +205,7 @@ export const {
|
|||||||
} = messagesSlice.actions
|
} = messagesSlice.actions
|
||||||
|
|
||||||
const handleResponseMessageUpdate = (
|
const handleResponseMessageUpdate = (
|
||||||
|
assistant: Assistant,
|
||||||
message: Message,
|
message: Message,
|
||||||
topicId: string,
|
topicId: string,
|
||||||
dispatch: AppDispatch,
|
dispatch: AppDispatch,
|
||||||
@ -214,7 +215,7 @@ const handleResponseMessageUpdate = (
|
|||||||
if (message.status !== 'pending') {
|
if (message.status !== 'pending') {
|
||||||
// When message is complete, commit to messages and sync with DB
|
// When message is complete, commit to messages and sync with DB
|
||||||
if (message.status === 'success') {
|
if (message.status === 'success') {
|
||||||
EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME)
|
autoRenameTopic(assistant, topicId)
|
||||||
}
|
}
|
||||||
if (message.status !== 'sending') {
|
if (message.status !== 'sending') {
|
||||||
dispatch(commitStreamMessage({ topicId, messageId: message.id }))
|
dispatch(commitStreamMessage({ topicId, messageId: message.id }))
|
||||||
@ -347,6 +348,7 @@ export const sendMessage =
|
|||||||
// 创建节流函数,限制Redux更新频率
|
// 创建节流函数,限制Redux更新频率
|
||||||
// 使用节流函数更新Redux
|
// 使用节流函数更新Redux
|
||||||
throttledDispatch(
|
throttledDispatch(
|
||||||
|
assistant,
|
||||||
{
|
{
|
||||||
...assistantMessage,
|
...assistantMessage,
|
||||||
...updateMessage
|
...updateMessage
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user