mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
📋 fix: Agent Resource Deduplication & Sharing Duplicate False Positive (#7835)
* fix: `primeResources` to Prevent Duplicate Files Across Sources - Added multiple test cases to ensure that the `primeResources` function correctly handles duplicate files from OCR and attachments, including scenarios with shared files, files without IDs, and duplicates within attachments. - Implemented logic to categorize files into appropriate tool resources while preventing duplicates across different categories. - Enhanced error handling and ensured that unique files are returned in the final attachments array. * fix: Update ToolService to handle single OCR tool case (no loaded tool necessary) * refactor: Add skipVersioning option to updateAgent for isolated updates - for now, mainly concerns sharing/unsharing of agents * chore: Update translation for shared agent message in UI
This commit is contained in:
parent
cdf42b3a03
commit
13c7ceb918
7 changed files with 730 additions and 43 deletions
|
@ -259,11 +259,12 @@ const isDuplicateVersion = (updateData, currentData, versions, actionsHash = nul
|
|||
* @param {Object} [options] - Optional configuration object.
|
||||
* @param {string} [options.updatingUserId] - The ID of the user performing the update (used for tracking non-author updates).
|
||||
* @param {boolean} [options.forceVersion] - Force creation of a new version even if no fields changed.
|
||||
* @param {boolean} [options.skipVersioning] - Skip version creation entirely (useful for isolated operations like sharing).
|
||||
* @returns {Promise<Agent>} The updated or newly created agent document as a plain object.
|
||||
* @throws {Error} If the update would create a duplicate version
|
||||
*/
|
||||
const updateAgent = async (searchParameter, updateData, options = {}) => {
|
||||
const { updatingUserId = null, forceVersion = false } = options;
|
||||
const { updatingUserId = null, forceVersion = false, skipVersioning = false } = options;
|
||||
const mongoOptions = { new: true, upsert: false };
|
||||
|
||||
const currentAgent = await Agent.findOne(searchParameter);
|
||||
|
@ -300,7 +301,8 @@ const updateAgent = async (searchParameter, updateData, options = {}) => {
|
|||
}
|
||||
|
||||
const shouldCreateVersion =
|
||||
forceVersion || Object.keys(directUpdates).length > 0 || $push || $pull || $addToSet;
|
||||
!skipVersioning &&
|
||||
(forceVersion || Object.keys(directUpdates).length > 0 || $push || $pull || $addToSet);
|
||||
|
||||
if (shouldCreateVersion) {
|
||||
const duplicateVersion = isDuplicateVersion(updateData, versionData, versions, actionsHash);
|
||||
|
@ -335,7 +337,7 @@ const updateAgent = async (searchParameter, updateData, options = {}) => {
|
|||
versionEntry.updatedBy = new mongoose.Types.ObjectId(updatingUserId);
|
||||
}
|
||||
|
||||
if (shouldCreateVersion || forceVersion) {
|
||||
if (shouldCreateVersion) {
|
||||
updateData.$push = {
|
||||
...($push || {}),
|
||||
versions: versionEntry,
|
||||
|
@ -546,7 +548,10 @@ const updateAgentProjects = async ({ user, agentId, projectIds, removeProjectIds
|
|||
delete updateQuery.author;
|
||||
}
|
||||
|
||||
const updatedAgent = await updateAgent(updateQuery, updateOps, { updatingUserId: user.id });
|
||||
const updatedAgent = await updateAgent(updateQuery, updateOps, {
|
||||
updatingUserId: user.id,
|
||||
skipVersioning: true,
|
||||
});
|
||||
if (updatedAgent) {
|
||||
return updatedAgent;
|
||||
}
|
||||
|
|
|
@ -2447,6 +2447,65 @@ describe('models/Agent', () => {
|
|||
expect(updated2.description).toBe('Another description');
|
||||
});
|
||||
|
||||
test('should skip version creation when skipVersioning option is used', async () => {
|
||||
const agentId = `agent_${uuidv4()}`;
|
||||
const authorId = new mongoose.Types.ObjectId();
|
||||
const projectId1 = new mongoose.Types.ObjectId();
|
||||
const projectId2 = new mongoose.Types.ObjectId();
|
||||
|
||||
// Create agent with initial projectIds
|
||||
await createAgent({
|
||||
id: agentId,
|
||||
name: 'Test Agent',
|
||||
provider: 'test',
|
||||
model: 'test-model',
|
||||
author: authorId,
|
||||
projectIds: [projectId1],
|
||||
});
|
||||
|
||||
// Share agent using updateAgentProjects (which uses skipVersioning)
|
||||
const shared = await updateAgentProjects({
|
||||
user: { id: authorId.toString() }, // Use the same author ID
|
||||
agentId: agentId,
|
||||
projectIds: [projectId2.toString()],
|
||||
});
|
||||
|
||||
// Should NOT create a new version due to skipVersioning
|
||||
expect(shared.versions).toHaveLength(1);
|
||||
expect(shared.projectIds.map((id) => id.toString())).toContain(projectId1.toString());
|
||||
expect(shared.projectIds.map((id) => id.toString())).toContain(projectId2.toString());
|
||||
|
||||
// Unshare agent using updateAgentProjects
|
||||
const unshared = await updateAgentProjects({
|
||||
user: { id: authorId.toString() },
|
||||
agentId: agentId,
|
||||
removeProjectIds: [projectId1.toString()],
|
||||
});
|
||||
|
||||
// Still should NOT create a new version
|
||||
expect(unshared.versions).toHaveLength(1);
|
||||
expect(unshared.projectIds.map((id) => id.toString())).not.toContain(projectId1.toString());
|
||||
expect(unshared.projectIds.map((id) => id.toString())).toContain(projectId2.toString());
|
||||
|
||||
// Regular update without skipVersioning should create a version
|
||||
const regularUpdate = await updateAgent(
|
||||
{ id: agentId },
|
||||
{ description: 'Updated description' },
|
||||
);
|
||||
|
||||
expect(regularUpdate.versions).toHaveLength(2);
|
||||
expect(regularUpdate.description).toBe('Updated description');
|
||||
|
||||
// Direct updateAgent with MongoDB operators should still create versions
|
||||
const directUpdate = await updateAgent(
|
||||
{ id: agentId },
|
||||
{ $addToSet: { projectIds: { $each: [projectId1] } } },
|
||||
);
|
||||
|
||||
expect(directUpdate.versions).toHaveLength(3);
|
||||
expect(directUpdate.projectIds.length).toBe(2);
|
||||
});
|
||||
|
||||
test('should preserve agent_ids in version history', async () => {
|
||||
const agentId = `agent_${uuidv4()}`;
|
||||
const authorId = new mongoose.Types.ObjectId();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue