| const { |
| CacheKeys, |
| Constants, |
| AuthTypeEnum, |
| actionDelimiter, |
| isImageVisionTool, |
| actionDomainSeparator, |
| } = require('librechat-data-provider'); |
| const { encryptV2, decryptV2 } = require('~/server/utils/crypto'); |
| const { getActions, deleteActions } = require('~/models/Action'); |
| const { deleteAssistant } = require('~/models/Assistant'); |
| const { getLogStores } = require('~/cache'); |
| const { logger } = require('~/config'); |
|
|
| const toolNameRegex = /^[a-zA-Z0-9_-]+$/; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| const validateAndUpdateTool = async ({ req, tool, assistant_id }) => { |
| let actions; |
| if (isImageVisionTool(tool)) { |
| return null; |
| } |
| if (!toolNameRegex.test(tool.function.name)) { |
| const [functionName, domain] = tool.function.name.split(actionDelimiter); |
| actions = await getActions({ assistant_id, user: req.user.id }, true); |
| const matchingActions = actions.filter((action) => { |
| const metadata = action.metadata; |
| return metadata && metadata.domain === domain; |
| }); |
| const action = matchingActions[0]; |
| if (!action) { |
| return null; |
| } |
|
|
| const parsedDomain = await domainParser(req, domain, true); |
|
|
| if (!parsedDomain) { |
| return null; |
| } |
|
|
| tool.function.name = `${functionName}${actionDelimiter}${parsedDomain}`; |
| } |
| return tool; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function domainParser(req, domain, inverse = false) { |
| if (!domain) { |
| return; |
| } |
|
|
| const domainsCache = getLogStores(CacheKeys.ENCODED_DOMAINS); |
| const cachedDomain = await domainsCache.get(domain); |
| if (inverse && cachedDomain) { |
| return domain; |
| } |
|
|
| if (inverse && domain.length <= Constants.ENCODED_DOMAIN_LENGTH) { |
| return domain.replace(/\./g, actionDomainSeparator); |
| } |
|
|
| if (inverse) { |
| const modifiedDomain = Buffer.from(domain).toString('base64'); |
| const key = modifiedDomain.substring(0, Constants.ENCODED_DOMAIN_LENGTH); |
| await domainsCache.set(key, modifiedDomain); |
| return key; |
| } |
|
|
| const replaceSeparatorRegex = new RegExp(actionDomainSeparator, 'g'); |
|
|
| if (!cachedDomain) { |
| return domain.replace(replaceSeparatorRegex, '.'); |
| } |
|
|
| try { |
| return Buffer.from(cachedDomain, 'base64').toString('utf-8'); |
| } catch (error) { |
| logger.error(`Failed to parse domain (possibly not base64): ${domain}`, error); |
| return domain; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| async function loadActionSets(searchParams) { |
| return await getActions(searchParams, true); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| function createActionTool({ action, requestBuilder }) { |
| action.metadata = decryptMetadata(action.metadata); |
| const _call = async (toolInput) => { |
| try { |
| requestBuilder.setParams(toolInput); |
| if (action.metadata.auth && action.metadata.auth.type !== AuthTypeEnum.None) { |
| await requestBuilder.setAuth(action.metadata); |
| } |
| const res = await requestBuilder.execute(); |
| if (typeof res.data === 'object') { |
| return JSON.stringify(res.data); |
| } |
| return res.data; |
| } catch (error) { |
| logger.error(`API call to ${action.metadata.domain} failed`, error); |
| if (error.response) { |
| const { status, data } = error.response; |
| return `API call to ${ |
| action.metadata.domain |
| } failed with status ${status}: ${JSON.stringify(data)}`; |
| } |
|
|
| return `API call to ${action.metadata.domain} failed.`; |
| } |
| }; |
|
|
| return { |
| _call, |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function encryptMetadata(metadata) { |
| const encryptedMetadata = { ...metadata }; |
|
|
| |
| if (metadata.auth && metadata.auth.type === AuthTypeEnum.ServiceHttp) { |
| if (metadata.api_key) { |
| encryptedMetadata.api_key = encryptV2(metadata.api_key); |
| } |
| } |
|
|
| |
| else if (metadata.auth && metadata.auth.type === AuthTypeEnum.OAuth) { |
| if (metadata.oauth_client_id) { |
| encryptedMetadata.oauth_client_id = encryptV2(metadata.oauth_client_id); |
| } |
| if (metadata.oauth_client_secret) { |
| encryptedMetadata.oauth_client_secret = encryptV2(metadata.oauth_client_secret); |
| } |
| } |
|
|
| return encryptedMetadata; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function decryptMetadata(metadata) { |
| const decryptedMetadata = { ...metadata }; |
|
|
| |
| if (metadata.auth && metadata.auth.type === AuthTypeEnum.ServiceHttp) { |
| if (metadata.api_key) { |
| decryptedMetadata.api_key = decryptV2(metadata.api_key); |
| } |
| } |
|
|
| |
| else if (metadata.auth && metadata.auth.type === AuthTypeEnum.OAuth) { |
| if (metadata.oauth_client_id) { |
| decryptedMetadata.oauth_client_id = decryptV2(metadata.oauth_client_id); |
| } |
| if (metadata.oauth_client_secret) { |
| decryptedMetadata.oauth_client_secret = decryptV2(metadata.oauth_client_secret); |
| } |
| } |
|
|
| return decryptedMetadata; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| const deleteAssistantActions = async ({ req, assistant_id }) => { |
| try { |
| await deleteActions({ assistant_id, user: req.user.id }); |
| await deleteAssistant({ assistant_id, user: req.user.id }); |
| } catch (error) { |
| const message = 'Trouble deleting Assistant Actions for Assistant ID: ' + assistant_id; |
| logger.error(message, error); |
| throw new Error(message); |
| } |
| }; |
|
|
| module.exports = { |
| deleteAssistantActions, |
| validateAndUpdateTool, |
| createActionTool, |
| encryptMetadata, |
| decryptMetadata, |
| loadActionSets, |
| domainParser, |
| }; |
|
|