@n8n/n8n-nodes-langchain
Version:

276 lines • 11.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ToolCode = void 0;
const tools_1 = require("@langchain/core/tools");
const JsTaskRunnerSandbox_1 = require("n8n-nodes-base/dist/nodes/Code/JsTaskRunnerSandbox");
const PythonTaskRunnerSandbox_1 = require("n8n-nodes-base/dist/nodes/Code/PythonTaskRunnerSandbox");
const n8n_workflow_1 = require("n8n-workflow");
const descriptions_1 = require("../../../utils/descriptions");
const schemaParsing_1 = require("../../../utils/schemaParsing");
const ai_utilities_1 = require("@n8n/ai-utilities");
const jsonSchemaExampleField = (0, descriptions_1.buildJsonSchemaExampleField)({
showExtraProps: { specifyInputSchema: [true] },
});
const jsonSchemaExampleNotice = (0, descriptions_1.buildJsonSchemaExampleNotice)({
showExtraProps: {
specifyInputSchema: [true],
'@version': [{ _cnd: { gte: 1.3 } }],
},
});
const jsonSchemaField = (0, descriptions_1.buildInputSchemaField)({ showExtraProps: { specifyInputSchema: [true] } });
function getTool(ctx, itemIndex, log = true) {
const node = ctx.getNode();
const workflowMode = ctx.getMode();
const { typeVersion } = node;
const name = typeVersion <= 1.1
? ctx.getNodeParameter('name', itemIndex)
: (0, n8n_workflow_1.nodeNameToToolName)(node);
const description = ctx.getNodeParameter('description', itemIndex);
const useSchema = ctx.getNodeParameter('specifyInputSchema', itemIndex);
const language = ctx.getNodeParameter('language', itemIndex);
let code = '';
if (language === 'javaScript') {
code = ctx.getNodeParameter('jsCode', itemIndex);
}
else {
code = ctx.getNodeParameter('pythonCode', itemIndex);
}
const runFunction = async (query) => {
if (language === 'javaScript') {
const sandbox = new JsTaskRunnerSandbox_1.JsTaskRunnerSandbox(workflowMode, ctx, undefined, {
query,
});
return await sandbox.runCodeForTool(code);
}
else {
const sandbox = new PythonTaskRunnerSandbox_1.PythonTaskRunnerSandbox(code, 'runOnceForAllItems', workflowMode, ctx, {
query,
});
return await sandbox.runCodeForTool();
}
};
const toolHandler = async (query) => {
const { index } = log
? ctx.addInputData(n8n_workflow_1.NodeConnectionTypes.AiTool, [[{ json: { query } }]])
: { index: 0 };
let response = '';
let executionError;
try {
response = await runFunction(query);
}
catch (error) {
executionError = new n8n_workflow_1.NodeOperationError(ctx.getNode(), error);
response = `There was an error: "${executionError.message}"`;
}
if (typeof response === 'number') {
response = response.toString();
}
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 && log) {
void ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, executionError);
}
else if (log) {
void ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, index, [[{ json: { response } }]]);
}
return response;
};
const commonToolOptions = {
name,
description,
func: toolHandler,
};
let tool = undefined;
if (useSchema) {
try {
const jsonExample = ctx.getNodeParameter('jsonSchemaExample', itemIndex, '');
const inputSchema = ctx.getNodeParameter('inputSchema', itemIndex, '');
const schemaType = ctx.getNodeParameter('schemaType', itemIndex);
const jsonSchema = schemaType === 'fromJson'
? (0, schemaParsing_1.generateSchemaFromExample)(jsonExample, ctx.getNode().typeVersion >= 1.3)
: (0, n8n_workflow_1.jsonParse)(inputSchema);
const zodSchema = (0, schemaParsing_1.convertJsonSchemaToZod)(jsonSchema);
tool = new tools_1.DynamicStructuredTool({
schema: zodSchema,
...commonToolOptions,
});
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Error during parsing of JSON Schema. \n ' + error);
}
}
else {
tool = new tools_1.DynamicTool(commonToolOptions);
}
return tool;
}
class ToolCode {
constructor() {
this.description = {
displayName: 'Code Tool',
name: 'toolCode',
icon: 'fa:code',
iconColor: 'black',
group: ['transform'],
version: [1, 1.1, 1.2, 1.3],
description: 'Write a tool in JS or Python',
defaults: {
name: 'Code Tool',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Tools'],
Tools: ['Recommended Tools'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolcode/',
},
],
},
},
inputs: [],
outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
outputNames: ['Tool'],
properties: [
(0, ai_utilities_1.getConnectionHintNoticeField)([n8n_workflow_1.NodeConnectionTypes.AiAgent]),
{
displayName: 'See an example of a conversational agent with custom tool written in JavaScript <a href="/templates/1963" target="_blank">here</a>.',
name: 'noticeTemplateExample',
type: 'notice',
default: '',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
placeholder: 'My_Tool',
displayOptions: {
show: {
'@version': [1],
},
},
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
placeholder: 'e.g. My_Tool',
validateType: 'string-alphanumeric',
description: 'The name of the function to be called, could contain letters, numbers, and underscores only',
displayOptions: {
show: {
'@version': [1.1],
},
},
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
placeholder: 'Call this tool to get a random color. The input should be a string with comma separted names of colors to exclude.',
typeOptions: {
rows: 3,
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
noDataExpression: true,
options: [
{
name: 'JavaScript',
value: 'javaScript',
},
{
name: 'Python (Beta)',
value: 'python',
},
],
default: 'javaScript',
},
{
displayName: 'JavaScript',
name: 'jsCode',
type: 'string',
displayOptions: {
show: {
language: ['javaScript'],
},
},
typeOptions: {
editor: 'jsEditor',
},
default: '// Example: convert the incoming query to uppercase and return it\nreturn query.toUpperCase()',
hint: 'You can access the input the tool receives via the input property "query". The returned value should be a single string.',
description: 'E.g. Converts any text to uppercase',
noDataExpression: true,
},
{
displayName: 'Python',
name: 'pythonCode',
type: 'string',
displayOptions: {
show: {
language: ['python'],
},
},
typeOptions: {
editor: 'codeNodeEditor',
editorLanguage: 'python',
},
default: '# Example: convert the incoming query to uppercase and return it\nreturn _query.upper()',
hint: 'You can access the input the tool receives via the input property "_query". The returned value should be a single string.',
description: 'E.g. Converts any text to uppercase',
noDataExpression: true,
},
{
displayName: 'Specify Input Schema',
name: 'specifyInputSchema',
type: 'boolean',
description: 'Whether to specify the schema for the function. This would require the LLM to provide the input in the correct format and would validate it against the schema.',
noDataExpression: true,
default: false,
},
{ ...descriptions_1.schemaTypeField, displayOptions: { show: { specifyInputSchema: [true] } } },
jsonSchemaExampleField,
jsonSchemaExampleNotice,
jsonSchemaField,
],
};
}
async supplyData(itemIndex) {
return {
response: getTool(this, itemIndex),
};
}
async execute() {
const result = [];
const input = this.getInputData();
for (let i = 0; i < input.length; i++) {
const item = input[i];
const tool = getTool(this, i, false);
result.push({
json: {
response: await tool.invoke(item.json),
},
pairedItem: {
item: i,
},
});
}
return [result];
}
}
exports.ToolCode = ToolCode;
//# sourceMappingURL=ToolCode.node.js.map