mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-12 00:49:14 +08:00
refactor(assistant): enhance assistant creation by automatically adding default topics; streamline default assistant handling
This commit is contained in:
parent
cb1fcf7d2d
commit
d41f175a05
@ -1,5 +1,4 @@
|
|||||||
import { db } from '@renderer/databases'
|
import { db } from '@renderer/databases'
|
||||||
import { getDefaultTopic } from '@renderer/services/AssistantService'
|
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import {
|
import {
|
||||||
addAssistant,
|
addAssistant,
|
||||||
@ -22,7 +21,10 @@ export function useAssistants() {
|
|||||||
return {
|
return {
|
||||||
assistants,
|
assistants,
|
||||||
updateAssistants: (assistants: Assistant[]) => dispatch(updateAssistants(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) => {
|
removeAssistant: (id: string) => {
|
||||||
dispatch(removeAssistant({ id }))
|
dispatch(removeAssistant({ id }))
|
||||||
// Remove all topics for this assistant
|
// Remove all topics for this assistant
|
||||||
@ -85,24 +87,15 @@ export function useTopicsForAssistant(assistantId: string) {
|
|||||||
return useAppSelector((state) => selectTopicsForAssistant(state, assistantId))
|
return useAppSelector((state) => selectTopicsForAssistant(state, assistantId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认助手模板
|
||||||
|
*/
|
||||||
export function useDefaultAssistant() {
|
export function useDefaultAssistant() {
|
||||||
const defaultAssistant = useAppSelector((state) => state.assistants.defaultAssistant)
|
const defaultAssistant = useAppSelector((state) => state.assistants.defaultAssistant)
|
||||||
const topics = useTopicsForAssistant(defaultAssistant.id)
|
|
||||||
const dispatch = useAppDispatch()
|
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 {
|
return {
|
||||||
defaultAssistant: {
|
defaultAssistant,
|
||||||
...defaultAssistant,
|
|
||||||
topics: finalTopics
|
|
||||||
},
|
|
||||||
updateDefaultAssistant: (assistant: Assistant) => dispatch(updateDefaultAssistant({ assistant }))
|
updateDefaultAssistant: (assistant: Assistant) => dispatch(updateDefaultAssistant({ assistant }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@
|
|||||||
import i18n from '@renderer/i18n'
|
import i18n from '@renderer/i18n'
|
||||||
import store from '@renderer/store'
|
import store from '@renderer/store'
|
||||||
import { addAssistant } from '@renderer/store/assistants'
|
import { addAssistant } from '@renderer/store/assistants'
|
||||||
|
import { topicsActions } from '@renderer/store/topics'
|
||||||
import type { Agent, Assistant, AssistantSettings, Model, Provider, Topic } from '@renderer/types'
|
import type { Agent, Assistant, AssistantSettings, Model, Provider, Topic } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ export function getDefaultAssistant(): Assistant {
|
|||||||
name: i18n.t('chat.default.name'),
|
name: i18n.t('chat.default.name'),
|
||||||
emoji: '😀',
|
emoji: '😀',
|
||||||
prompt: '',
|
prompt: '',
|
||||||
topics: [getDefaultTopic('default')],
|
topics: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
type: 'assistant',
|
type: 'assistant',
|
||||||
regularPhrases: [] // Added regularPhrases
|
regularPhrases: [] // Added regularPhrases
|
||||||
@ -124,13 +125,13 @@ export async function createAssistantFromAgent(agent: Agent) {
|
|||||||
id: assistantId,
|
id: assistantId,
|
||||||
name: agent.name,
|
name: agent.name,
|
||||||
emoji: agent.emoji,
|
emoji: agent.emoji,
|
||||||
topics: [topic],
|
|
||||||
model: agent.defaultModel,
|
model: agent.defaultModel,
|
||||||
type: 'assistant',
|
type: 'assistant',
|
||||||
regularPhrases: agent.regularPhrases || [] // Ensured regularPhrases
|
regularPhrases: agent.regularPhrases || [] // Ensured regularPhrases
|
||||||
}
|
}
|
||||||
|
|
||||||
store.dispatch(addAssistant(assistant))
|
store.dispatch(addAssistant(assistant))
|
||||||
|
store.dispatch(topicsActions.addTopic({ assistantId, topic }))
|
||||||
|
|
||||||
window.message.success({
|
window.message.success({
|
||||||
content: i18n.t('message.assistant.added.content'),
|
content: i18n.t('message.assistant.added.content'),
|
||||||
|
|||||||
@ -9,14 +9,9 @@ export interface AssistantsState {
|
|||||||
tagsOrder: string[]
|
tagsOrder: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 之前的两个实例会导致两个助手不一致的问题
|
|
||||||
// FIXME: 更彻底的办法在这次重构就直接把二者合并了
|
|
||||||
// Create a single default assistant instance to ensure consistency
|
|
||||||
const defaultAssistant = getDefaultAssistant()
|
|
||||||
|
|
||||||
const initialState: AssistantsState = {
|
const initialState: AssistantsState = {
|
||||||
defaultAssistant: defaultAssistant,
|
defaultAssistant: getDefaultAssistant(), // 这个是模型设置的默认助手
|
||||||
assistants: [defaultAssistant], // Share the same reference
|
assistants: [getDefaultAssistant()], // 这个是主页列表的默认助手
|
||||||
tagsOrder: []
|
tagsOrder: []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,21 +22,9 @@ const assistantsSlice = createSlice({
|
|||||||
updateDefaultAssistant: (state, action: PayloadAction<{ assistant: Assistant }>) => {
|
updateDefaultAssistant: (state, action: PayloadAction<{ assistant: Assistant }>) => {
|
||||||
const assistant = action.payload.assistant
|
const assistant = action.payload.assistant
|
||||||
state.defaultAssistant = 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[]>) => {
|
updateAssistants: (state, action: PayloadAction<Assistant[]>) => {
|
||||||
state.assistants = action.payload
|
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>) => {
|
addAssistant: (state, action: PayloadAction<Assistant>) => {
|
||||||
state.assistants.push(action.payload)
|
state.assistants.push(action.payload)
|
||||||
@ -52,11 +35,6 @@ const assistantsSlice = createSlice({
|
|||||||
updateAssistant: (state, action: PayloadAction<Assistant>) => {
|
updateAssistant: (state, action: PayloadAction<Assistant>) => {
|
||||||
const assistant = action.payload
|
const assistant = action.payload
|
||||||
state.assistants = state.assistants.map((c) => (c.id === assistant.id ? assistant : c))
|
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: (
|
updateAssistantSettings: (
|
||||||
state,
|
state,
|
||||||
@ -91,14 +69,6 @@ const assistantsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
: assistant
|
: 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[]>) => {
|
setTagsOrder: (state, action: PayloadAction<string[]>) => {
|
||||||
state.tagsOrder = action.payload
|
state.tagsOrder = action.payload
|
||||||
|
|||||||
@ -1570,26 +1570,21 @@ const migrateConfig = {
|
|||||||
},
|
},
|
||||||
'113': (state: RootState) => {
|
'113': (state: RootState) => {
|
||||||
try {
|
try {
|
||||||
// Step 1: Merge defaultAssistant and assistants[0] topics to ensure consistency
|
// Step 1: 把默认助手模板下面的话题合并到主页列表的默认助手Id下面,保持默认助手模板的纯粹性
|
||||||
// This fixes any inconsistencies from backup restores or previous versions
|
|
||||||
|
|
||||||
if (state.assistants?.defaultAssistant && state.assistants?.assistants?.length > 0) {
|
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)
|
const defaultAssistantInArray = state.assistants.assistants.find((a) => a.id === defaultAssistantId)
|
||||||
|
|
||||||
if (defaultAssistantInArray) {
|
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 || []
|
const arrayTopics = defaultAssistantInArray.topics || []
|
||||||
|
|
||||||
// Create a map to avoid duplicates (by topic id)
|
|
||||||
const topicsMap = new Map<string, Topic>()
|
const topicsMap = new Map<string, Topic>()
|
||||||
|
|
||||||
// Add topics from both sources
|
const allTopics = [...arrayTopics, ...defaultTopics]
|
||||||
const allTopics = [...defaultTopics, ...arrayTopics]
|
|
||||||
allTopics.forEach((topic) => {
|
allTopics.forEach((topic) => {
|
||||||
if (topic && topic.id) {
|
if (topic && topic.id) {
|
||||||
// Keep the one with more recent updatedAt, or prefer the one from assistants array
|
|
||||||
const existing = topicsMap.get(topic.id)
|
const existing = topicsMap.get(topic.id)
|
||||||
if (
|
if (
|
||||||
!existing ||
|
!existing ||
|
||||||
@ -1603,19 +1598,14 @@ const migrateConfig = {
|
|||||||
|
|
||||||
const mergedTopics = Array.from(topicsMap.values())
|
const mergedTopics = Array.from(topicsMap.values())
|
||||||
|
|
||||||
// Update both defaultAssistant and the assistant in array
|
state['assistants'].defaultAssistant.topics = []
|
||||||
state.assistants.defaultAssistant.topics = mergedTopics
|
|
||||||
defaultAssistantInArray.topics = mergedTopics
|
defaultAssistantInArray.topics = mergedTopics
|
||||||
} else {
|
} 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
|
// Step 2: 迁移话题结构,从嵌套结构迁移到扁平结构
|
||||||
// This should run after v112 which ensures defaultAssistant and assistants[0] consistency
|
|
||||||
|
|
||||||
// Initialize the new topics slice if it doesn't exist
|
|
||||||
if (!state.topics) {
|
if (!state.topics) {
|
||||||
state.topics = {
|
state.topics = {
|
||||||
ids: [],
|
ids: [],
|
||||||
@ -1632,8 +1622,8 @@ const migrateConfig = {
|
|||||||
const topicIdsByAssistant: Record<string, string[]> = {}
|
const topicIdsByAssistant: Record<string, string[]> = {}
|
||||||
|
|
||||||
// Process regular assistants
|
// Process regular assistants
|
||||||
if (state.assistants?.assistants) {
|
if (state['assistants'].assistants && state['assistants'].assistants.length > 0) {
|
||||||
state.assistants.assistants.forEach((assistant) => {
|
state['assistants'].assistants.forEach((assistant) => {
|
||||||
const legacyAssistant = assistant as LegacyAssistant
|
const legacyAssistant = assistant as LegacyAssistant
|
||||||
if (legacyAssistant.topics && Array.isArray(legacyAssistant.topics) && legacyAssistant.topics.length > 0) {
|
if (legacyAssistant.topics && Array.isArray(legacyAssistant.topics) && legacyAssistant.topics.length > 0) {
|
||||||
allTopics.push(...legacyAssistant.topics)
|
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
|
// Populate the new topics slice
|
||||||
const topicEntities: Record<string, Topic> = {}
|
const topicEntities: Record<string, Topic> = {}
|
||||||
const topicIds: string[] = []
|
const topicIds: string[] = []
|
||||||
@ -1670,7 +1652,6 @@ const migrateConfig = {
|
|||||||
topicIds.push(topic.id)
|
topicIds.push(topic.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update topics slice
|
|
||||||
state.topics = {
|
state.topics = {
|
||||||
ids: topicIds,
|
ids: topicIds,
|
||||||
entities: topicEntities,
|
entities: topicEntities,
|
||||||
@ -1679,7 +1660,6 @@ const migrateConfig = {
|
|||||||
|
|
||||||
return state
|
return state
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Migration 112 failed:', error)
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,9 +130,7 @@ const topicsSlice = createSlice({
|
|||||||
topicsAdapter.removeMany(state, topicIds)
|
topicsAdapter.removeMany(state, topicIds)
|
||||||
|
|
||||||
// Create default topic
|
// Create default topic
|
||||||
const defaultTopic = getDefaultTopic(assistantId)
|
topicsActions.addDefaultTopic({ assistantId })
|
||||||
topicsAdapter.addOne(state, defaultTopic)
|
|
||||||
state.topicIdsByAssistant[assistantId] = [defaultTopic.id]
|
|
||||||
},
|
},
|
||||||
moveTopic(state, action: PayloadAction<MoveTopicPayload>) {
|
moveTopic(state, action: PayloadAction<MoveTopicPayload>) {
|
||||||
const { fromAssistantId, toAssistantId, topicId } = action.payload
|
const { fromAssistantId, toAssistantId, topicId } = action.payload
|
||||||
@ -154,6 +152,12 @@ const topicsSlice = createSlice({
|
|||||||
state.topicIdsByAssistant[toAssistantId] = []
|
state.topicIdsByAssistant[toAssistantId] = []
|
||||||
}
|
}
|
||||||
state.topicIdsByAssistant[toAssistantId].unshift(topicId)
|
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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user