UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

382 lines 13.6 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.optimizeResponseProperties = exports.configureResponseOptimizer = void 0; 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 n8n_workflow_1 = require("n8n-workflow"); function 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 response === 'string') { try { responseData = (0, n8n_workflow_1.jsonParse)(response, { errorMessage: 'Invalid JSON response' }); } catch (error) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Received invalid JSON from response '${response}'`, { itemIndex }); } } 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) { if (!Object.prototype.hasOwnProperty.call(responseData, dataField)) { throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Target field "${dataField}" not found in response.`, { itemIndex, description: `The response contained these fields: [${Object.keys(responseData).join(', ')}]`, }); } 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 returnData; }; }; 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 (x) => x; }; exports.configureResponseOptimizer = configureResponseOptimizer; exports.optimizeResponseProperties = [ { displayName: 'Optimize Response', name: 'optimizeResponse', type: 'boolean', default: false, noDataExpression: true, description: 'Whether the optimize the tool response to reduce amount of data passed to the LLM that could lead to better result and reduce cost', }, { displayName: 'Expected Response Type', name: 'responseType', type: 'options', displayOptions: { show: { optimizeResponse: [true], }, }, options: [ { name: 'JSON', value: 'json', }, { name: 'HTML', value: 'html', }, { name: 'Text', value: 'text', }, ], default: 'json', }, { displayName: 'Field Containing Data', name: 'dataField', type: 'string', default: '', placeholder: 'e.g. records', description: 'Specify the name of the field in the response containing the data', hint: 'leave blank to use whole response', requiresDataPath: 'single', displayOptions: { show: { optimizeResponse: [true], responseType: ['json'], }, }, }, { displayName: 'Include Fields', name: 'fieldsToInclude', type: 'options', description: 'What fields response object should include', default: 'all', displayOptions: { show: { optimizeResponse: [true], responseType: ['json'], }, }, options: [ { name: 'All', value: 'all', description: 'Include all fields', }, { name: 'Selected', value: 'selected', description: 'Include only fields specified below', }, { name: 'Except', value: 'except', description: 'Exclude fields specified below', }, ], }, { displayName: 'Fields', name: 'fields', type: 'string', default: '', placeholder: 'e.g. field1,field2', description: 'Comma-separated list of the field names. Supports dot notation. You can drag the selected fields from the input panel.', requiresDataPath: 'multiple', displayOptions: { show: { optimizeResponse: [true], responseType: ['json'], }, hide: { fieldsToInclude: ['all'], }, }, }, { displayName: 'Selector (CSS)', name: 'cssSelector', type: 'string', description: 'Select specific element(e.g. body) or multiple elements(e.g. div) of chosen type in the response HTML.', placeholder: 'e.g. body', default: 'body', displayOptions: { show: { optimizeResponse: [true], responseType: ['html'], }, }, }, { displayName: 'Return Only Content', name: 'onlyContent', type: 'boolean', default: false, description: 'Whether to return only content of html elements, stripping html tags and attributes', hint: 'Uses less tokens and may be easier for model to understand', displayOptions: { show: { optimizeResponse: [true], responseType: ['html'], }, }, }, { displayName: 'Elements To Omit', name: 'elementsToOmit', type: 'string', displayOptions: { show: { optimizeResponse: [true], responseType: ['html'], onlyContent: [true], }, }, default: '', placeholder: 'e.g. img, .className, #ItemId', description: 'Comma-separated list of selectors that would be excluded when extracting content', }, { displayName: 'Truncate Response', name: 'truncateResponse', type: 'boolean', default: false, hint: 'Helps save tokens', displayOptions: { show: { optimizeResponse: [true], responseType: ['text', 'html'], }, }, }, { displayName: 'Max Response Characters', name: 'maxLength', type: 'number', default: 1000, typeOptions: { minValue: 1, }, displayOptions: { show: { optimizeResponse: [true], responseType: ['text', 'html'], truncateResponse: [true], }, }, }, ]; //# sourceMappingURL=optimizeResponse.js.map