mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00

* feat: Add BedrockIcon component to SVG library * feat: EModelEndpoint.bedrock * feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint * fix: declare endpoint in initialization step * chore: Update @librechat/agents dependency to version 1.4.5 * feat: backend content aggregation for agents/bedrock * feat: abort agent requests * feat: AWS Bedrock icons * WIP: agent provider schema parsing * chore: Update EditIcon props type * refactor(useGenerationsByLatest): make agents and bedrock editable * refactor: non-assistant message content, parts * fix: Bedrock response `sender` * fix: use endpointOption.model_parameters not endpointOption.modelOptions * fix: types for step handler * refactor: Update Agents.ToolCallDelta type * refactor: Remove unnecessary assignment of parentMessageId in AskController * refactor: remove unnecessary assignment of parentMessageId (agent request handler) * fix(bedrock/agents): message regeneration * refactor: dynamic form elements using react-hook-form Controllers * fix: agent icons/labels for messages * fix: agent actions * fix: use of new dynamic tags causing application crash * refactor: dynamic settings touch-ups * refactor: update Slider component to allow custom track class name * refactor: update DynamicSlider component styles * refactor: use Constants value for GLOBAL_PROJECT_NAME (enum) * feat: agent share global methods/controllers * fix: agents query * fix: `getResponseModel` * fix: share prompt a11y issue * refactor: update SharePrompt dialog theme styles * refactor: explicit typing for SharePrompt * feat: add agent roles/permissions * chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case * fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary * fix: handle text parts with tool_call_ids and empty text * fix: role initialization * refactor: don't make instructions required * refactor: improve typing of Text part * fix: setShowStopButton for agents route * chore: remove params for now * fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API * refactor: remove console.log statement in ContentRender component * chore: typing, rename Context to Delete Button * chore(DeleteButton): logging * refactor(Action): make accessible * style(Action): improve a11y again * refactor: remove use/mention of mongoose sessions * feat: first pass, sharing agents * feat: visual indicator for global agent, remove author when serving to non-author * wip: params * chore: fix typing issues * fix(schemas): typing * refactor: improve accessibility of ListCard component and fix console React warning * wip: reset templates for non-legacy new convos * Revert "wip: params" This reverts commitf8067e91d4
. * Revert "refactor: dynamic form elements using react-hook-form Controllers" This reverts commit2150c4815d
. * fix(Parameters): types and parameter effect update to only update local state to parameters * refactor: optimize useDebouncedInput hook for better performance * feat: first pass, anthropic bedrock params * chore: paramEndpoints check for endpointType too * fix: maxTokens to use coerceNumber.optional(), * feat: extra chat model params * chore: reduce code repetition * refactor: improve preset title handling in SaveAsPresetDialog component * refactor: improve preset handling in HeaderOptions component * chore: improve typing, replace legacy dialog for SaveAsPresetDialog * feat: save as preset from parameters panel * fix: multi-search in select dropdown when using Option type * refactor: update default showDefault value to false in Dynamic components * feat: Bedrock presets settings * chore: config, fix agents schema, update config version * refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION * refactor: update baseEndpointSchema in config.ts to include baseURL property * refactor: update createRun function to include req parameter and set streamRate based on provider * feat: availableRegions via config * refactor: remove unused demo agent controller file * WIP: title * Update @librechat/agents to version 1.5.0 * chore: addTitle.js to handle empty responseText * feat: support images and titles * feat: context token updates * Refactor BaseClient test to use expect.objectContaining * refactor: add model select, remove header options params, move side panel params below prompts * chore: update models list, catch title error * feat: model service for bedrock models (env) * chore: Remove verbose debug log in AgentClient class following stream * feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models * refactor: handle streamRate in `handleLLMNewToken` callback * chore: AWS Bedrock example config in `.env.example` * refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers * refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes * feat: titleModel support for bedrock * refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
239 lines
7.1 KiB
JavaScript
239 lines
7.1 KiB
JavaScript
const { nanoid } = require('nanoid');
|
|
const { FileContext, Constants } = require('librechat-data-provider');
|
|
const {
|
|
getAgent,
|
|
createAgent,
|
|
updateAgent,
|
|
deleteAgent,
|
|
getListAgents,
|
|
} = require('~/models/Agent');
|
|
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
|
const { uploadImageBuffer } = require('~/server/services/Files/process');
|
|
const { getProjectByName } = require('~/models/Project');
|
|
const { updateAgentProjects } = require('~/models/Agent');
|
|
const { deleteFileByFilter } = require('~/models/File');
|
|
const { logger } = require('~/config');
|
|
|
|
/**
|
|
* Creates an Agent.
|
|
* @route POST /Agents
|
|
* @param {ServerRequest} req - The request object.
|
|
* @param {AgentCreateParams} req.body - The request body.
|
|
* @param {ServerResponse} res - The response object.
|
|
* @returns {Agent} 201 - success response - application/json
|
|
*/
|
|
const createAgentHandler = async (req, res) => {
|
|
try {
|
|
const { tools = [], provider, name, description, instructions, model, ...agentData } = req.body;
|
|
const { id: userId } = req.user;
|
|
|
|
agentData.tools = tools
|
|
.map((tool) => (typeof tool === 'string' ? req.app.locals.availableTools[tool] : tool))
|
|
.filter(Boolean);
|
|
|
|
Object.assign(agentData, {
|
|
author: userId,
|
|
name,
|
|
description,
|
|
instructions,
|
|
provider,
|
|
model,
|
|
});
|
|
|
|
agentData.id = `agent_${nanoid()}`;
|
|
const agent = await createAgent(agentData);
|
|
res.status(201).json(agent);
|
|
} catch (error) {
|
|
logger.error('[/Agents] Error creating agent', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieves an Agent by ID.
|
|
* @route GET /Agents/:id
|
|
* @param {object} req - Express Request
|
|
* @param {object} req.params - Request params
|
|
* @param {string} req.params.id - Agent identifier.
|
|
* @param {object} req.user - Authenticated user information
|
|
* @param {string} req.user.id - User ID
|
|
* @returns {Promise<Agent>} 200 - success response - application/json
|
|
* @returns {Error} 404 - Agent not found
|
|
*/
|
|
const getAgentHandler = async (req, res) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const author = req.user.id;
|
|
|
|
let query = { id, author };
|
|
|
|
const globalProject = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, ['agentIds']);
|
|
if (globalProject && (globalProject.agentIds?.length ?? 0) > 0) {
|
|
query = {
|
|
$or: [{ id, $in: globalProject.agentIds }, query],
|
|
};
|
|
}
|
|
|
|
const agent = await getAgent(query);
|
|
|
|
if (!agent) {
|
|
return res.status(404).json({ error: 'Agent not found' });
|
|
}
|
|
|
|
if (agent.author !== author) {
|
|
delete agent.author;
|
|
}
|
|
|
|
return res.status(200).json(agent);
|
|
} catch (error) {
|
|
logger.error('[/Agents/:id] Error retrieving agent', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Updates an Agent.
|
|
* @route PATCH /Agents/:id
|
|
* @param {object} req - Express Request
|
|
* @param {object} req.params - Request params
|
|
* @param {string} req.params.id - Agent identifier.
|
|
* @param {AgentUpdateParams} req.body - The Agent update parameters.
|
|
* @returns {Agent} 200 - success response - application/json
|
|
*/
|
|
const updateAgentHandler = async (req, res) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const { projectIds, removeProjectIds, ...updateData } = req.body;
|
|
|
|
let updatedAgent;
|
|
if (Object.keys(updateData).length > 0) {
|
|
updatedAgent = await updateAgent({ id, author: req.user.id }, updateData);
|
|
}
|
|
|
|
if (projectIds || removeProjectIds) {
|
|
updatedAgent = await updateAgentProjects(id, projectIds, removeProjectIds);
|
|
}
|
|
|
|
return res.json(updatedAgent);
|
|
} catch (error) {
|
|
logger.error('[/Agents/:id] Error updating Agent', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deletes an Agent based on the provided ID.
|
|
* @route DELETE /Agents/:id
|
|
* @param {object} req - Express Request
|
|
* @param {object} req.params - Request params
|
|
* @param {string} req.params.id - Agent identifier.
|
|
* @returns {Agent} 200 - success response - application/json
|
|
*/
|
|
const deleteAgentHandler = async (req, res) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const agent = await getAgent({ id });
|
|
if (!agent) {
|
|
return res.status(404).json({ error: 'Agent not found' });
|
|
}
|
|
await deleteAgent({ id, author: req.user.id });
|
|
return res.json({ message: 'Agent deleted' });
|
|
} catch (error) {
|
|
logger.error('[/Agents/:id] Error deleting Agent', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @route GET /Agents
|
|
* @param {object} req - Express Request
|
|
* @param {object} req.query - Request query
|
|
* @param {string} [req.query.user] - The user ID of the agent's author.
|
|
* @returns {Promise<AgentListResponse>} 200 - success response - application/json
|
|
*/
|
|
const getListAgentsHandler = async (req, res) => {
|
|
try {
|
|
const data = await getListAgents({
|
|
author: req.user.id,
|
|
});
|
|
return res.json(data);
|
|
} catch (error) {
|
|
logger.error('[/Agents] Error listing Agents', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Uploads and updates an avatar for a specific agent.
|
|
* @route POST /avatar/:agent_id
|
|
* @param {object} req - Express Request
|
|
* @param {object} req.params - Request params
|
|
* @param {string} req.params.agent_id - The ID of the agent.
|
|
* @param {Express.Multer.File} req.file - The avatar image file.
|
|
* @param {object} req.body - Request body
|
|
* @param {string} [req.body.avatar] - Optional avatar for the agent's avatar.
|
|
* @returns {Object} 200 - success response - application/json
|
|
*/
|
|
const uploadAgentAvatarHandler = async (req, res) => {
|
|
try {
|
|
const { agent_id } = req.params;
|
|
if (!agent_id) {
|
|
return res.status(400).json({ message: 'Agent ID is required' });
|
|
}
|
|
|
|
let { avatar: _avatar = '{}' } = req.body;
|
|
|
|
const image = await uploadImageBuffer({
|
|
req,
|
|
context: FileContext.avatar,
|
|
metadata: {
|
|
buffer: req.file.buffer,
|
|
},
|
|
});
|
|
|
|
try {
|
|
_avatar = JSON.parse(_avatar);
|
|
} catch (error) {
|
|
logger.error('[/avatar/:agent_id] Error parsing avatar', error);
|
|
_avatar = {};
|
|
}
|
|
|
|
if (_avatar && _avatar.source) {
|
|
const { deleteFile } = getStrategyFunctions(_avatar.source);
|
|
try {
|
|
await deleteFile(req, { filepath: _avatar.filepath });
|
|
await deleteFileByFilter({ filepath: _avatar.filepath });
|
|
} catch (error) {
|
|
logger.error('[/avatar/:agent_id] Error deleting old avatar', error);
|
|
}
|
|
}
|
|
|
|
const promises = [];
|
|
|
|
const data = {
|
|
avatar: {
|
|
filepath: image.filepath,
|
|
source: req.app.locals.fileStrategy,
|
|
},
|
|
};
|
|
|
|
promises.push(await updateAgent({ id: agent_id, author: req.user.id }, data));
|
|
|
|
const resolved = await Promise.all(promises);
|
|
res.status(201).json(resolved[0]);
|
|
} catch (error) {
|
|
const message = 'An error occurred while updating the Agent Avatar';
|
|
logger.error(message, error);
|
|
res.status(500).json({ message });
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
createAgent: createAgentHandler,
|
|
getAgent: getAgentHandler,
|
|
updateAgent: updateAgentHandler,
|
|
deleteAgent: deleteAgentHandler,
|
|
getListAgents: getListAgentsHandler,
|
|
uploadAgentAvatar: uploadAgentAvatarHandler,
|
|
};
|