From 143b4c46c82d030e54f58893f8c892108c88a510 Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Sat, 29 Nov 2025 00:37:03 +0800 Subject: [PATCH] refactor(preferences): update memory preference handling - Replace direct Redux state access for memory preferences with preferenceService calls in multiple components. - Introduce new preference mappings for memory settings, including 'feature.memory.auto_dimensions'. - Remove deprecated preference mappings and update default preferences accordingly. - Regenerate preference schemas to reflect the latest changes in memory configuration. --- .../data/preference/preferenceSchemas.ts | 8 +++---- .../plugins/searchOrchestrationPlugin.ts | 11 +++++---- .../src/aiCore/tools/MemorySearchTool.ts | 7 +++--- .../AssistantMemorySettings.tsx | 5 ++-- .../MemorySettings/MemorySettings.tsx | 24 +++++++------------ src/renderer/src/services/MemoryProcessor.ts | 6 ++--- .../src/services/__tests__/ApiService.test.ts | 4 +++- src/renderer/src/store/memory.ts | 6 ++--- 8 files changed, 35 insertions(+), 36 deletions(-) diff --git a/packages/shared/data/preference/preferenceSchemas.ts b/packages/shared/data/preference/preferenceSchemas.ts index f90487792e..368a38bb70 100644 --- a/packages/shared/data/preference/preferenceSchemas.ts +++ b/packages/shared/data/preference/preferenceSchemas.ts @@ -1,6 +1,6 @@ /** * Auto-generated preferences configuration - * Generated at: 2025-11-28T15:24:33.354Z + * Generated at: 2025-11-28T16:19:17.585Z * * This file is automatically generated from classification.json * To update this file, modify classification.json and run: @@ -310,6 +310,8 @@ export interface PreferenceSchemas { 'feature.csaas.host': string // redux/settings/apiServer.port 'feature.csaas.port': number + // redux/memory/memoryConfig.isAutoDimensions + 'feature.memory.auto_dimensions': boolean // redux/memory/currentUserId 'feature.memory.current_user_id': string // redux/memory/memoryConfig.embedderDimensions @@ -318,8 +320,6 @@ export interface PreferenceSchemas { 'feature.memory.enabled': boolean // redux/memory/memoryConfig.customFactExtractionPrompt 'feature.memory.fact_extraction_prompt': string - // redux/memory/memoryConfig.isAutoDimensions - 'feature.memory.is_auto_dimensions': boolean // redux/memory/memoryConfig.customUpdateMemoryPrompt 'feature.memory.update_memory_prompt': string // redux/settings/maxKeepAliveMinapps @@ -585,11 +585,11 @@ export const DefaultPreferences: PreferenceSchemas = { 'feature.csaas.enabled': false, 'feature.csaas.host': 'localhost', 'feature.csaas.port': 23333, + 'feature.memory.auto_dimensions': true, 'feature.memory.current_user_id': 'default-user', 'feature.memory.embedder_dimensions': 1536, 'feature.memory.enabled': false, 'feature.memory.fact_extraction_prompt': MEMORY_FACT_EXTRACTION_PROMPT, - 'feature.memory.is_auto_dimensions': true, 'feature.memory.update_memory_prompt': MEMORY_UPDATE_SYSTEM_PROMPT, 'feature.minapp.max_keep_alive': 3, 'feature.minapp.open_link_external': false, diff --git a/src/renderer/src/aiCore/plugins/searchOrchestrationPlugin.ts b/src/renderer/src/aiCore/plugins/searchOrchestrationPlugin.ts index 6e3b4ba968..aa4a48693b 100644 --- a/src/renderer/src/aiCore/plugins/searchOrchestrationPlugin.ts +++ b/src/renderer/src/aiCore/plugins/searchOrchestrationPlugin.ts @@ -7,10 +7,11 @@ * 3. onRequestEnd: 自动记忆存储 */ import { type AiRequestContext, definePlugin } from '@cherrystudio/ai-core' +import { preferenceService } from '@data/PreferenceService' import { loggerService } from '@logger' import { getDefaultModel, getProviderByModel } from '@renderer/services/AssistantService' import store from '@renderer/store' -import { selectCurrentUserId, selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory' +import { selectMemoryConfig } from '@renderer/store/memory' import type { Assistant } from '@renderer/types' import type { ExtractResults } from '@renderer/utils/extract' import { extractInfoFromXML } from '@renderer/utils/extract' @@ -176,7 +177,7 @@ async function storeConversationMemory( assistant: Assistant, context: AiRequestContext ): Promise { - const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState()) + const globalMemoryEnabled = await preferenceService.get('feature.memory.enabled') if (!globalMemoryEnabled || !assistant.enableMemory) { return @@ -199,7 +200,7 @@ async function storeConversationMemory( return } - const currentUserId = selectCurrentUserId(store.getState()) + const currentUserId = await preferenceService.get('feature.memory.current_user_id') // const lastUserMessage = messages.findLast((m) => m.role === 'user') const processorConfig = MemoryProcessor.getProcessorConfig( @@ -267,7 +268,7 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string) const knowledgeBaseIds = assistant.knowledge_bases?.map((base) => base.id) const hasKnowledgeBase = !isEmpty(knowledgeBaseIds) const knowledgeRecognition = assistant.knowledgeRecognition || 'on' - const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState()) + const globalMemoryEnabled = await preferenceService.get('feature.memory.enabled') const shouldWebSearch = !!assistant.webSearchProviderId const shouldKnowledgeSearch = hasKnowledgeBase && knowledgeRecognition === 'on' const shouldMemorySearch = globalMemoryEnabled && assistant.enableMemory @@ -369,7 +370,7 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string) } // 🧠 记忆搜索工具配置 - const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState()) + const globalMemoryEnabled = await preferenceService.get('feature.memory.enabled') if (globalMemoryEnabled && assistant.enableMemory) { // logger.info('🧠 Adding memory search tool') params.tools['builtin_memory_search'] = memorySearchTool() diff --git a/src/renderer/src/aiCore/tools/MemorySearchTool.ts b/src/renderer/src/aiCore/tools/MemorySearchTool.ts index 20064dd1b2..bf2bbd286a 100644 --- a/src/renderer/src/aiCore/tools/MemorySearchTool.ts +++ b/src/renderer/src/aiCore/tools/MemorySearchTool.ts @@ -1,5 +1,6 @@ +import { preferenceService } from '@data/PreferenceService' import store from '@renderer/store' -import { selectCurrentUserId, selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory' +import { selectMemoryConfig } from '@renderer/store/memory' import { type InferToolInput, type InferToolOutput, tool } from 'ai' import * as z from 'zod' @@ -18,7 +19,7 @@ export const memorySearchTool = () => { limit: z.number().min(1).max(20).default(5).describe('Maximum number of memories to return') }), execute: async ({ query, limit = 5 }) => { - const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState()) + const globalMemoryEnabled = await preferenceService.get('feature.memory.enabled') if (!globalMemoryEnabled) { return [] } @@ -28,7 +29,7 @@ export const memorySearchTool = () => { return [] } - const currentUserId = selectCurrentUserId(store.getState()) + const currentUserId = await preferenceService.get('feature.memory.current_user_id') const processorConfig = MemoryProcessor.getProcessorConfig(memoryConfig, 'default', currentUserId) const memoryProcessor = new MemoryProcessor() diff --git a/src/renderer/src/pages/settings/AssistantSettings/AssistantMemorySettings.tsx b/src/renderer/src/pages/settings/AssistantSettings/AssistantMemorySettings.tsx index 3a897dcc53..60cc2b5b85 100644 --- a/src/renderer/src/pages/settings/AssistantSettings/AssistantMemorySettings.tsx +++ b/src/renderer/src/pages/settings/AssistantSettings/AssistantMemorySettings.tsx @@ -1,8 +1,9 @@ import { Box, Button, InfoTooltip, Switch, Tooltip } from '@cherrystudio/ui' import { loggerService } from '@logger' +import { usePreference } from '@renderer/data/hooks/usePreference' import MemoriesSettingsModal from '@renderer/pages/memory/settings-modal' import MemoryService from '@renderer/services/MemoryService' -import { selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory' +import { selectMemoryConfig } from '@renderer/store/memory' import type { Assistant, AssistantSettings } from '@renderer/types' import { Alert, Card, Space, Typography } from 'antd' import { useForm } from 'antd/es/form/Form' @@ -26,7 +27,7 @@ interface Props { const AssistantMemorySettings: React.FC = ({ assistant, updateAssistant, onClose }) => { const { t } = useTranslation() const memoryConfig = useSelector(selectMemoryConfig) - const globalMemoryEnabled = useSelector(selectGlobalMemoryEnabled) + const [globalMemoryEnabled] = usePreference('feature.memory.enabled') const [memoryStats, setMemoryStats] = useState<{ count: number; loading: boolean }>({ count: 0, loading: true diff --git a/src/renderer/src/pages/settings/MemorySettings/MemorySettings.tsx b/src/renderer/src/pages/settings/MemorySettings/MemorySettings.tsx index 6bd4664f0e..171cb80a51 100644 --- a/src/renderer/src/pages/settings/MemorySettings/MemorySettings.tsx +++ b/src/renderer/src/pages/settings/MemorySettings/MemorySettings.tsx @@ -4,6 +4,7 @@ import { Flex } from '@cherrystudio/ui' import { Switch } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui' import { cacheService } from '@data/CacheService' +import { usePreference } from '@data/hooks/usePreference' import { loggerService } from '@logger' import { DeleteIcon, EditIcon, LoadingIcon, RefreshIcon } from '@renderer/components/Icons' import TextBadge from '@renderer/components/TextBadge' @@ -11,13 +12,7 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { useModel } from '@renderer/hooks/useModel' import MemoriesSettingsModal from '@renderer/pages/memory/settings-modal' import MemoryService from '@renderer/services/MemoryService' -import { - selectCurrentUserId, - selectGlobalMemoryEnabled, - selectMemoryConfig, - setCurrentUserId, - setGlobalMemoryEnabled -} from '@renderer/store/memory' +import { selectMemoryConfig } from '@renderer/store/memory' import type { MemoryItem } from '@types' import { Badge, Dropdown, Empty, Form, Input, Modal, Pagination, Space, Spin } from 'antd' import dayjs from 'dayjs' @@ -25,7 +20,7 @@ import relativeTime from 'dayjs/plugin/relativeTime' import { Brain, Calendar, MenuIcon, PlusIcon, Settings2, UserRound, UserRoundMinus, UserRoundPlus } from 'lucide-react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' +import { useSelector } from 'react-redux' import styled from 'styled-components' import { @@ -281,9 +276,8 @@ const AddUserModal: React.FC = ({ visible, onCancel, onAdd, e const MemorySettings = () => { const { t } = useTranslation() - const dispatch = useDispatch() - const currentUser = useSelector(selectCurrentUserId) - const globalMemoryEnabled = useSelector(selectGlobalMemoryEnabled) + const [currentUser, setCurrentUserId] = usePreference('feature.memory.current_user_id') + const [globalMemoryEnabled, setGlobalMemoryEnabled] = usePreference('feature.memory.enabled') const [allMemories, setAllMemories] = useState([]) const [loading, setLoading] = useState(false) @@ -436,8 +430,8 @@ const MemorySettings = () => { const handleUserSwitch = async (userId: string) => { logger.verbose(`Switching to user: ${userId}`) - // First update Redux state - dispatch(setCurrentUserId(userId)) + // First update preference state + setCurrentUserId(userId) // Clear current memories to show loading state immediately setAllMemories([]) @@ -483,7 +477,7 @@ const MemorySettings = () => { await memoryService.updateConfig() if (cacheService.get('memory.wait.settings')) { cacheService.delete('memory.wait.settings') - dispatch(setGlobalMemoryEnabled(true)) + setGlobalMemoryEnabled(true) } } @@ -560,7 +554,7 @@ const MemorySettings = () => { return setSettingsModalVisible(true) } - dispatch(setGlobalMemoryEnabled(enabled)) + setGlobalMemoryEnabled(enabled) if (enabled) { return window.modal.confirm({ diff --git a/src/renderer/src/services/MemoryProcessor.ts b/src/renderer/src/services/MemoryProcessor.ts index c0d35de288..05f1476901 100644 --- a/src/renderer/src/services/MemoryProcessor.ts +++ b/src/renderer/src/services/MemoryProcessor.ts @@ -6,9 +6,9 @@ import { FactRetrievalSchema, getFactRetrievalMessages, getUpdateMemoryMessages, - MemoryUpdateSchema, - updateMemorySystemPrompt + MemoryUpdateSchema } from '@renderer/utils/memory-prompts' +import { MEMORY_UPDATE_SYSTEM_PROMPT } from '@shared/config/prompts' import type { MemoryConfig, MemoryItem } from '@types' import jaison from 'jaison/lib/index.js' @@ -122,7 +122,7 @@ export class MemoryProcessor { const updateMemoryUserPrompt = getUpdateMemoryMessages(existingMemories, facts) const responseContent = await fetchGenerate({ - prompt: updateMemorySystemPrompt, + prompt: MEMORY_UPDATE_SYSTEM_PROMPT, content: updateMemoryUserPrompt, model: getModel(memoryConfig.llmApiClient.model, memoryConfig.llmApiClient.provider) }) diff --git a/src/renderer/src/services/__tests__/ApiService.test.ts b/src/renderer/src/services/__tests__/ApiService.test.ts index c5f2e48bad..bb6b5df962 100644 --- a/src/renderer/src/services/__tests__/ApiService.test.ts +++ b/src/renderer/src/services/__tests__/ApiService.test.ts @@ -113,7 +113,9 @@ vi.mock(import('@renderer/config/providers'), async (importOriginal) => { vi.mock('@shared/config/prompts', () => ({ WEB_SEARCH_PROMPT_FOR_OPENROUTER: 'mock-prompt', TRANSLATE_PROMPT: - 'You are a translation expert. Your only task is to translate text enclosed with from input language to {{target_language}}, provide the translation result directly without any explanation, without `TRANSLATE` and keep original format.' + 'You are a translation expert. Your only task is to translate text enclosed with from input language to {{target_language}}, provide the translation result directly without any explanation, without `TRANSLATE` and keep original format.', + MEMORY_FACT_EXTRACTION_PROMPT: 'mock-memory-fact-extraction-prompt', + MEMORY_UPDATE_SYSTEM_PROMPT: 'mock-memory-update-system-prompt' })) vi.mock('@renderer/config/systemModels', () => ({ diff --git a/src/renderer/src/store/memory.ts b/src/renderer/src/store/memory.ts index 808f2154af..2a02546053 100644 --- a/src/renderer/src/store/memory.ts +++ b/src/renderer/src/store/memory.ts @@ -1,5 +1,5 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit' -import { factExtractionPrompt, updateMemorySystemPrompt } from '@renderer/utils/memory-prompts' +import { MEMORY_FACT_EXTRACTION_PROMPT, MEMORY_UPDATE_SYSTEM_PROMPT } from '@shared/config/prompts' import type { MemoryConfig } from '@types' /** @@ -19,8 +19,8 @@ export interface MemoryState { const defaultMemoryConfig: MemoryConfig = { embedderDimensions: 1536, isAutoDimensions: true, - customFactExtractionPrompt: factExtractionPrompt, - customUpdateMemoryPrompt: updateMemorySystemPrompt + customFactExtractionPrompt: MEMORY_FACT_EXTRACTION_PROMPT, + customUpdateMemoryPrompt: MEMORY_UPDATE_SYSTEM_PROMPT } /**