| |
| |
| |
| |
| |
| |
| |
| import { type LLMGateway } from '@wfo/integrations/llm-providers/index'; |
| import type { WorkflowGraph, WorkflowIntent, N8nWorkflow } from '../types/workflow'; |
| import { COMPILER_PROMPT } from '../prompts/compiler'; |
| import { N8N_NODE_REGISTRY, isValidNodeType } from '../knowledge/nodeRegistry'; |
|
|
| export class WorkflowCompiler { |
| private llm: LLMGateway; |
|
|
| constructor(llm: LLMGateway) { |
| this.llm = llm; |
| } |
|
|
| async compile(graph: WorkflowGraph, intent: WorkflowIntent): Promise<N8nWorkflow> { |
| |
| const nodeTypes = [...new Set(graph.nodes.map((n) => n.n8nNodeType))]; |
| const validNodeTypes = nodeTypes.filter(isValidNodeType); |
| const nodeSchemas = validNodeTypes |
| .map((type) => N8N_NODE_REGISTRY[type]) |
| .filter(Boolean) |
| .map((s) => JSON.stringify(s, null, 2)) |
| .join('\n\n'); |
|
|
| const workflow = await this.llm.completeJSON<N8nWorkflow>([ |
| { role: 'system', content: COMPILER_PROMPT }, |
| { |
| role: 'user', |
| content: `Compile this WorkflowGraph to a valid n8n JSON workflow. |
| |
| GRAPH: |
| ${JSON.stringify(graph, null, 2)} |
| |
| INTENT: |
| ${JSON.stringify(intent, null, 2)} |
| |
| REGISTERED NODE SCHEMAS (use ONLY these β parameters MUST match schema): |
| ${nodeSchemas || '(No pre-built schemas β use standard n8n defaults)'} |
| |
| CRITICAL COMPILATION RULES: |
| 1. active MUST be false β NEVER true |
| 2. All node IDs must be unique UUID strings (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) |
| 3. connections MUST wire ALL graph edges correctly |
| 4. NEVER invent parameters not in the node schema above |
| 5. Every node MUST have real expressions for dynamic fields β NO static placeholder text |
| 6. Add retryOnFail: true, maxTries: 3, waitBetweenTries: 1000 on all critical/external nodes |
| 7. Add meaningful node notes explaining what each node does |
| 8. USE these expression patterns: |
| - Input from trigger: {{$json?.body?.fieldName ?? $json?.fieldName ?? ""}} |
| - Previous node: {{$node["NodeName"].json?.field ?? ""}} |
| - Array item: {{$json?.items?.[0]?.value ?? ""}} |
| - Conditional: {{$json?.status === "active" ? "yes" : "no"}} |
| 9. SET nodes MUST have explicit field mappings β NOT empty values array |
| 10. IF nodes MUST have real boolean conditions referencing actual fields |
| 11. CODE nodes MUST contain real JavaScript logic β NOT placeholder comments |
| 12. AI Agent nodes MUST include system message AND dynamic user message from previous node data |
| 13. Return ONLY complete, importable n8n JSON workflow`, |
| }, |
| ], { |
| temperature: 0.0, |
| retries: 3, |
| }); |
|
|
| return this.ensureSafeDefaults(workflow); |
| } |
|
|
| |
| |
| |
| |
| private ensureSafeDefaults(workflow: N8nWorkflow): N8nWorkflow { |
| return { |
| ...workflow, |
| active: false, |
| settings: { |
| callerPolicy: 'workflowsFromSameOwner', |
| errorWorkflow: workflow.settings?.errorWorkflow, |
| timezone: workflow.settings?.timezone ?? 'UTC', |
| ...workflow.settings, |
| |
| executionOrder: 'v1', |
| saveManualExecutions: true, |
| }, |
| nodes: workflow.nodes.map((node) => ({ |
| ...node, |
| onError: node.onError ?? 'continueErrorOutput', |
| |
| retryOnFail: node.retryOnFail ?? false, |
| maxTries: node.maxTries ?? 3, |
| waitBetweenTries: node.waitBetweenTries ?? 1000, |
| })), |
| }; |
| } |
| } |
|
|