diff --git a/api/server/routes/agents/actions.js b/api/server/routes/agents/actions.js index 398481b6aa..5d5456c29f 100644 --- a/api/server/routes/agents/actions.js +++ b/api/server/routes/agents/actions.js @@ -1,6 +1,6 @@ const express = require('express'); const { nanoid } = require('nanoid'); -const { actionDelimiter } = require('librechat-data-provider'); +const { actionDelimiter, SystemRoles } = require('librechat-data-provider'); const { encryptMetadata, domainParser } = require('~/server/services/ActionService'); const { updateAction, getActions, deleteAction } = require('~/models/Action'); const { isActionDomainAllowed } = require('~/server/services/domains'); @@ -9,6 +9,12 @@ const { logger } = require('~/config'); const router = express.Router(); +// If the user has ADMIN role +// then action edition is possible even if not owner of the assistant +const isAdmin = (req) => { + return req.user.role === SystemRoles.ADMIN; +}; + /** * Retrieves all user's actions * @route GET /actions/ @@ -17,7 +23,10 @@ const router = express.Router(); */ router.get('/', async (req, res) => { try { - res.json(await getActions({ user: req.user.id })); + const admin = isAdmin(req); + // If admin, get all actions, otherwise only user's actions + const searchParams = admin ? {} : { user: req.user.id }; + res.json(await getActions(searchParams)); } catch (error) { res.status(500).json({ error: error.message }); } @@ -57,9 +66,12 @@ router.post('/:agent_id', async (req, res) => { const action_id = _action_id ?? nanoid(); const initialPromises = []; + const admin = isAdmin(req); + // If admin, can edit any agent, otherwise only user's agents + const agentQuery = admin ? { id: agent_id } : { id: agent_id, author: req.user.id }; // TODO: share agents - initialPromises.push(getAgent({ id: agent_id, author: req.user.id })); + initialPromises.push(getAgent(agentQuery)); if (_action_id) { initialPromises.push(getActions({ action_id }, true)); } @@ -75,7 +87,7 @@ router.post('/:agent_id', async (req, res) => { metadata = { ...action.metadata, ...metadata }; } - const { actions: _actions = [] } = agent ?? {}; + const { actions: _actions = [], author: agent_author } = agent ?? {}; const actions = []; for (const action of _actions) { const [_action_domain, current_action_id] = action.split(actionDelimiter); @@ -95,14 +107,19 @@ router.post('/:agent_id', async (req, res) => { .filter((tool) => !(tool && (tool.includes(domain) || tool.includes(action_id)))) .concat(functions.map((tool) => `${tool.function.name}${actionDelimiter}${domain}`)); - const updatedAgent = await updateAgent( - { id: agent_id, author: req.user.id }, - { tools, actions }, - ); + const updatedAgent = await updateAgent(agentQuery, { tools, actions }); + + // Only update user field for new actions + const actionUpdateData = { metadata, agent_id }; + if (!actions_result || !actions_result.length) { + // For new actions, use the agent owner's user ID + actionUpdateData.user = agent_author || req.user.id; + } + /** @type {[Action]} */ const updatedAction = await updateAction( { action_id }, - { metadata, agent_id, user: req.user.id }, + actionUpdateData, ); const sensitiveFields = ['api_key', 'oauth_client_id', 'oauth_client_secret']; @@ -130,8 +147,11 @@ router.post('/:agent_id', async (req, res) => { router.delete('/:agent_id/:action_id', async (req, res) => { try { const { agent_id, action_id } = req.params; + const admin = isAdmin(req); - const agent = await getAgent({ id: agent_id, author: req.user.id }); + // If admin, can delete any agent, otherwise only user's agents + const agentQuery = admin ? { id: agent_id } : { id: agent_id, author: req.user.id }; + const agent = await getAgent(agentQuery); if (!agent) { return res.status(404).json({ message: 'Agent not found for deleting action' }); } @@ -155,11 +175,10 @@ router.delete('/:agent_id/:action_id', async (req, res) => { const updatedTools = tools.filter((tool) => !(tool && tool.includes(domain))); - await updateAgent( - { id: agent_id, author: req.user.id }, - { tools: updatedTools, actions: updatedActions }, - ); - await deleteAction({ action_id }); + await updateAgent(agentQuery, { tools: updatedTools, actions: updatedActions }); + // If admin, can delete any action, otherwise only user's actions + const actionQuery = admin ? { action_id } : { action_id, user: req.user.id }; + await deleteAction(actionQuery); res.status(200).json({ message: 'Action deleted successfully' }); } catch (error) { const message = 'Trouble deleting the Agent Action'; diff --git a/api/server/routes/assistants/actions.js b/api/server/routes/assistants/actions.js index c3941e9177..2d12187372 100644 --- a/api/server/routes/assistants/actions.js +++ b/api/server/routes/assistants/actions.js @@ -63,7 +63,7 @@ router.post('/:assistant_id', async (req, res) => { return res.status(404).json({ message: 'Assistant not found' }); } - const { actions: _actions = [] } = assistant_data ?? {}; + const { actions: _actions = [], user: assistant_user } = assistant_data ?? {}; const actions = []; for (const action of _actions) { const [_action_domain, current_action_id] = action.split(actionDelimiter); @@ -99,16 +99,26 @@ router.post('/:assistant_id', async (req, res) => { let updatedAssistant = await openai.beta.assistants.update(assistant_id, { tools }); const promises = []; + + // Only update user field for new assistant documents + const assistantUpdateData = { actions }; + if (!assistant_data) { + assistantUpdateData.user = req.user.id; + } promises.push( updateAssistantDoc( { assistant_id }, - { - actions, - user: req.user.id, - }, - ), + assistantUpdateData, + ) ); - promises.push(updateAction({ action_id }, { metadata, assistant_id, user: req.user.id })); + + // Only update user field for new actions + const actionUpdateData = { metadata, assistant_id }; + if (!actions_result || !actions_result.length) { + // For new actions, use the assistant owner's user ID + actionUpdateData.user = assistant_user || req.user.id; + } + promises.push(updateAction({ action_id }, actionUpdateData)); /** @type {[AssistantDocument, Action]} */ let [assistantDocument, updatedAction] = await Promise.all(promises); @@ -180,13 +190,15 @@ router.delete('/:assistant_id/:action_id/:model', async (req, res) => { await openai.beta.assistants.update(assistant_id, { tools: updatedTools }); const promises = []; + // Only update user field if assistant document doesn't exist + const assistantUpdateData = { actions }; + if (!assistant_data) { + assistantUpdateData.user = req.user.id; + } promises.push( updateAssistantDoc( - { assistant_id }, - { - actions: updatedActions, - user: req.user.id, - }, + { assistant_id }, + assistantUpdateData, ), ); promises.push(deleteAction({ action_id }));