mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
🧩 fix: Bring memory tool to parity with other ephemeral tools
- Add `memory` to the model-spec schema/type and honor `modelSpec.memory` in both ephemeral paths (load.ts, added.ts) and the frontend spec application, so admins can pre-enable Memory from a model spec exactly like webSearch/fileSearch/executeCode. - Add LAST_MEMORY_TOGGLE_ to the timestamped-storage cleanup list so stale per-conversation memory toggles are purged on startup like the others. - Hide the agent-builder Memory toggle for users who disabled memory in personalization (memories === false), mirroring the chat badge's opt-out gate, so the setting isn't shown as inert/misleading.
This commit is contained in:
parent
b5362e9b28
commit
672a03b052
7 changed files with 31 additions and 4 deletions
|
|
@ -20,7 +20,13 @@ import {
|
||||||
getIconKey,
|
getIconKey,
|
||||||
cn,
|
cn,
|
||||||
} from '~/utils';
|
} from '~/utils';
|
||||||
import { useLocalize, useVisibleTools, useHasAccess, useHasMemoryAccess } from '~/hooks';
|
import {
|
||||||
|
useLocalize,
|
||||||
|
useVisibleTools,
|
||||||
|
useHasAccess,
|
||||||
|
useHasMemoryAccess,
|
||||||
|
useAuthContext,
|
||||||
|
} from '~/hooks';
|
||||||
import { ToolSelectDialog, MCPToolSelectDialog } from '~/components/Tools';
|
import { ToolSelectDialog, MCPToolSelectDialog } from '~/components/Tools';
|
||||||
import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities';
|
import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities';
|
||||||
import { useListSkillsQuery, useGetAgentFiles } from '~/data-provider';
|
import { useListSkillsQuery, useGetAgentFiles } from '~/data-provider';
|
||||||
|
|
@ -114,8 +120,12 @@ export default function AgentConfig() {
|
||||||
permissionType: PermissionTypes.SKILLS,
|
permissionType: PermissionTypes.SKILLS,
|
||||||
permission: Permissions.USE,
|
permission: Permissions.USE,
|
||||||
});
|
});
|
||||||
|
const { user } = useAuthContext();
|
||||||
const hasMemoryAccess = useHasMemoryAccess();
|
const hasMemoryAccess = useHasMemoryAccess();
|
||||||
const showMemory = hasMemoryAccess && memoryEnabled;
|
/** Mirror the chat memory badge's opt-out gate: a user who disabled memory in
|
||||||
|
* personalization can't use the inline tools, so the builder toggle is inert
|
||||||
|
* for them and must be hidden too. */
|
||||||
|
const showMemory = hasMemoryAccess && memoryEnabled && user?.personalization?.memories !== false;
|
||||||
const showSkills = hasSkillsAccess && skillsEnabled;
|
const showSkills = hasSkillsAccess && skillsEnabled;
|
||||||
const { data: skillsData } = useListSkillsQuery({ limit: 100 }, { enabled: showSkills });
|
const { data: skillsData } = useListSkillsQuery({ limit: 100 }, { enabled: showSkills });
|
||||||
const skillsMap = useMemo(() => {
|
const skillsMap = useMemo(() => {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,19 @@ describe('timestamps', () => {
|
||||||
|
|
||||||
expect(localStorage.getItem(regularKey)).toBe('value');
|
expect(localStorage.getItem(regularKey)).toBe('value');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should purge stale memory toggle entries', () => {
|
||||||
|
const key = `${LocalStorageKeys.LAST_MEMORY_TOGGLE_}convo-321`;
|
||||||
|
const oldTimestamp = Date.now() - 3 * 24 * 60 * 60 * 1000; // 3 days ago
|
||||||
|
|
||||||
|
localStorage.setItem(key, 'true');
|
||||||
|
localStorage.setItem(`${key}_TIMESTAMP`, oldTimestamp.toString());
|
||||||
|
|
||||||
|
cleanupTimestampedStorage();
|
||||||
|
|
||||||
|
expect(localStorage.getItem(key)).toBeNull();
|
||||||
|
expect(localStorage.getItem(`${key}_TIMESTAMP`)).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('migrateExistingEntries', () => {
|
describe('migrateExistingEntries', () => {
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,7 @@ export function applyModelSpecEphemeralAgent({
|
||||||
web_search: modelSpec.webSearch ?? false,
|
web_search: modelSpec.webSearch ?? false,
|
||||||
file_search: modelSpec.fileSearch ?? false,
|
file_search: modelSpec.fileSearch ?? false,
|
||||||
execute_code: modelSpec.executeCode ?? false,
|
execute_code: modelSpec.executeCode ?? false,
|
||||||
|
memory: modelSpec.memory ?? false,
|
||||||
artifacts: modelSpec.artifacts === true ? 'default' : modelSpec.artifacts || '',
|
artifacts: modelSpec.artifacts === true ? 'default' : modelSpec.artifacts || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const TIMESTAMPED_KEYS = [
|
||||||
LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
|
LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
|
||||||
LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_,
|
LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_,
|
||||||
LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
|
LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
|
||||||
|
LocalStorageKeys.LAST_MEMORY_TOGGLE_,
|
||||||
LocalStorageKeys.PIN_MCP_,
|
LocalStorageKeys.PIN_MCP_,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ export async function loadAddedAgent(
|
||||||
if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) {
|
if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) {
|
||||||
tools.push(Tools.web_search);
|
tools.push(Tools.web_search);
|
||||||
}
|
}
|
||||||
if (ephemeralAgent?.memory === true) {
|
if (ephemeralAgent?.memory === true || modelSpec?.memory === true) {
|
||||||
tools.push(Tools.memory);
|
tools.push(Tools.memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ export async function loadEphemeralAgent(
|
||||||
if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) {
|
if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) {
|
||||||
tools.push(Tools.web_search);
|
tools.push(Tools.web_search);
|
||||||
}
|
}
|
||||||
if (ephemeralAgent?.memory === true) {
|
if (ephemeralAgent?.memory === true || modelSpec?.memory === true) {
|
||||||
tools.push(Tools.memory);
|
tools.push(Tools.memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ export type TModelSpec = {
|
||||||
webSearch?: boolean;
|
webSearch?: boolean;
|
||||||
fileSearch?: boolean;
|
fileSearch?: boolean;
|
||||||
executeCode?: boolean;
|
executeCode?: boolean;
|
||||||
|
memory?: boolean;
|
||||||
artifacts?: string | boolean;
|
artifacts?: string | boolean;
|
||||||
mcpServers?: string[];
|
mcpServers?: string[];
|
||||||
skills?: boolean | string[];
|
skills?: boolean | string[];
|
||||||
|
|
@ -76,6 +77,7 @@ export const tModelSpecSchema = z.object({
|
||||||
webSearch: z.boolean().optional(),
|
webSearch: z.boolean().optional(),
|
||||||
fileSearch: z.boolean().optional(),
|
fileSearch: z.boolean().optional(),
|
||||||
executeCode: z.boolean().optional(),
|
executeCode: z.boolean().optional(),
|
||||||
|
memory: z.boolean().optional(),
|
||||||
artifacts: z.union([z.string(), z.boolean()]).optional(),
|
artifacts: z.union([z.string(), z.boolean()]).optional(),
|
||||||
mcpServers: z.array(z.string()).optional(),
|
mcpServers: z.array(z.string()).optional(),
|
||||||
skills: z.union([z.boolean(), z.array(z.string())]).optional(),
|
skills: z.union([z.boolean(), z.array(z.string())]).optional(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue