UNPKG

n8n

Version:

n8n Workflow Automation Tool

183 lines 8.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TTWFGenerateCommand = void 0; const ai_workflow_builder_1 = require("@n8n/ai-workflow-builder"); const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const fs_1 = __importDefault(require("fs")); const n8n_workflow_1 = require("n8n-workflow"); const zod_1 = require("zod"); const node_types_1 = require("../../node-types"); const worker_pool_1 = require("./worker-pool"); const base_command_1 = require("../base-command"); async function waitForWorkflowGenerated(aiResponse) { let workflowJson; for await (const chunk of aiResponse) { const wfGeneratedMessage = chunk.messages.find((m) => 'type' in m && m.type === 'workflow-generated'); if (wfGeneratedMessage?.codeSnippet) { workflowJson = wfGeneratedMessage.codeSnippet; } } if (!workflowJson) { throw new n8n_workflow_1.UserError('No workflow generated message found in AI response'); } return workflowJson; } const flagsSchema = zod_1.z.object({ prompt: zod_1.z .string() .alias('p') .describe('Prompt to generate a workflow from. Mutually exclusive with --input.') .optional(), input: zod_1.z .string() .alias('i') .describe('Input dataset file name. Mutually exclusive with --prompt.') .optional(), output: zod_1.z .string() .alias('o') .describe('Output file name to save the results. Default is ttwf-results.jsonl') .default('ttwf-results.jsonl'), limit: zod_1.z .number() .int() .alias('l') .describe('Number of items from the dataset to process. Only valid with --input.') .default(-1), concurrency: zod_1.z .number() .int() .alias('c') .describe('Number of items to process in parallel. Only valid with --input.') .default(1), }); let TTWFGenerateCommand = class TTWFGenerateCommand extends base_command_1.BaseCommand { async readDataset(filePath) { try { const data = await fs_1.default.promises.readFile(filePath, { encoding: 'utf-8' }); const lines = data.split('\n').filter((line) => line.trim() !== ''); if (lines.length === 0) { throw new n8n_workflow_1.UserError('Dataset file is empty or contains no valid lines'); } return lines.map((line, index) => { try { return (0, n8n_workflow_1.jsonParse)(line); } catch (error) { throw new n8n_workflow_1.UserError(`Invalid JSON line on index: ${index}`); } }); } catch (error) { throw new n8n_workflow_1.UserError(`Failed to read dataset file: ${error}`); } } async run() { const { flags } = this; if (!flags.input && !flags.prompt) { throw new n8n_workflow_1.UserError('Either --input or --prompt must be provided.'); } if (flags.input && flags.prompt) { throw new n8n_workflow_1.UserError('You cannot use --input and --prompt together. Use one or the other.'); } const nodeTypes = di_1.Container.get(node_types_1.NodeTypes); const wfBuilder = new ai_workflow_builder_1.AiWorkflowBuilderService(nodeTypes); if (flags.prompt) { if (flags.output && fs_1.default.existsSync(flags.output)) { if (fs_1.default.lstatSync(flags.output).isDirectory()) { this.logger.info('The parameter --output must be a writeable file'); return; } this.logger.warn('The output file already exists. It will be overwritten.'); fs_1.default.unlinkSync(flags.output); } try { this.logger.info(`Processing prompt: ${flags.prompt}`); const aiResponse = wfBuilder.chat({ question: flags.prompt }); const generatedWorkflow = await waitForWorkflowGenerated(aiResponse); this.logger.info(`Generated workflow for prompt: ${flags.prompt}`); if (flags.output) { fs_1.default.writeFileSync(flags.output, generatedWorkflow); this.logger.info(`Workflow saved to ${flags.output}`); } else { this.logger.info('Generated Workflow:'); this.logger.info(JSON.stringify(JSON.parse(generatedWorkflow), null, 2)); } } catch (e) { const errorMessage = e instanceof Error ? e.message : 'An error occurred'; this.logger.error(`Error processing prompt "${flags.prompt}": ${errorMessage}`); } } else if (flags.input) { const output = flags.output ?? 'ttwf-results.jsonl'; if (fs_1.default.existsSync(output)) { if (fs_1.default.lstatSync(output).isDirectory()) { this.logger.info('The parameter --output must be a writeable file'); return; } this.logger.warn('The output file already exists. It will be overwritten.'); fs_1.default.unlinkSync(output); } const pool = new worker_pool_1.WorkerPool(flags.concurrency ?? 1); const dataset = await this.readDataset(flags.input); const outputStream = fs_1.default.createWriteStream(output, { flags: 'a' }); const datasetWithLimit = (flags.limit ?? -1) > 0 ? dataset.slice(0, flags.limit) : dataset; await Promise.allSettled(datasetWithLimit.map(async (item) => { try { const generatedWorkflow = await pool.execute(async () => { this.logger.info(`Processing prompt: ${item.prompt}`); const aiResponse = wfBuilder.chat({ question: item.prompt }); return await waitForWorkflowGenerated(aiResponse); }); this.logger.info(`Generated workflow for prompt: ${item.prompt}`); outputStream.write(JSON.stringify({ prompt: item.prompt, generatedWorkflow, referenceWorkflow: item.referenceWorkflow, }) + '\n'); } catch (e) { const errorMessage = e instanceof Error ? e.message : 'An error occurred'; this.logger.error(`Error processing prompt "${item.prompt}": ${errorMessage}`); outputStream.write(JSON.stringify({ prompt: item.prompt, referenceWorkflow: item.referenceWorkflow, errorMessage, }) + '\n'); } })); outputStream.end(); } } async catch(error) { this.logger.error('\nGOT ERROR'); this.logger.error('===================================='); this.logger.error(error.message); this.logger.error(error.stack); } }; exports.TTWFGenerateCommand = TTWFGenerateCommand; exports.TTWFGenerateCommand = TTWFGenerateCommand = __decorate([ (0, decorators_1.Command)({ name: 'ttwf:generate', description: 'Create a workflow(s) using AI Text-to-Workflow builder', examples: [ '$ n8n ttwf:generate --prompt "Create a telegram chatbot that can tell current weather in Berlin" --output result.json', '$ n8n ttwf:generate --input dataset.jsonl --output results.jsonl', ], flagsSchema, }) ], TTWFGenerateCommand); //# sourceMappingURL=generate.js.map