mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-25 11:47:32 +00:00
✏️ feat: Add Agent Management Update Endpoint (#15566)
Some checks are pending
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
Some checks are pending
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
* feat: add Agent Management update endpoint * fix: reject unsupported Agent update values * test: simplify Agent update mocks
This commit is contained in:
parent
9a43362663
commit
ab0d74b492
8 changed files with 545 additions and 15 deletions
|
|
@ -105,6 +105,7 @@ const {
|
|||
createAgentManagementAuth,
|
||||
createAgentManagementCreateHandler,
|
||||
createAgentManagementReadHandlers,
|
||||
createAgentManagementUpdateHandler,
|
||||
mergeDeploymentSkillIds,
|
||||
refreshS3Url,
|
||||
} = require('@librechat/api');
|
||||
|
|
@ -380,7 +381,7 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
expect(agentInDb.author.toString()).toBe(mockReq.user.id);
|
||||
});
|
||||
|
||||
test('management creation can be read back through the authenticated management API', async () => {
|
||||
test('management creation can be updated and read through the authenticated management API', async () => {
|
||||
const tenantId = `tenant-${nanoid(8)}`;
|
||||
const clientId = `client-${nanoid(8)}`;
|
||||
const principal = await tenantStorage.run({ tenantId }, () => createOwner());
|
||||
|
|
@ -435,6 +436,14 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
getRoleByName,
|
||||
createAgent: createAgentHandler,
|
||||
});
|
||||
const checkEditPermission = async ({ userId: accessibleUserId, resourceType, resourceId }) =>
|
||||
(await AclEntry.exists({
|
||||
principalId: accessibleUserId,
|
||||
resourceType,
|
||||
resourceId,
|
||||
permBits: { $bitsAllSet: PermissionBits.EDIT },
|
||||
})) != null;
|
||||
const hasCapability = jest.fn().mockResolvedValue(false);
|
||||
const reads = createAgentManagementReadHandlers({
|
||||
getRoleByName,
|
||||
getAgentWithVersionCount: db.getAgentWithVersionCount,
|
||||
|
|
@ -445,14 +454,15 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
resourceType,
|
||||
permBits: { $bitsAllSet: PermissionBits.EDIT },
|
||||
}),
|
||||
checkPermission: async ({ userId: accessibleUserId, resourceType, resourceId }) =>
|
||||
(await AclEntry.exists({
|
||||
principalId: accessibleUserId,
|
||||
resourceType,
|
||||
resourceId,
|
||||
permBits: { $bitsAllSet: PermissionBits.EDIT },
|
||||
})) != null,
|
||||
hasCapability: jest.fn().mockResolvedValue(false),
|
||||
checkPermission: checkEditPermission,
|
||||
hasCapability,
|
||||
});
|
||||
const update = createAgentManagementUpdateHandler({
|
||||
getRoleByName,
|
||||
getAgentWithVersionCount: db.getAgentWithVersionCount,
|
||||
checkPermission: checkEditPermission,
|
||||
hasCapability,
|
||||
updateAgent: updateAgentHandler,
|
||||
});
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
|
@ -463,14 +473,21 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
});
|
||||
app.get('/api/agents/v1/agents', reads.list);
|
||||
app.get('/api/agents/v1/agents/:id', reads.get);
|
||||
app.patch('/api/agents/v1/agents/:id', (req, res) => {
|
||||
req.config = {};
|
||||
return update(req, res);
|
||||
});
|
||||
|
||||
const createdResponse = await request(app)
|
||||
.post('/api/agents/v1/agents')
|
||||
.set('Authorization', 'Bearer valid-token')
|
||||
.send({
|
||||
name: 'Managed Agent',
|
||||
description: 'Description that remains unchanged',
|
||||
provider: 'openai',
|
||||
model: 'gpt-4',
|
||||
avatar: { filepath: 'avatars/managed.png', source: 'local' },
|
||||
conversation_starters: ['Help me get started'],
|
||||
});
|
||||
|
||||
expect(createdResponse.status).toBe(201);
|
||||
|
|
@ -499,11 +516,63 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
expect(listedResponse.status).toBe(200);
|
||||
expect(listedResponse.body.data).toEqual([createdResponse.body]);
|
||||
|
||||
const created = await tenantStorage.run({ tenantId }, () =>
|
||||
Agent.findOne({ id: createdResponse.body.id }).lean(),
|
||||
const otherTenantId = `tenant-${nanoid(8)}`;
|
||||
await tenantStorage.run({ tenantId: otherTenantId }, () =>
|
||||
Agent.create({
|
||||
id: createdResponse.body.id,
|
||||
name: 'Other tenant Agent',
|
||||
provider: 'openai',
|
||||
model: 'gpt-4',
|
||||
author: new mongoose.Types.ObjectId(),
|
||||
}),
|
||||
);
|
||||
|
||||
const updatedResponse = await request(app)
|
||||
.patch(`/api/agents/v1/agents/${createdResponse.body.id}`)
|
||||
.set('Authorization', 'Bearer valid-token')
|
||||
.send({
|
||||
name: 'Updated Managed Agent',
|
||||
avatar: null,
|
||||
conversation_starters: [],
|
||||
});
|
||||
|
||||
expect(updatedResponse.status).toBe(200);
|
||||
expect(updatedResponse.body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: createdResponse.body.id,
|
||||
name: 'Updated Managed Agent',
|
||||
description: 'Description that remains unchanged',
|
||||
avatar: null,
|
||||
conversation_starters: [],
|
||||
version: 2,
|
||||
}),
|
||||
);
|
||||
expect(updatedResponse.body).not.toHaveProperty('_id');
|
||||
expect(updatedResponse.body).not.toHaveProperty('author');
|
||||
expect(updatedResponse.body).not.toHaveProperty('tenantId');
|
||||
|
||||
const retrievedAfterUpdate = await request(app)
|
||||
.get(`/api/agents/v1/agents/${createdResponse.body.id}`)
|
||||
.set('Authorization', 'Bearer valid-token');
|
||||
expect(retrievedAfterUpdate.status).toBe(200);
|
||||
expect(retrievedAfterUpdate.body).toEqual(updatedResponse.body);
|
||||
|
||||
const created = await Agent.collection.findOne({
|
||||
id: createdResponse.body.id,
|
||||
tenantId,
|
||||
});
|
||||
const otherTenantAgent = await Agent.collection.findOne({
|
||||
id: createdResponse.body.id,
|
||||
tenantId: otherTenantId,
|
||||
});
|
||||
expect(created.tenantId).toBe(tenantId);
|
||||
expect(created.author.toString()).toBe(userId);
|
||||
expect(created.name).toBe('Updated Managed Agent');
|
||||
expect(created.description).toBe('Description that remains unchanged');
|
||||
expect(created.avatar).toBeNull();
|
||||
expect(created.conversation_starters).toEqual([]);
|
||||
expect(created.versions).toHaveLength(2);
|
||||
expect(otherTenantAgent.name).toBe('Other tenant Agent');
|
||||
expect(grantPermission).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
principalType: PrincipalType.USER,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue