UNPKG

@n8n/n8n-nodes-langchain

Version:

![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png)

610 lines 25.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.configureToolFunction = exports.prepareToolDescription = exports.updateParametersAndOptions = exports.extractParametersFromText = exports.configureResponseOptimizer = exports.configureHttpRequestFunction = void 0; exports.makeToolInputSchema = makeToolInputSchema; const readability_1 = require("@mozilla/readability"); const cheerio = __importStar(require("cheerio")); const html_to_text_1 = require("html-to-text"); const jsdom_1 = require("jsdom"); const get_1 = __importDefault(require("lodash/get")); const set_1 = __importDefault(require("lodash/set")); const unset_1 = __importDefault(require("lodash/unset")); const GenericFunctions_1 = require("n8n-nodes-base/dist/nodes/HttpRequest/GenericFunctions"); const n8n_workflow_1 = require("n8n-workflow"); const zod_1 = require("zod"); const genericCredentialRequest = async (ctx, itemIndex) => { const genericType = ctx.getNodeParameter('genericAuthType', itemIndex); if (genericType === 'httpBasicAuth' || genericType === 'httpDigestAuth') { const basicAuth = await ctx.getCredentials('httpBasicAuth', itemIndex); const sendImmediately = genericType === 'httpDigestAuth' ? false : undefined; return async (options) => { options.auth = { username: basicAuth.user, password: basicAuth.password, sendImmediately, }; return await ctx.helpers.httpRequest(options); }; } if (genericType === 'httpHeaderAuth') { const headerAuth = await ctx.getCredentials('httpHeaderAuth', itemIndex); return async (options) => { if (!options.headers) options.headers = {}; options.headers[headerAuth.name] = headerAuth.value; return await ctx.helpers.httpRequest(options); }; } if (genericType === 'httpQueryAuth') { const queryAuth = await ctx.getCredentials('httpQueryAuth', itemIndex); return async (options) => { if (!options.qs) options.qs = {}; options.qs[queryAuth.name] = queryAuth.value; return await ctx.helpers.httpRequest(options); }; } if (genericType === 'httpCustomAuth') { const customAuth = await ctx.getCredentials('httpCustomAuth', itemIndex); return async (options) => { const auth = (0, n8n_workflow_1.jsonParse)(customAuth.json || '{}', { errorMessage: 'Invalid Custom Auth JSON', }); if (auth.headers) { options.headers = { ...options.headers, ...auth.headers }; } if (auth.body) { options.body = { ...options.body, ...auth.body }; } if (auth.qs) { options.qs = { ...options.qs, ...auth.qs }; } return await ctx.helpers.httpRequest(options); }; } if (genericType === 'oAuth1Api') { return async (options) => { return await ctx.helpers.requestOAuth1.call(ctx, 'oAuth1Api', options); }; } if (genericType === 'oAuth2Api') { return async (options) => { return await ctx.helpers.requestOAuth2.call(ctx, 'oAuth2Api', options, { tokenType: 'Bearer', }); }; } throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `The type ${genericType} is not supported`, { itemIndex, }); }; const predefinedCredentialRequest = async (ctx, itemIndex) => { const predefinedType = ctx.getNodeParameter('nodeCredentialType', itemIndex); const additionalOptions = (0, GenericFunctions_1.getOAuth2AdditionalParameters)(predefinedType); return async (options) => { return await ctx.helpers.httpRequestWithAuthentication.call(ctx, predefinedType, options, additionalOptions && { oauth2: additionalOptions }); }; }; const configureHttpRequestFunction = async (ctx, credentialsType, itemIndex) => { switch (credentialsType) { case 'genericCredentialType': return await genericCredentialRequest(ctx, itemIndex); case 'predefinedCredentialType': return await predefinedCredentialRequest(ctx, itemIndex); default: return async (options) => { return await ctx.helpers.httpRequest(options); }; } }; exports.configureHttpRequestFunction = configureHttpRequestFunction; const defaultOptimizer = (response) => { if (typeof response === 'string') { return response; } if (typeof response === 'object') { return JSON.stringify(response, null, 2); } return String(response); }; function isBinary(data) { if (Buffer.isBuffer(data)) { return true; } if (typeof data === 'string') { if (data.includes('\0')) { return true; } return false; } return false; } const htmlOptimizer = (ctx, itemIndex, maxLength) => { const cssSelector = ctx.getNodeParameter('cssSelector', itemIndex, ''); const onlyContent = ctx.getNodeParameter('onlyContent', itemIndex, false); let elementsToOmit = []; if (onlyContent) { const elementsToOmitUi = ctx.getNodeParameter('elementsToOmit', itemIndex, ''); if (typeof elementsToOmitUi === 'string') { elementsToOmit = elementsToOmitUi .split(',') .filter((s) => s) .map((s) => s.trim()); } } return (response) => { if (typeof response !== 'string') { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `The response type must be a string. Received: ${typeof response}`, { itemIndex }); } const returnData = []; const html = cheerio.load(response); const htmlElements = html(cssSelector); htmlElements.each((_, el) => { let value = html(el).html() || ''; if (onlyContent) { let htmlToTextOptions; if (elementsToOmit?.length) { htmlToTextOptions = { selectors: elementsToOmit.map((selector) => ({ selector, format: 'skip', })), }; } value = (0, html_to_text_1.convert)(value, htmlToTextOptions); } value = value .trim() .replace(/^\s+|\s+$/g, '') .replace(/(\r\n|\n|\r)/gm, '') .replace(/\s+/g, ' '); returnData.push(value); }); const text = JSON.stringify(returnData, null, 2); if (maxLength > 0 && text.length > maxLength) { return text.substring(0, maxLength); } return text; }; }; const textOptimizer = (ctx, itemIndex, maxLength) => { return (response) => { if (typeof response === 'object') { try { response = JSON.stringify(response, null, 2); } catch (error) { } } if (typeof response !== 'string') { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `The response type must be a string. Received: ${typeof response}`, { itemIndex }); } const dom = new jsdom_1.JSDOM(response); const article = new readability_1.Readability(dom.window.document, { keepClasses: true, }).parse(); const text = article?.textContent || ''; if (maxLength > 0 && text.length > maxLength) { return text.substring(0, maxLength); } return text; }; }; const jsonOptimizer = (ctx, itemIndex) => { return (response) => { let responseData = response; if (typeof responseData === 'string') { responseData = (0, n8n_workflow_1.jsonParse)(response); } if (typeof responseData !== 'object' || !responseData) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'The response type must be an object or an array of objects', { itemIndex }); } const dataField = ctx.getNodeParameter('dataField', itemIndex, ''); let returnData = []; if (!Array.isArray(responseData)) { if (dataField) { const data = responseData[dataField]; if (Array.isArray(data)) { responseData = data; } else { responseData = [data]; } } else { responseData = [responseData]; } } else { if (dataField) { responseData = responseData.map((data) => data[dataField]); } } const fieldsToInclude = ctx.getNodeParameter('fieldsToInclude', itemIndex, 'all'); let fields = []; if (fieldsToInclude !== 'all') { fields = ctx.getNodeParameter('fields', itemIndex, []); if (typeof fields === 'string') { fields = fields.split(',').map((field) => field.trim()); } } else { returnData = responseData; } if (fieldsToInclude === 'selected') { for (const item of responseData) { const newItem = {}; for (const field of fields) { (0, set_1.default)(newItem, field, (0, get_1.default)(item, field)); } returnData.push(newItem); } } if (fieldsToInclude === 'except') { for (const item of responseData) { for (const field of fields) { (0, unset_1.default)(item, field); } returnData.push(item); } } return JSON.stringify(returnData, null, 2); }; }; const configureResponseOptimizer = (ctx, itemIndex) => { const optimizeResponse = ctx.getNodeParameter('optimizeResponse', itemIndex, false); if (optimizeResponse) { const responseType = ctx.getNodeParameter('responseType', itemIndex); let maxLength = 0; const truncateResponse = ctx.getNodeParameter('truncateResponse', itemIndex, false); if (truncateResponse) { maxLength = ctx.getNodeParameter('maxLength', itemIndex, 0); } switch (responseType) { case 'html': return htmlOptimizer(ctx, itemIndex, maxLength); case 'text': return textOptimizer(ctx, itemIndex, maxLength); case 'json': return jsonOptimizer(ctx, itemIndex); } } return defaultOptimizer; }; exports.configureResponseOptimizer = configureResponseOptimizer; const extractPlaceholders = (text) => { const placeholder = /(\{[a-zA-Z0-9_-]+\})/g; const returnData = []; const matches = text.matchAll(placeholder); for (const match of matches) { returnData.push(match[0].replace(/{|}/g, '')); } return returnData; }; const extractParametersFromText = (placeholders, text, sendIn, key) => { if (typeof text !== 'string') return []; const parameters = extractPlaceholders(text); if (parameters.length) { const inputParameters = prepareParameters(parameters.map((name) => ({ name, valueProvider: 'modelRequired', })), placeholders, 'keypair', sendIn, ''); return key ? inputParameters.parameters.map((p) => ({ ...p, key })) : inputParameters.parameters; } return []; }; exports.extractParametersFromText = extractParametersFromText; function prepareParameters(rawParameters, placeholders, parametersInputType, sendIn, modelInputDescription, jsonWithPlaceholders) { const parameters = []; const values = {}; if (parametersInputType === 'model') { return { parameters: [ { name: sendIn, required: true, type: 'json', description: modelInputDescription, sendIn, }, ], values: {}, }; } if (parametersInputType === 'keypair') { for (const entry of rawParameters) { if (entry.valueProvider.includes('model')) { const placeholder = placeholders.find((p) => p.name === entry.name); const parameter = { name: entry.name, required: entry.valueProvider === 'modelRequired', sendIn, }; if (placeholder) { parameter.type = placeholder.type; parameter.description = placeholder.description; } parameters.push(parameter); } else if (entry.value) { parameters.push(...(0, exports.extractParametersFromText)(placeholders, entry.value, sendIn, entry.name)); values[entry.name] = entry.value; } } } if (parametersInputType === 'json' && jsonWithPlaceholders) { parameters.push(...(0, exports.extractParametersFromText)(placeholders, jsonWithPlaceholders, sendIn, `${sendIn + 'Raw'}`)); } return { parameters, values, }; } const MODEL_INPUT_DESCRIPTION = { qs: 'Query parameters for request as key value pairs', headers: 'Headers parameters for request as key value pairs', body: 'Body parameters for request as key value pairs', }; const updateParametersAndOptions = (options) => { const { ctx, itemIndex, toolParameters, placeholdersDefinitions, requestOptions, rawRequestOptions, requestOptionsProperty, inputTypePropertyName, jsonPropertyName, parametersPropertyName, } = options; const inputType = ctx.getNodeParameter(inputTypePropertyName, itemIndex, 'keypair'); let parametersValues = []; if (inputType === 'json') { rawRequestOptions[requestOptionsProperty] = ctx.getNodeParameter(jsonPropertyName, itemIndex, ''); } else { parametersValues = ctx.getNodeParameter(parametersPropertyName, itemIndex, []); } const inputParameters = prepareParameters(parametersValues, placeholdersDefinitions, inputType, requestOptionsProperty, MODEL_INPUT_DESCRIPTION[requestOptionsProperty], rawRequestOptions[requestOptionsProperty]); toolParameters.push(...inputParameters.parameters); requestOptions[requestOptionsProperty] = { ...requestOptions[requestOptionsProperty], ...inputParameters.values, }; }; exports.updateParametersAndOptions = updateParametersAndOptions; const getParametersDescription = (parameters) => parameters .map((p) => `${p.name}: (description: ${p.description ?? ''}, type: ${p.type ?? 'string'}, required: ${!!p.required})`) .join(',\n '); const prepareToolDescription = (toolDescription, toolParameters) => { let description = `${toolDescription}`; if (toolParameters.length) { description += ` Tool expects valid stringified JSON object with ${toolParameters.length} properties. Property names with description, type and required status: ${getParametersDescription(toolParameters)} ALL parameters marked as required must be provided`; } return description; }; exports.prepareToolDescription = prepareToolDescription; const configureToolFunction = (ctx, itemIndex, toolParameters, requestOptions, rawRequestOptions, httpRequest, optimizeResponse) => { return async (query) => { const { index } = ctx.addInputData(n8n_workflow_1.NodeConnectionTypes.AiTool, [[{ json: { query } }]]); const options = structuredClone(requestOptions); const clonedRawRequestOptions = structuredClone(rawRequestOptions); let fullResponse; let response = ''; let executionError = undefined; if (!toolParameters.length) { query = '{}'; } try { if (query) { let dataFromModel; if (typeof query === 'string') { try { dataFromModel = (0, n8n_workflow_1.jsonParse)(query); } catch (error) { if (toolParameters.length === 1) { dataFromModel = { [toolParameters[0].name]: query }; } else { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Input is not a valid JSON: ${error.message}`, { itemIndex }); } } } else { dataFromModel = query; } for (const parameter of toolParameters) { if (parameter.required && (dataFromModel[parameter.name] === undefined || dataFromModel[parameter.name] === null)) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Model did not provide parameter '${parameter.name}' which is required and must be present in the input`, { itemIndex }); } } for (const parameter of toolParameters) { let argument = dataFromModel[parameter.name]; if (argument && parameter.type === 'json' && !['qsRaw', 'headersRaw', 'bodyRaw'].includes(parameter.key ?? '') && typeof argument !== 'object') { try { argument = (0, n8n_workflow_1.jsonParse)(String(argument)); } catch (error) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Parameter ${parameter.name} is not a valid JSON: ${error.message}`, { itemIndex, }); } } if (parameter.sendIn === 'path') { argument = String(argument); argument = argument.replace(/^['"]+|['"]+$/g, ''); options.url = options.url.replace(`{${parameter.name}}`, argument); continue; } if (parameter.sendIn === parameter.name) { (0, set_1.default)(options, [parameter.sendIn], argument); continue; } if (['qsRaw', 'headersRaw', 'bodyRaw'].includes(parameter.key ?? '')) { if (parameter.type === 'string') { argument = String(argument); if (!argument.startsWith('"') && !clonedRawRequestOptions[parameter.sendIn].includes(`"{${parameter.name}}"`)) { argument = `"${argument}"`; } } if (typeof argument === 'object') { argument = JSON.stringify(argument); } clonedRawRequestOptions[parameter.sendIn] = clonedRawRequestOptions[parameter.sendIn].replace(`{${parameter.name}}`, String(argument)); continue; } if (parameter.key) { let requestOptionsValue = (0, get_1.default)(options, [parameter.sendIn, parameter.key]); if (typeof requestOptionsValue === 'string') { requestOptionsValue = requestOptionsValue.replace(`{${parameter.name}}`, String(argument)); } (0, set_1.default)(options, [parameter.sendIn, parameter.key], requestOptionsValue); continue; } (0, set_1.default)(options, [parameter.sendIn, parameter.name], argument); } for (const [key, value] of Object.entries(clonedRawRequestOptions)) { if (value) { let parsedValue; try { parsedValue = (0, n8n_workflow_1.jsonParse)(value, { repairJSON: true }); } catch (error) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Could not replace placeholders in ${key}: ${error.message}`); } options[key] = parsedValue; } } } if (options) { options.url = encodeURI(options.url); if (options.headers && !Object.keys(options.headers).length) { delete options.headers; } if (options.qs && !Object.keys(options.qs).length) { delete options.qs; } if (options.body && !Object.keys(options.body).length) { delete options.body; } } } catch (error) { const errorMessage = 'Input provided by model is not valid'; if (error instanceof n8n_workflow_1.NodeOperationError) { executionError = error; } else { executionError = new n8n_workflow_1.NodeOperationError(ctx.getNode(), errorMessage, { itemIndex, }); } response = errorMessage; } if (options) { try { fullResponse = await httpRequest(options); } catch (error) { const httpCode = error.httpCode; response = `${httpCode ? `HTTP ${httpCode} ` : ''}There was an error: "${error.message}"`; } if (!response) { try { if (fullResponse.body && isBinary(fullResponse.body)) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Binary data is not supported'); } response = optimizeResponse(fullResponse.body ?? fullResponse); } catch (error) { response = `There was an error: "${error.message}"`; } } } if (typeof response !== 'string') { executionError = new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Wrong output type returned', { description: `The response property should be a string, but it is an ${typeof response}`, }); response = `There was an error: "${executionError.message}"`; } if (executionError) { void ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, executionError); } else { void ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, [[{ json: { response } }]]); } return response; }; }; exports.configureToolFunction = configureToolFunction; function makeParameterZodSchema(parameter) { let schema; if (parameter.type === 'string') { schema = zod_1.z.string(); } else if (parameter.type === 'number') { schema = zod_1.z.number(); } else if (parameter.type === 'boolean') { schema = zod_1.z.boolean(); } else if (parameter.type === 'json') { schema = zod_1.z.record(zod_1.z.any()); } else { schema = zod_1.z.string(); } if (!parameter.required) { schema = schema.optional(); } if (parameter.description) { schema = schema.describe(parameter.description); } return schema; } function makeToolInputSchema(parameters) { const schemaEntries = parameters.map((parameter) => [ parameter.name, makeParameterZodSchema(parameter), ]); return zod_1.z.object(Object.fromEntries(schemaEntries)); } //# sourceMappingURL=utils.js.map