🐛 fix: global memory setting & tool enabled logic (#12610)

* fix: global enable user memories

* fix: memory tool enabled logic
This commit is contained in:
Rdmclin2
2026-03-03 16:50:24 +08:00
committed by GitHub
parent 9ab2f219e4
commit ceeb9c6613
11 changed files with 60 additions and 47 deletions

View File

@@ -164,10 +164,10 @@
"memory.effort.low.title": "Low",
"memory.effort.medium.title": "Medium",
"memory.effort.title": "Aggressiveness",
"memory.off.desc": "Disable memory for this conversation.",
"memory.off.title": "Disable Memory",
"memory.on.desc": "Remember preferences and info from conversations.",
"memory.on.title": "Enable Memory",
"memory.off.desc": "AI will not search, create, or update memories in this conversation.",
"memory.off.title": "Disable Memory Tool",
"memory.on.desc": "Allow AI to actively search and manage your memories during conversation.",
"memory.on.title": "Enable Memory Tool",
"memory.title": "Memory",
"mention.title": "Mention Members",
"messageAction.collapse": "Collapse Message",

View File

@@ -164,10 +164,10 @@
"memory.effort.low.title": "低",
"memory.effort.medium.title": "中",
"memory.effort.title": "积极性",
"memory.off.desc": "禁用此对话的记忆功能。",
"memory.off.title": "关闭记忆",
"memory.on.desc": "从对话中记住偏好和信息。",
"memory.on.title": "启用记忆",
"memory.off.desc": "AI 将不会在此对话中搜索、创建或更新记忆。",
"memory.off.title": "关闭记忆工具",
"memory.on.desc": "允许 AI 在对话中主动搜索和管理你的记忆。",
"memory.on.title": "启用记忆工具",
"memory.title": "记忆",
"mention.title": "提及成员",
"messageAction.collapse": "收起消息",

View File

@@ -27,6 +27,7 @@ export const defaultToolIds = [
SkillStoreManifest.identifier,
WebBrowsingManifest.identifier,
KnowledgeBaseManifest.identifier,
MemoryManifest.identifier,
];
export const builtinTools: LobeBuiltinTool[] = [

View File

@@ -60,7 +60,9 @@ interface ToggleOption {
const ToggleItem = memo<ToggleOption>(({ value, description, icon, label }) => {
const agentId = useAgentId();
const { updateAgentChatConfig } = useUpdateAgentConfig();
const isEnabled = useAgentStore((s) => chatConfigByIdSelectors.isMemoryEnabledById(agentId)(s));
const isEnabled = useAgentStore((s) =>
chatConfigByIdSelectors.isMemoryToolEnabledById(agentId)(s),
);
const isActive = value === 'on' ? isEnabled : !isEnabled;
@@ -90,8 +92,8 @@ const Controls = memo(() => {
const agentId = useAgentId();
const { updateAgentChatConfig } = useUpdateAgentConfig();
const [isEnabled, effort] = useAgentStore((s) => [
chatConfigByIdSelectors.isMemoryEnabledById(agentId)(s),
chatConfigByIdSelectors.getMemoryEffortById(agentId)(s),
chatConfigByIdSelectors.isMemoryToolEnabledById(agentId)(s),
chatConfigByIdSelectors.getMemoryToolEffortById(agentId)(s),
]);
const toggleOptions: ToggleOption[] = [

View File

@@ -18,7 +18,7 @@ const Memory = memo(() => {
const { updateAgentChatConfig } = useUpdateAgentConfig();
const [isLoading, isEnabled] = useAgentStore((s) => [
agentByIdSelectors.isAgentConfigLoadingById(agentId)(s),
chatConfigByIdSelectors.isMemoryEnabledById(agentId)(s),
chatConfigByIdSelectors.isMemoryToolEnabledById(agentId)(s),
]);
const isMobile = useIsMobile();

View File

@@ -2,6 +2,7 @@
* Tools Engineering - Unified tools processing using ToolsEngine
*/
import { KnowledgeBaseManifest } from '@lobechat/builtin-tool-knowledge-base';
import { MemoryManifest } from '@lobechat/builtin-tool-memory';
import { WebBrowsingManifest } from '@lobechat/builtin-tool-web-browsing';
import { defaultToolIds } from '@lobechat/builtin-tools';
import { isDesktop } from '@lobechat/const';
@@ -11,7 +12,7 @@ import { type ChatCompletionTool, type WorkingModel } from '@lobechat/types';
import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
import { getAgentStoreState } from '@/store/agent';
import { agentSelectors } from '@/store/agent/selectors';
import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
import { getToolStoreState } from '@/store/tool';
import {
klavisStoreSelectors,
@@ -116,6 +117,11 @@ export const createAgentToolsEngine = (workingModel: WorkingModel) =>
return agentSelectors.hasEnabledKnowledgeBases(agentState);
}
// For MemoryManifest, check per-agent memory tool toggle
if (pluginId === MemoryManifest.identifier) {
return agentChatConfigSelectors.isMemoryToolEnabled(getAgentStoreState());
}
// For all other plugins, enable by default
return true;
},

View File

@@ -265,10 +265,10 @@ export default {
'memory.effort.low.title': 'Low',
'memory.effort.medium.title': 'Medium',
'memory.effort.title': 'Aggressiveness',
'memory.off.desc': 'Disable memory for this conversation.',
'memory.off.title': 'Disable Memory',
'memory.on.desc': 'Remember preferences and info from conversations.',
'memory.on.title': 'Enable Memory',
'memory.off.desc': 'AI will not search, create, or update memories in this conversation.',
'memory.off.title': 'Disable Memory Tool',
'memory.on.desc': 'Allow AI to actively search and manage your memories during conversation.',
'memory.on.title': 'Enable Memory Tool',
'memory.title': 'Memory',
'search.grounding.searchQueries': 'Search Keywords',
'search.grounding.title': 'Found {{count}} results',

View File

@@ -142,9 +142,7 @@ class ChatService {
// =================== 1.1 process user memories =================== //
const globalMemoryEnabled = settingsSelectors.memoryEnabled(getUserStoreState());
const agentMemoryEnabled = chatConfig.memory?.enabled ?? false;
const enableUserMemories = globalMemoryEnabled && agentMemoryEnabled;
const enableUserMemories = settingsSelectors.memoryEnabled(getUserStoreState());
const userMemorySettings = settingsSelectors.currentMemorySettings(getUserStoreState());
const effectiveMemoryEffort =
chatConfig.memory?.effort ?? userMemorySettings.effort ?? 'medium';

View File

@@ -249,7 +249,7 @@ describe('chatConfigByIdSelectors', () => {
});
});
describe('getMemoryConfigById', () => {
describe('getMemoryToolConfigById', () => {
it('should return memory config for specified agent', () => {
const state = createState({
agentMap: {
@@ -261,7 +261,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.getMemoryConfigById('agent-1')(state)).toEqual({
expect(chatConfigByIdSelectors.getMemoryToolConfigById('agent-1')(state)).toEqual({
effort: 'high',
enabled: true,
toolPermission: 'read-write',
@@ -273,7 +273,7 @@ describe('chatConfigByIdSelectors', () => {
agentMap: { 'agent-1': {} },
});
expect(chatConfigByIdSelectors.getMemoryConfigById('agent-1')(state)).toBeUndefined();
expect(chatConfigByIdSelectors.getMemoryToolConfigById('agent-1')(state)).toBeUndefined();
});
it('should return undefined for non-existent agent', () => {
@@ -281,11 +281,13 @@ describe('chatConfigByIdSelectors', () => {
agentMap: {},
});
expect(chatConfigByIdSelectors.getMemoryConfigById('non-existent')(state)).toBeUndefined();
expect(
chatConfigByIdSelectors.getMemoryToolConfigById('non-existent')(state),
).toBeUndefined();
});
});
describe('isMemoryEnabledById', () => {
describe('isMemoryToolEnabledById', () => {
it('should return true when memory is enabled', () => {
const state = createState({
agentMap: {
@@ -293,7 +295,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-1')(state)).toBe(true);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-1')(state)).toBe(true);
});
it('should return false when memory is explicitly disabled', () => {
@@ -303,7 +305,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-1')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-1')(state)).toBe(false);
});
it('should return false when memory config is not set (default)', () => {
@@ -311,7 +313,7 @@ describe('chatConfigByIdSelectors', () => {
agentMap: { 'agent-1': {} },
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-1')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-1')(state)).toBe(false);
});
it('should return false when memory exists but enabled is not set', () => {
@@ -321,7 +323,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-1')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-1')(state)).toBe(false);
});
it('should return false for non-existent agent', () => {
@@ -329,7 +331,7 @@ describe('chatConfigByIdSelectors', () => {
agentMap: {},
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('non-existent')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('non-existent')(state)).toBe(false);
});
it('should work with different agents independently', () => {
@@ -341,13 +343,13 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-1')(state)).toBe(true);
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-2')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryEnabledById('agent-3')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-1')(state)).toBe(true);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-2')(state)).toBe(false);
expect(chatConfigByIdSelectors.isMemoryToolEnabledById('agent-3')(state)).toBe(false);
});
});
describe('getMemoryEffortById', () => {
describe('getMemoryToolEffortById', () => {
it('should return effort level for specified agent', () => {
const state = createState({
agentMap: {
@@ -355,7 +357,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-1')(state)).toBe('high');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-1')(state)).toBe('high');
});
it('should return "medium" as default when effort is not set', () => {
@@ -365,7 +367,7 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-1')(state)).toBe('medium');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-1')(state)).toBe('medium');
});
it('should return "medium" when memory config is not set', () => {
@@ -373,7 +375,7 @@ describe('chatConfigByIdSelectors', () => {
agentMap: { 'agent-1': {} },
});
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-1')(state)).toBe('medium');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-1')(state)).toBe('medium');
});
it('should return "medium" for non-existent agent', () => {
@@ -381,7 +383,7 @@ describe('chatConfigByIdSelectors', () => {
agentMap: {},
});
expect(chatConfigByIdSelectors.getMemoryEffortById('non-existent')(state)).toBe('medium');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('non-existent')(state)).toBe('medium');
});
it('should return each effort level correctly', () => {
@@ -393,9 +395,9 @@ describe('chatConfigByIdSelectors', () => {
},
});
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-low')(state)).toBe('low');
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-medium')(state)).toBe('medium');
expect(chatConfigByIdSelectors.getMemoryEffortById('agent-high')(state)).toBe('high');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-low')(state)).toBe('low');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-medium')(state)).toBe('medium');
expect(chatConfigByIdSelectors.getMemoryToolEffortById('agent-high')(state)).toBe('high');
});
});

View File

@@ -54,24 +54,24 @@ const getUseModelBuiltinSearchById = (agentId: string) => (s: AgentStoreState) =
const getSearchFCModelById = (agentId: string) => (s: AgentStoreState) =>
getChatConfigById(agentId)(s).searchFCModel || DEFAULT_AGENT_SEARCH_FC_MODEL;
const getMemoryConfigById = (agentId: string) => (s: AgentStoreState) =>
const getMemoryToolConfigById = (agentId: string) => (s: AgentStoreState) =>
getChatConfigById(agentId)(s).memory;
const isMemoryEnabledById = (agentId: string) => (s: AgentStoreState) =>
const isMemoryToolEnabledById = (agentId: string) => (s: AgentStoreState) =>
getChatConfigById(agentId)(s).memory?.enabled ?? false;
const getMemoryEffortById = (agentId: string) => (s: AgentStoreState) =>
const getMemoryToolEffortById = (agentId: string) => (s: AgentStoreState) =>
getChatConfigById(agentId)(s).memory?.effort ?? 'medium';
export const chatConfigByIdSelectors = {
getChatConfigById,
getEnableHistoryCountById,
getHistoryCountById,
getMemoryConfigById,
getMemoryEffortById,
getMemoryToolConfigById,
getMemoryToolEffortById,
getSearchFCModelById,
getSearchModeById,
getUseModelBuiltinSearchById,
isEnableSearchById,
isMemoryEnabledById,
isMemoryToolEnabledById,
};

View File

@@ -27,6 +27,9 @@ const enableHistoryCount = (s: AgentStoreState) =>
const historyCount = (s: AgentStoreState): number =>
chatConfigByIdSelectors.getHistoryCountById(s.activeAgentId || '')(s);
const isMemoryToolEnabled = (s: AgentStoreState) =>
chatConfigByIdSelectors.isMemoryToolEnabledById(s.activeAgentId || '')(s);
const enableHistoryDivider =
(historyLength: number, currentIndex: number) => (s: AgentStoreState) => {
const config = currentChatConfig(s);
@@ -45,6 +48,7 @@ export const agentChatConfigSelectors = {
enableHistoryDivider,
historyCount,
isAgentEnableSearch,
isMemoryToolEnabled,
searchFCModel,
useModelBuiltinSearch,
};