mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-21 17:44:11 +00:00
🦥 perf: Lazy-Load Agent Version History in Editor (#13977)
Opening the agent editor fetched the full `versions` array (each a complete config snapshot) alongside the agent, so agents with large histories were slow to open. Version history is now loaded only when the user opens it. - Add `getAgentWithVersionCount` (aggregation: version count, no versions array) and `getAgentVersions` data-schemas methods. - `getAgentHandler` returns the version count without the heavy array; add `GET /agents/:id/versions` (EDIT-gated) for lazy retrieval. - Add `useGetAgentVersionsQuery`; VersionPanel reads current config from the cached expanded query and fetches versions on open. Revert keeps the expanded cache and versions query in sync.
This commit is contained in:
parent
b15d40e3e4
commit
12fea693bb
10 changed files with 301 additions and 45 deletions
|
|
@ -487,16 +487,15 @@ const getAgentHandler = async (req, res, expandProperties = false) => {
|
|||
const id = req.params.id;
|
||||
const author = req.user.id;
|
||||
|
||||
// Permissions are validated by middleware before calling this function
|
||||
// Simply load the agent by ID
|
||||
const agent = await db.getAgent({ id });
|
||||
// Permissions are validated by middleware before calling this function.
|
||||
// Load the agent with a `version` count but without the heavy `versions`
|
||||
// array; version history is fetched lazily via GET /agents/:id/versions.
|
||||
const agent = await db.getAgentWithVersionCount({ id });
|
||||
|
||||
if (!agent) {
|
||||
return res.status(404).json({ error: 'Agent not found' });
|
||||
}
|
||||
|
||||
agent.version = agent.versions ? agent.versions.length : 0;
|
||||
|
||||
if (agent.avatar && agent.avatar?.source === FileSources.s3) {
|
||||
try {
|
||||
agent.avatar = {
|
||||
|
|
@ -561,6 +560,32 @@ const getAgentHandler = async (req, res, expandProperties = false) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves an agent's version history.
|
||||
* Loaded lazily so the editor doesn't transfer large histories up front.
|
||||
* @route GET /agents/:id/versions
|
||||
* @param {object} req - Express Request
|
||||
* @param {object} req.params - Request params
|
||||
* @param {string} req.params.id - Agent identifier.
|
||||
* @returns {Promise<Agent[]>} 200 - The agent's version history - application/json
|
||||
* @returns {Error} 404 - Agent not found
|
||||
*/
|
||||
const getAgentVersionsHandler = async (req, res) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
const versions = await db.getAgentVersions({ id });
|
||||
|
||||
if (versions == null) {
|
||||
return res.status(404).json({ error: 'Agent not found' });
|
||||
}
|
||||
|
||||
return res.status(200).json(versions);
|
||||
} catch (error) {
|
||||
logger.error('[/Agents/:id/versions] Error retrieving agent versions', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an Agent.
|
||||
* @route PATCH /Agents/:id
|
||||
|
|
@ -1330,6 +1355,7 @@ const getAgentCategories = async (_req, res) => {
|
|||
module.exports = {
|
||||
createAgent: createAgentHandler,
|
||||
getAgent: getAgentHandler,
|
||||
getAgentVersions: getAgentVersionsHandler,
|
||||
updateAgent: updateAgentHandler,
|
||||
duplicateAgent: duplicateAgentHandler,
|
||||
deleteAgent: deleteAgentHandler,
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ jest.mock('~/cache', () => ({
|
|||
const {
|
||||
createAgent: createAgentHandler,
|
||||
getAgent: getAgentHandler,
|
||||
getAgentVersions: getAgentVersionsHandler,
|
||||
duplicateAgent: duplicateAgentHandler,
|
||||
revertAgentVersion: revertAgentVersionHandler,
|
||||
updateAgent: updateAgentHandler,
|
||||
|
|
@ -603,6 +604,45 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getAgentVersionsHandler', () => {
|
||||
test('returns the version history and excludes it from the basic VIEW response', async () => {
|
||||
const agent = await Agent.create({
|
||||
id: `agent_${uuidv4()}`,
|
||||
name: 'Versioned Agent',
|
||||
provider: 'openai',
|
||||
model: 'gpt-4',
|
||||
author: mockReq.user.id,
|
||||
versions: [
|
||||
{ name: 'V1', provider: 'openai', model: 'gpt-4', updatedAt: new Date() },
|
||||
{ name: 'V2', provider: 'openai', model: 'gpt-4', updatedAt: new Date() },
|
||||
],
|
||||
});
|
||||
mockReq.params = { id: agent.id };
|
||||
|
||||
await getAgentHandler(mockReq, mockRes);
|
||||
const basicResponse = mockRes.json.mock.calls[0][0];
|
||||
expect(basicResponse.versions).toBeUndefined();
|
||||
expect(basicResponse.version).toBe(2);
|
||||
|
||||
mockRes.json.mockClear();
|
||||
await getAgentVersionsHandler(mockReq, mockRes);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(200);
|
||||
const versions = mockRes.json.mock.calls[0][0];
|
||||
expect(Array.isArray(versions)).toBe(true);
|
||||
expect(versions).toHaveLength(2);
|
||||
expect(versions.map((v) => v.name)).toEqual(['V1', 'V2']);
|
||||
});
|
||||
|
||||
test('returns 404 when the agent does not exist', async () => {
|
||||
mockReq.params = { id: `agent_${uuidv4()}` };
|
||||
|
||||
await getAgentVersionsHandler(mockReq, mockRes);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateAgentHandler', () => {
|
||||
let existingAgentId;
|
||||
let existingAgentAuthorId;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,23 @@ router.get(
|
|||
}),
|
||||
(req, res) => v1.getAgent(req, res, true), // Expanded version
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves an agent's version history (EDIT permission required).
|
||||
* Loaded lazily so the editor doesn't transfer large histories up front.
|
||||
* @route GET /agents/:id/versions
|
||||
* @param {string} req.params.id - Agent identifier.
|
||||
* @returns {Agent[]} 200 - Agent version history - application/json
|
||||
*/
|
||||
router.get(
|
||||
'/:id/versions',
|
||||
checkAgentAccess,
|
||||
canAccessAgentResource({
|
||||
requiredPermission: PermissionBits.EDIT,
|
||||
resourceIdParam: 'id',
|
||||
}),
|
||||
v1.getAgentVersions,
|
||||
);
|
||||
/**
|
||||
* Updates an agent.
|
||||
* @route PATCH /agents/:id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue