@n8n/n8n-nodes-langchain
Version:

610 lines • 25.8 kB
JavaScript
;
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