| | const fs = require('fs').promises; |
| | const { logger } = require('@librechat/data-schemas'); |
| | const { FileContext } = require('librechat-data-provider'); |
| | const { uploadImageBuffer, filterFile } = require('~/server/services/Files/process'); |
| | const validateAuthor = require('~/server/middleware/assistants/validateAuthor'); |
| | const { getStrategyFunctions } = require('~/server/services/Files/strategies'); |
| | const { deleteAssistantActions } = require('~/server/services/ActionService'); |
| | const { updateAssistantDoc, getAssistants } = require('~/models/Assistant'); |
| | const { getOpenAIClient, fetchAssistants } = require('./helpers'); |
| | const { getCachedTools } = require('~/server/services/Config'); |
| | const { manifestToolMap } = require('~/app/clients/tools'); |
| | const { deleteFileByFilter } = require('~/models/File'); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | const createAssistant = async (req, res) => { |
| | try { |
| | const { openai } = await getOpenAIClient({ req, res }); |
| |
|
| | const { |
| | tools = [], |
| | endpoint, |
| | conversation_starters, |
| | append_current_datetime, |
| | ...assistantData |
| | } = req.body; |
| | delete assistantData.conversation_starters; |
| | delete assistantData.append_current_datetime; |
| |
|
| | const toolDefinitions = await getCachedTools(); |
| |
|
| | assistantData.tools = tools |
| | .map((tool) => { |
| | if (typeof tool !== 'string') { |
| | return tool; |
| | } |
| |
|
| | const toolDef = toolDefinitions[tool]; |
| | if (!toolDef && manifestToolMap[tool] && manifestToolMap[tool].toolkit === true) { |
| | return Object.entries(toolDefinitions) |
| | .filter(([key]) => key.startsWith(`${tool}_`)) |
| |
|
| | .map(([_, val]) => val); |
| | } |
| |
|
| | return toolDef; |
| | }) |
| | .filter((tool) => tool) |
| | .flat(); |
| |
|
| | let azureModelIdentifier = null; |
| | if (openai.locals?.azureOptions) { |
| | azureModelIdentifier = assistantData.model; |
| | assistantData.model = openai.locals.azureOptions.azureOpenAIApiDeploymentName; |
| | } |
| |
|
| | assistantData.metadata = { |
| | author: req.user.id, |
| | endpoint, |
| | }; |
| |
|
| | const assistant = await openai.beta.assistants.create(assistantData); |
| |
|
| | const createData = { user: req.user.id }; |
| | if (conversation_starters) { |
| | createData.conversation_starters = conversation_starters; |
| | } |
| | if (append_current_datetime !== undefined) { |
| | createData.append_current_datetime = append_current_datetime; |
| | } |
| |
|
| | const document = await updateAssistantDoc({ assistant_id: assistant.id }, createData); |
| |
|
| | if (azureModelIdentifier) { |
| | assistant.model = azureModelIdentifier; |
| | } |
| |
|
| | if (document.conversation_starters) { |
| | assistant.conversation_starters = document.conversation_starters; |
| | } |
| |
|
| | if (append_current_datetime !== undefined) { |
| | assistant.append_current_datetime = append_current_datetime; |
| | } |
| |
|
| | logger.debug('/assistants/', assistant); |
| | res.status(201).json(assistant); |
| | } catch (error) { |
| | logger.error('[/assistants] Error creating assistant', error); |
| | res.status(500).json({ error: error.message }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | const retrieveAssistant = async (req, res) => { |
| | try { |
| | |
| | const { openai } = await getOpenAIClient({ req, res }); |
| | const assistant_id = req.params.id; |
| | const assistant = await openai.beta.assistants.retrieve(assistant_id); |
| | res.json(assistant); |
| | } catch (error) { |
| | logger.error('[/assistants/:id] Error retrieving assistant', error); |
| | res.status(500).json({ error: error.message }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const patchAssistant = async (req, res) => { |
| | try { |
| | const { openai } = await getOpenAIClient({ req, res }); |
| | await validateAuthor({ req, openai }); |
| |
|
| | const assistant_id = req.params.id; |
| | const { |
| | endpoint: _e, |
| | conversation_starters, |
| | append_current_datetime, |
| | ...updateData |
| | } = req.body; |
| |
|
| | const toolDefinitions = await getCachedTools(); |
| |
|
| | updateData.tools = (updateData.tools ?? []) |
| | .map((tool) => { |
| | if (typeof tool !== 'string') { |
| | return tool; |
| | } |
| |
|
| | const toolDef = toolDefinitions[tool]; |
| | if (!toolDef && manifestToolMap[tool] && manifestToolMap[tool].toolkit === true) { |
| | return Object.entries(toolDefinitions) |
| | .filter(([key]) => key.startsWith(`${tool}_`)) |
| |
|
| | .map(([_, val]) => val); |
| | } |
| |
|
| | return toolDef; |
| | }) |
| | .filter((tool) => tool) |
| | .flat(); |
| |
|
| | if (openai.locals?.azureOptions && updateData.model) { |
| | updateData.model = openai.locals.azureOptions.azureOpenAIApiDeploymentName; |
| | } |
| |
|
| | const updatedAssistant = await openai.beta.assistants.update(assistant_id, updateData); |
| |
|
| | if (conversation_starters !== undefined) { |
| | const conversationStartersUpdate = await updateAssistantDoc( |
| | { assistant_id }, |
| | { conversation_starters }, |
| | ); |
| | updatedAssistant.conversation_starters = conversationStartersUpdate.conversation_starters; |
| | } |
| |
|
| | if (append_current_datetime !== undefined) { |
| | await updateAssistantDoc({ assistant_id }, { append_current_datetime }); |
| | updatedAssistant.append_current_datetime = append_current_datetime; |
| | } |
| |
|
| | res.json(updatedAssistant); |
| | } catch (error) { |
| | logger.error('[/assistants/:id] Error updating assistant', error); |
| | res.status(500).json({ error: error.message }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const deleteAssistant = async (req, res) => { |
| | try { |
| | const { openai } = await getOpenAIClient({ req, res }); |
| | await validateAuthor({ req, openai }); |
| |
|
| | const assistant_id = req.params.id; |
| | const deletionStatus = await openai.beta.assistants.delete(assistant_id); |
| | if (deletionStatus?.deleted) { |
| | await deleteAssistantActions({ req, assistant_id }); |
| | } |
| | res.json(deletionStatus); |
| | } catch (error) { |
| | logger.error('[/assistants/:id] Error deleting assistant', error); |
| | res.status(500).json({ error: 'Error deleting assistant' }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const listAssistants = async (req, res) => { |
| | try { |
| | const body = await fetchAssistants({ req, res }); |
| | res.json(body); |
| | } catch (error) { |
| | logger.error('[/assistants] Error listing assistants', error); |
| | res.status(500).json({ message: 'Error listing assistants' }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function filterAssistantDocs({ documents, userId, assistantsConfig = {} }) { |
| | const { supportedIds, excludedIds, privateAssistants } = assistantsConfig; |
| | const removeUserId = (doc) => { |
| | const { user: _u, ...document } = doc; |
| | return document; |
| | }; |
| |
|
| | if (privateAssistants) { |
| | return documents.filter((doc) => userId === doc.user.toString()).map(removeUserId); |
| | } else if (supportedIds?.length) { |
| | return documents.filter((doc) => supportedIds.includes(doc.assistant_id)).map(removeUserId); |
| | } else if (excludedIds?.length) { |
| | return documents.filter((doc) => !excludedIds.includes(doc.assistant_id)).map(removeUserId); |
| | } |
| | return documents.map(removeUserId); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | const getAssistantDocuments = async (req, res) => { |
| | try { |
| | const appConfig = req.config; |
| | const endpoint = req.query; |
| | const assistantsConfig = appConfig.endpoints?.[endpoint]; |
| | const documents = await getAssistants( |
| | {}, |
| | { |
| | user: 1, |
| | assistant_id: 1, |
| | conversation_starters: 1, |
| | createdAt: 1, |
| | updatedAt: 1, |
| | append_current_datetime: 1, |
| | }, |
| | ); |
| |
|
| | const docs = filterAssistantDocs({ |
| | documents, |
| | userId: req.user.id, |
| | assistantsConfig, |
| | }); |
| | res.json(docs); |
| | } catch (error) { |
| | logger.error('[/assistants/documents] Error listing assistant documents', error); |
| | res.status(500).json({ error: error.message }); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const uploadAssistantAvatar = async (req, res) => { |
| | try { |
| | const appConfig = req.config; |
| | filterFile({ req, file: req.file, image: true, isAvatar: true }); |
| | const { assistant_id } = req.params; |
| | if (!assistant_id) { |
| | return res.status(400).json({ message: 'Assistant ID is required' }); |
| | } |
| |
|
| | const { openai } = await getOpenAIClient({ req, res }); |
| | await validateAuthor({ req, openai }); |
| |
|
| | const buffer = await fs.readFile(req.file.path); |
| | const image = await uploadImageBuffer({ |
| | req, |
| | context: FileContext.avatar, |
| | metadata: { buffer }, |
| | }); |
| |
|
| | let _metadata; |
| |
|
| | try { |
| | const assistant = await openai.beta.assistants.retrieve(assistant_id); |
| | if (assistant) { |
| | _metadata = assistant.metadata; |
| | } |
| | } catch (error) { |
| | logger.error('[/:assistant_id/avatar] Error fetching assistant', error); |
| | _metadata = {}; |
| | } |
| |
|
| | if (_metadata.avatar && _metadata.avatar_source) { |
| | const { deleteFile } = getStrategyFunctions(_metadata.avatar_source); |
| | try { |
| | await deleteFile(req, { filepath: _metadata.avatar }); |
| | await deleteFileByFilter({ user: req.user.id, filepath: _metadata.avatar }); |
| | } catch (error) { |
| | logger.error('[/:assistant_id/avatar] Error deleting old avatar', error); |
| | } |
| | } |
| |
|
| | const metadata = { |
| | ..._metadata, |
| | avatar: image.filepath, |
| | avatar_source: appConfig.fileStrategy, |
| | }; |
| |
|
| | const promises = []; |
| | promises.push( |
| | updateAssistantDoc( |
| | { assistant_id }, |
| | { |
| | avatar: { |
| | filepath: image.filepath, |
| | source: appConfig.fileStrategy, |
| | }, |
| | user: req.user.id, |
| | }, |
| | ), |
| | ); |
| | promises.push(openai.beta.assistants.update(assistant_id, { metadata })); |
| |
|
| | const resolved = await Promise.all(promises); |
| | res.status(201).json(resolved[1]); |
| | } catch (error) { |
| | const message = 'An error occurred while updating the Assistant Avatar'; |
| | logger.error(message, error); |
| | res.status(500).json({ message }); |
| | } finally { |
| | try { |
| | await fs.unlink(req.file.path); |
| | logger.debug('[/:agent_id/avatar] Temp. image upload file deleted'); |
| | } catch { |
| | logger.debug('[/:agent_id/avatar] Temp. image upload file already deleted'); |
| | } |
| | } |
| | }; |
| |
|
| | module.exports = { |
| | createAssistant, |
| | retrieveAssistant, |
| | patchAssistant, |
| | deleteAssistant, |
| | listAssistants, |
| | getAssistantDocuments, |
| | uploadAssistantAvatar, |
| | }; |
| |
|