PYAE1994's picture
Upload folder using huggingface_hub
dd480ef verified
/**
* Workflow Compiler Agent β€” UPGRADED
* Converts WorkflowGraph (IR) β†’ valid n8n JSON
* Schema-aware, version-aware, credential-aware
* Provider-agnostic LLM Gateway β€” NO direct OpenAI dependency
* STRICT: Only uses nodes from registry. Zero hallucinated params.
*/
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> {
// Build node schema context from registry (ONLY nodes used in graph)
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);
}
/**
* Enforce safety defaults regardless of LLM output
* CRITICAL: active = false, proper settings, error handling
*/
private ensureSafeDefaults(workflow: N8nWorkflow): N8nWorkflow {
return {
...workflow,
active: false, // NEVER activate on compile β€” ABSOLUTE safety rule
settings: {
callerPolicy: 'workflowsFromSameOwner',
errorWorkflow: workflow.settings?.errorWorkflow,
timezone: workflow.settings?.timezone ?? 'UTC',
...workflow.settings,
// Force these even if LLM overrides:
executionOrder: 'v1',
saveManualExecutions: true,
},
nodes: workflow.nodes.map((node) => ({
...node,
onError: node.onError ?? 'continueErrorOutput',
// Ensure critical external nodes have retry
retryOnFail: node.retryOnFail ?? false,
maxTries: node.maxTries ?? 3,
waitBetweenTries: node.waitBetweenTries ?? 1000,
})),
};
}
}