UNPKG

@n8n/n8n-nodes-langchain

Version:

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

413 lines (409 loc) 14.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Code_node_exports = {}; __export(Code_node_exports, { Code: () => Code, transformLegacyLangchainImport: () => transformLegacyLangchainImport, vmResolver: () => vmResolver }); module.exports = __toCommonJS(Code_node_exports); var import_vm2 = require("@n8n/vm2"); var import_JavaScriptSandbox = require("n8n-nodes-base/dist/nodes/Code/JavaScriptSandbox"); var import_Sandbox = require("n8n-nodes-base/dist/nodes/Code/Sandbox"); var import_utils = require("n8n-nodes-base/dist/nodes/Code/utils"); var import_n8n_workflow = require("n8n-workflow"); var import_logWrapper = require("../../utils/logWrapper"); const { NODE_FUNCTION_ALLOW_BUILTIN: builtIn, NODE_FUNCTION_ALLOW_EXTERNAL: external } = process.env; const connectorTypes = { [import_n8n_workflow.NodeConnectionTypes.AiChain]: "Chain", [import_n8n_workflow.NodeConnectionTypes.AiDocument]: "Document", [import_n8n_workflow.NodeConnectionTypes.AiEmbedding]: "Embedding", [import_n8n_workflow.NodeConnectionTypes.AiLanguageModel]: "Language Model", [import_n8n_workflow.NodeConnectionTypes.AiMemory]: "Memory", [import_n8n_workflow.NodeConnectionTypes.AiOutputParser]: "Output Parser", [import_n8n_workflow.NodeConnectionTypes.AiTextSplitter]: "Text Splitter", [import_n8n_workflow.NodeConnectionTypes.AiTool]: "Tool", [import_n8n_workflow.NodeConnectionTypes.AiVectorStore]: "Vector Store", [import_n8n_workflow.NodeConnectionTypes.Main]: "Main" }; const defaultCodeExecute = `const { PromptTemplate } = require('@langchain/core/prompts'); const query = 'Tell me a joke'; const prompt = PromptTemplate.fromTemplate(query); // If you are allowing more than one language model input connection (-1 or // anything greater than 1), getInputConnectionData returns an array, so you // will have to change the code below it to deal with that. For example, use // llm[0] in the chain definition const llm = await this.getInputConnectionData('ai_languageModel', 0); let chain = prompt.pipe(llm); const output = await chain.invoke(); return [ {json: { output } } ]; // NOTE: Old langchain imports (e.g., 'langchain/chains') are automatically // converted to '@langchain/classic' imports for backwards compatibility.`; const defaultCodeSupplyData = `const { WikipediaQueryRun } = require( '@langchain/community/tools/wikipedia_query_run'); return new WikipediaQueryRun();`; function transformLegacyLangchainImport(moduleName) { const classicModules = [ "agents", "callbacks", "chains", "chat_models/universal", "document", "document_loaders", "document_transformers", "embeddings/cache_backed", "embeddings/fake", "evaluation", "experimental", "hub", "indexes", "load", "memory", "output_parsers", "retrievers", "schema", "smith", "sql_db", "storage", "stores", "text_splitter", "tools", "util", "vectorstores" ]; if (moduleName.startsWith("langchain/")) { const subpath = moduleName.substring("langchain/".length); for (const classicModule of classicModules) { if (subpath === classicModule || subpath.startsWith(classicModule + "/")) { return `@langchain/classic/${subpath}`; } } } return moduleName; } function transformLegacyLangchainCode(code) { let transformedCode = code.replace( /require\s*\(\s*['"]langchain\/([\w/_]+)['"]\s*\)/g, (match, subpath) => { const oldPath = `langchain/${subpath}`; const newPath = transformLegacyLangchainImport(oldPath); return newPath === oldPath ? match : `require('${newPath}')`; } ); transformedCode = transformedCode.replace( /from\s+['"]langchain\/([\w/_]+)['"]/g, (match, subpath) => { const oldPath = `langchain/${subpath}`; const newPath = transformLegacyLangchainImport(oldPath); return newPath === oldPath ? match : `from '${newPath}'`; } ); return transformedCode; } const langchainModules = ["langchain", "@langchain/*"]; const vmResolver = (0, import_vm2.makeResolverFromLegacyOptions)({ external: { modules: external ? [...langchainModules, ...external.split(",")] : [...langchainModules], transitive: false }, resolve(moduleName, parentDirname) { if (moduleName.match(/^langchain\//) ?? moduleName.match(/^@langchain\//)) { return require.resolve(`@n8n/n8n-nodes-langchain/node_modules/${moduleName}.cjs`, { paths: [parentDirname] }); } return; }, builtin: builtIn?.split(",") ?? [] }); function getSandbox(code, options) { const itemIndex = options?.itemIndex ?? 0; const node = this.getNode(); const workflowMode = this.getMode(); const transformedCode = transformLegacyLangchainCode(code); const context = import_Sandbox.getSandboxContext.call(this, itemIndex); context.addInputData = this.addInputData.bind(this); context.addOutputData = this.addOutputData.bind(this); context.getInputConnectionData = this.getInputConnectionData.bind(this); context.getInputData = this.getInputData.bind(this); context.getNode = this.getNode.bind(this); context.getExecutionCancelSignal = this.getExecutionCancelSignal.bind(this); context.getNodeOutputs = this.getNodeOutputs.bind(this); context.executeWorkflow = this.executeWorkflow.bind(this); context.getWorkflowDataProxy = this.getWorkflowDataProxy.bind(this); context.logger = this.logger; if (options?.addItems) { context.items = context.$input.all(); } const sandbox = new import_JavaScriptSandbox.JavaScriptSandbox(context, transformedCode, this.helpers, { resolver: vmResolver }); sandbox.on( "output", workflowMode === "manual" ? this.sendMessageToUI.bind(this) : (...args) => console.log(`[Workflow "${this.getWorkflow().id}"][Node "${node.name}"]`, ...args) ); return sandbox; } class Code { constructor() { this.description = { displayName: "LangChain Code", name: "code", icon: "fa:code", iconColor: "black", group: ["transform"], version: 1, description: "LangChain Code Node", defaults: { name: "LangChain Code" }, codex: { categories: ["AI"], subcategories: { AI: ["Miscellaneous"] }, resources: { primaryDocumentation: [ { url: "https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.code/" } ] } }, inputs: `={{ ((values) => { const connectorTypes = ${JSON.stringify( connectorTypes )}; return values.map(value => { return { type: value.type, required: value.required, maxConnections: value.maxConnections === -1 ? undefined : value.maxConnections, displayName: connectorTypes[value.type] !== 'Main' ? connectorTypes[value.type] : undefined } } ) })($parameter.inputs.input) }}`, outputs: `={{ ((values) => { const connectorTypes = ${JSON.stringify( connectorTypes )}; return values.map(value => { return { type: value.type, displayName: connectorTypes[value.type] !== 'Main' ? connectorTypes[value.type] : undefined } } ) })($parameter.outputs.output) }}`, properties: [ { displayName: "Code", name: "code", placeholder: "Add Code", type: "fixedCollection", noDataExpression: true, default: {}, options: [ { name: "execute", displayName: "Execute", values: [ { displayName: "JavaScript - Execute", name: "code", type: "string", typeOptions: { editor: "jsEditor" }, default: defaultCodeExecute, hint: 'This code will only run and return data if a "Main" input & output got created.', noDataExpression: true } ] }, { name: "supplyData", displayName: "Supply Data", values: [ { displayName: "JavaScript - Supply Data", name: "code", type: "string", typeOptions: { editor: "jsEditor" }, default: defaultCodeSupplyData, hint: 'This code will only run and return data if an output got created which is not "Main".', noDataExpression: true } ] } ] }, // TODO: Add links to docs which provide additional information regarding functionality { displayName: "You can import LangChain and use all available functionality. Debug by using <code>console.log()</code> statements and viewing their output in the browser console.", name: "notice", type: "notice", default: "" }, { displayName: "Inputs", name: "inputs", placeholder: "Add Input", type: "fixedCollection", noDataExpression: true, typeOptions: { multipleValues: true, sortable: true }, description: "The input to add", default: {}, options: [ { name: "input", displayName: "Input", values: [ { displayName: "Type", name: "type", type: "options", options: Object.keys(connectorTypes).map((key) => ({ name: connectorTypes[key], value: key })), noDataExpression: true, default: "", required: true, description: "The type of the input" }, { displayName: "Max Connections", name: "maxConnections", type: "number", noDataExpression: true, default: -1, required: true, description: "How many nodes of this type are allowed to be connected. Set it to -1 for unlimited." }, { displayName: "Required", name: "required", type: "boolean", noDataExpression: true, default: false, required: true, description: "Whether the input needs a connection" } ] } ] }, { displayName: "Outputs", name: "outputs", placeholder: "Add Output", type: "fixedCollection", noDataExpression: true, typeOptions: { multipleValues: true, sortable: true }, description: "The output to add", default: {}, options: [ { name: "output", displayName: "Output", values: [ { displayName: "Type", name: "type", type: "options", options: Object.keys(connectorTypes).map((key) => ({ name: connectorTypes[key], value: key })), noDataExpression: true, default: "", required: true, description: "The type of the input" } ] } ] } ] }; } async execute() { const itemIndex = 0; const code = this.getNodeParameter("code", itemIndex); if (!code.execute?.code) { throw new import_n8n_workflow.NodeOperationError( this.getNode(), `No code for "Execute" set on node "${this.getNode().name}`, { itemIndex } ); } const sandbox = getSandbox.call(this, code.execute.code, { addItems: true, itemIndex }); const outputs = this.getNodeOutputs(); const mainOutputs = outputs.filter( (output) => output.type === import_n8n_workflow.NodeConnectionTypes.Main ); const options = { multiOutput: mainOutputs.length !== 1 }; let items; try { items = await sandbox.runCodeAllItems(options); } catch (error) { if (!this.continueOnFail()) throw error; items = [{ json: { error: error.message } }]; if (options.multiOutput) { items = [items]; } } if (mainOutputs.length === 0) { throw new import_n8n_workflow.NodeOperationError( this.getNode(), 'The node does not have a "Main" output set. Please add one.', { itemIndex } ); } else if (!options.multiOutput) { for (const item of items) { (0, import_utils.standardizeOutput)(item.json); } return [items]; } else { items.forEach((data) => { for (const item of data) { (0, import_utils.standardizeOutput)(item.json); } }); return items; } } async supplyData(itemIndex) { const code = this.getNodeParameter("code", itemIndex); if (!code.supplyData?.code) { throw new import_n8n_workflow.NodeOperationError( this.getNode(), `No code for "Supply Data" set on node "${this.getNode().name}`, { itemIndex } ); } const sandbox = getSandbox.call(this, code.supplyData.code, { itemIndex }); const response = await sandbox.runCode(); return { response: (0, import_logWrapper.logWrapper)(response, this) }; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Code, transformLegacyLangchainImport, vmResolver }); //# sourceMappingURL=Code.node.js.map