refactor(assistant): enhance assistant creation by automatically adding default topics; streamline default assistant handling

This commit is contained in:
suyao 2025-06-12 21:03:17 +08:00
parent cb1fcf7d2d
commit d41f175a05
No known key found for this signature in database
5 changed files with 29 additions and 81 deletions

View File

@ -1,5 +1,4 @@
import { db } from '@renderer/databases'
import { getDefaultTopic } from '@renderer/services/AssistantService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
addAssistant,
@ -22,7 +21,10 @@ export function useAssistants() {
return {
assistants,
updateAssistants: (assistants: Assistant[]) => dispatch(updateAssistants(assistants)),
addAssistant: (assistant: Assistant) => dispatch(addAssistant(assistant)),
addAssistant: (assistant: Assistant) => {
dispatch(addAssistant(assistant))
dispatch(topicsActions.addDefaultTopic({ assistantId: assistant.id }))
},
removeAssistant: (id: string) => {
dispatch(removeAssistant({ id }))
// Remove all topics for this assistant
@ -85,24 +87,15 @@ export function useTopicsForAssistant(assistantId: string) {
return useAppSelector((state) => selectTopicsForAssistant(state, assistantId))
}
/**
*
*/
export function useDefaultAssistant() {
const defaultAssistant = useAppSelector((state) => state.assistants.defaultAssistant)
const topics = useTopicsForAssistant(defaultAssistant.id)
const dispatch = useAppDispatch()
// Ensure default assistant has at least one topic
const finalTopics = useMemo(() => {
if (topics.length > 0) {
return topics
}
return [getDefaultTopic(defaultAssistant.id)]
}, [topics, defaultAssistant.id])
return {
defaultAssistant: {
...defaultAssistant,
topics: finalTopics
},
defaultAssistant,
updateDefaultAssistant: (assistant: Assistant) => dispatch(updateDefaultAssistant({ assistant }))
}
}

View File

@ -2,6 +2,7 @@ import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@
import i18n from '@renderer/i18n'
import store from '@renderer/store'
import { addAssistant } from '@renderer/store/assistants'
import { topicsActions } from '@renderer/store/topics'
import type { Agent, Assistant, AssistantSettings, Model, Provider, Topic } from '@renderer/types'
import { uuid } from '@renderer/utils'
@ -11,7 +12,7 @@ export function getDefaultAssistant(): Assistant {
name: i18n.t('chat.default.name'),
emoji: '😀',
prompt: '',
topics: [getDefaultTopic('default')],
topics: [],
messages: [],
type: 'assistant',
regularPhrases: [] // Added regularPhrases
@ -124,13 +125,13 @@ export async function createAssistantFromAgent(agent: Agent) {
id: assistantId,
name: agent.name,
emoji: agent.emoji,
topics: [topic],
model: agent.defaultModel,
type: 'assistant',
regularPhrases: agent.regularPhrases || [] // Ensured regularPhrases
}
store.dispatch(addAssistant(assistant))
store.dispatch(topicsActions.addTopic({ assistantId, topic }))
window.message.success({
content: i18n.t('message.assistant.added.content'),

View File

@ -9,14 +9,9 @@ export interface AssistantsState {
tagsOrder: string[]
}
// 之前的两个实例会导致两个助手不一致的问题
// FIXME: 更彻底的办法在这次重构就直接把二者合并了
// Create a single default assistant instance to ensure consistency
const defaultAssistant = getDefaultAssistant()
const initialState: AssistantsState = {
defaultAssistant: defaultAssistant,
assistants: [defaultAssistant], // Share the same reference
defaultAssistant: getDefaultAssistant(), // 这个是模型设置的默认助手
assistants: [getDefaultAssistant()], // 这个是主页列表的默认助手
tagsOrder: []
}
@ -27,21 +22,9 @@ const assistantsSlice = createSlice({
updateDefaultAssistant: (state, action: PayloadAction<{ assistant: Assistant }>) => {
const assistant = action.payload.assistant
state.defaultAssistant = assistant
// Also update the corresponding assistant in the array
const index = state.assistants.findIndex((a) => a.id === assistant.id)
if (index !== -1) {
state.assistants[index] = assistant
}
},
updateAssistants: (state, action: PayloadAction<Assistant[]>) => {
state.assistants = action.payload
// Update defaultAssistant if it exists in the new array
const defaultInArray = action.payload.find((a) => a.id === state.defaultAssistant.id)
if (defaultInArray) {
state.defaultAssistant = defaultInArray
}
},
addAssistant: (state, action: PayloadAction<Assistant>) => {
state.assistants.push(action.payload)
@ -52,11 +35,6 @@ const assistantsSlice = createSlice({
updateAssistant: (state, action: PayloadAction<Assistant>) => {
const assistant = action.payload
state.assistants = state.assistants.map((c) => (c.id === assistant.id ? assistant : c))
// Also update defaultAssistant if it's the same assistant
if (state.defaultAssistant.id === assistant.id) {
state.defaultAssistant = assistant
}
},
updateAssistantSettings: (
state,
@ -91,14 +69,6 @@ const assistantsSlice = createSlice({
}
: assistant
)
// Also update defaultAssistant if it's the same assistant
if (state.defaultAssistant.id === assistantId) {
state.defaultAssistant = {
...state.defaultAssistant,
model: model
}
}
},
setTagsOrder: (state, action: PayloadAction<string[]>) => {
state.tagsOrder = action.payload

View File

@ -1570,26 +1570,21 @@ const migrateConfig = {
},
'113': (state: RootState) => {
try {
// Step 1: Merge defaultAssistant and assistants[0] topics to ensure consistency
// This fixes any inconsistencies from backup restores or previous versions
// Step 1: 把默认助手模板下面的话题合并到主页列表的默认助手Id下面保持默认助手模板的纯粹性
if (state.assistants?.defaultAssistant && state.assistants?.assistants?.length > 0) {
const defaultAssistantId = state.assistants.defaultAssistant.id
const defaultAssistantId = state['assistants'].defaultAssistant.id
const defaultAssistantInArray = state.assistants.assistants.find((a) => a.id === defaultAssistantId)
if (defaultAssistantInArray) {
// Merge topics from both defaultAssistant and assistants[0]
const defaultTopics = state.assistants.defaultAssistant.topics || []
const defaultTopics = state['assistants'].defaultAssistant.topics || []
const arrayTopics = defaultAssistantInArray.topics || []
// Create a map to avoid duplicates (by topic id)
const topicsMap = new Map<string, Topic>()
// Add topics from both sources
const allTopics = [...defaultTopics, ...arrayTopics]
const allTopics = [...arrayTopics, ...defaultTopics]
allTopics.forEach((topic) => {
if (topic && topic.id) {
// Keep the one with more recent updatedAt, or prefer the one from assistants array
const existing = topicsMap.get(topic.id)
if (
!existing ||
@ -1603,19 +1598,14 @@ const migrateConfig = {
const mergedTopics = Array.from(topicsMap.values())
// Update both defaultAssistant and the assistant in array
state.assistants.defaultAssistant.topics = mergedTopics
state['assistants'].defaultAssistant.topics = []
defaultAssistantInArray.topics = mergedTopics
} else {
// defaultAssistant not found in array, add it
state.assistants.assistants.unshift(state.assistants.defaultAssistant)
// 如果默认助手不存在,说明被用户删掉了
}
}
// Step 2: Migrate from nested topic structure to flattened topic structure
// This should run after v112 which ensures defaultAssistant and assistants[0] consistency
// Initialize the new topics slice if it doesn't exist
// Step 2: 迁移话题结构,从嵌套结构迁移到扁平结构
if (!state.topics) {
state.topics = {
ids: [],
@ -1632,8 +1622,8 @@ const migrateConfig = {
const topicIdsByAssistant: Record<string, string[]> = {}
// Process regular assistants
if (state.assistants?.assistants) {
state.assistants.assistants.forEach((assistant) => {
if (state['assistants'].assistants && state['assistants'].assistants.length > 0) {
state['assistants'].assistants.forEach((assistant) => {
const legacyAssistant = assistant as LegacyAssistant
if (legacyAssistant.topics && Array.isArray(legacyAssistant.topics) && legacyAssistant.topics.length > 0) {
allTopics.push(...legacyAssistant.topics)
@ -1653,14 +1643,6 @@ const migrateConfig = {
})
}
// Process default assistant - should already be consistent after v112
if (state.assistants?.defaultAssistant) {
const legacyDefaultAssistant = state.assistants.defaultAssistant as LegacyAssistant
// Since v112 already ensured consistency, just clear the deprecated field
legacyDefaultAssistant.topics = []
}
// Populate the new topics slice
const topicEntities: Record<string, Topic> = {}
const topicIds: string[] = []
@ -1670,7 +1652,6 @@ const migrateConfig = {
topicIds.push(topic.id)
})
// Update topics slice
state.topics = {
ids: topicIds,
entities: topicEntities,
@ -1679,7 +1660,6 @@ const migrateConfig = {
return state
} catch (error) {
console.error('Migration 112 failed:', error)
return state
}
}

View File

@ -130,9 +130,7 @@ const topicsSlice = createSlice({
topicsAdapter.removeMany(state, topicIds)
// Create default topic
const defaultTopic = getDefaultTopic(assistantId)
topicsAdapter.addOne(state, defaultTopic)
state.topicIdsByAssistant[assistantId] = [defaultTopic.id]
topicsActions.addDefaultTopic({ assistantId })
},
moveTopic(state, action: PayloadAction<MoveTopicPayload>) {
const { fromAssistantId, toAssistantId, topicId } = action.payload
@ -154,6 +152,12 @@ const topicsSlice = createSlice({
state.topicIdsByAssistant[toAssistantId] = []
}
state.topicIdsByAssistant[toAssistantId].unshift(topicId)
},
addDefaultTopic(state, action: PayloadAction<{ assistantId: string }>) {
const { assistantId } = action.payload
const defaultTopic = getDefaultTopic(assistantId)
topicsAdapter.addOne(state, defaultTopic)
state.topicIdsByAssistant[assistantId] = [defaultTopic.id]
}
}
})