mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 04:07:05 +00:00
🗃️ feat: Retain Agent Files During All-Data Retention (#13477)
* feat: add agent file retention exemption * refactor: centralize agent file retention policy
This commit is contained in:
parent
571d8d8284
commit
8ba0249f1e
10 changed files with 306 additions and 32 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { RetentionMode } from 'librechat-data-provider';
|
||||
import {
|
||||
createMinimalRetentionRequest,
|
||||
getAgentFileRetentionExpiry,
|
||||
getConversationExpirationDate,
|
||||
getRetentionExpiry,
|
||||
getSharedLinkExpiration,
|
||||
|
|
@ -191,6 +192,129 @@ describe('retention helpers', () => {
|
|||
await expect(getRetentionExpiry(undefined, dependencies)).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('skips persistent agent files in temporary retention mode when retainAgentFiles is disabled', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({
|
||||
config: {
|
||||
interfaceConfig: {
|
||||
retentionMode: RetentionMode.TEMPORARY,
|
||||
retainAgentFiles: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
messageAttachment: false,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({});
|
||||
expect(dependencies.getConvo).not.toHaveBeenCalled();
|
||||
expect(dependencies.createExpirationDate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips persistent agent files in temporary retention mode when retainAgentFiles is enabled', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({
|
||||
config: {
|
||||
interfaceConfig: {
|
||||
retentionMode: RetentionMode.TEMPORARY,
|
||||
retainAgentFiles: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
messageAttachment: false,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({});
|
||||
expect(dependencies.getConvo).not.toHaveBeenCalled();
|
||||
expect(dependencies.createExpirationDate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('applies all-data retention to persistent agent files when retainAgentFiles is disabled', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({
|
||||
config: {
|
||||
interfaceConfig: {
|
||||
retentionMode: RetentionMode.ALL,
|
||||
retainAgentFiles: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
messageAttachment: false,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({ expiredAt: expirationDate });
|
||||
expect(dependencies.getConvo).not.toHaveBeenCalled();
|
||||
expect(dependencies.createExpirationDate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('keeps current all-data retention behavior when retainAgentFiles is unset', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({ config: { interfaceConfig: { retentionMode: RetentionMode.ALL } } }),
|
||||
messageAttachment: false,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({ expiredAt: expirationDate });
|
||||
expect(dependencies.createExpirationDate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('skips all-data retention for persistent agent files when retainAgentFiles is enabled', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({
|
||||
config: {
|
||||
interfaceConfig: {
|
||||
retentionMode: RetentionMode.ALL,
|
||||
retainAgentFiles: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
messageAttachment: false,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({});
|
||||
expect(dependencies.getConvo).not.toHaveBeenCalled();
|
||||
expect(dependencies.createExpirationDate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('still applies all-data retention to agent message attachments when retainAgentFiles is enabled', async () => {
|
||||
const result = await getAgentFileRetentionExpiry(
|
||||
{
|
||||
req: request({
|
||||
config: {
|
||||
interfaceConfig: {
|
||||
retentionMode: RetentionMode.ALL,
|
||||
retainAgentFiles: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
messageAttachment: true,
|
||||
toolResource: 'context',
|
||||
},
|
||||
dependencies,
|
||||
);
|
||||
|
||||
expect(result).toEqual({ expiredAt: expirationDate });
|
||||
expect(dependencies.createExpirationDate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('parses valid conversation expiration dates and ignores invalid ones', () => {
|
||||
expect(getConversationExpirationDate({ expiredAt: expirationDate })).toBe(expirationDate);
|
||||
expect(getConversationExpirationDate({ expiredAt: expirationDate.toISOString() })).toEqual(
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ export type RetentionExpiry = {
|
|||
expiredAt?: Date | null;
|
||||
};
|
||||
|
||||
export type AgentFileRetentionRequest = {
|
||||
req: RetentionRequest | null | undefined;
|
||||
messageAttachment?: boolean | null;
|
||||
toolResource?: string | null;
|
||||
};
|
||||
|
||||
export type RetentionLogger = {
|
||||
error: (message: string, error?: unknown) => void;
|
||||
};
|
||||
|
|
@ -160,6 +166,35 @@ export async function getRetentionExpiry(
|
|||
return promise;
|
||||
}
|
||||
|
||||
const isPersistentAgentResourceUpload = ({
|
||||
messageAttachment,
|
||||
toolResource,
|
||||
}: Omit<AgentFileRetentionRequest, 'req'>): boolean => !messageAttachment && !!toolResource;
|
||||
|
||||
const shouldRetainPersistentAgentFile = ({
|
||||
req,
|
||||
messageAttachment,
|
||||
toolResource,
|
||||
}: AgentFileRetentionRequest): boolean => {
|
||||
const interfaceConfig = req?.config?.interfaceConfig;
|
||||
return (
|
||||
isPersistentAgentResourceUpload({ messageAttachment, toolResource }) &&
|
||||
(interfaceConfig?.retentionMode !== RetentionMode.ALL ||
|
||||
interfaceConfig?.retainAgentFiles === true)
|
||||
);
|
||||
};
|
||||
|
||||
export async function getAgentFileRetentionExpiry(
|
||||
params: AgentFileRetentionRequest,
|
||||
dependencies: RetentionDependencies,
|
||||
): Promise<RetentionExpiry> {
|
||||
if (shouldRetainPersistentAgentFile(params)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return await getRetentionExpiry(params.req, dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the retention deadline for a shared link derived from a conversation.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue