n8n-nodes-base
Version:
Base nodes of n8n
382 lines • 13.6 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.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