/** * Simulator Simulate Route — UPGRADED * Provider-agnostic dry-run simulation using LLM Gateway */ import { Router } from 'express'; import { z } from 'zod'; import { logger } from '../index'; import { DryRunSimulator } from '../agents/simulator'; import { createRequestLLM } from '../services/llmClient'; import type { N8nWorkflow, WorkflowGraph, WorkflowArchitecturePlan } from '../types/workflow'; export const simulateRoute: Router = Router(); const SimulateSchema = z.object({ jobId: z.string(), workflow: z.record(z.unknown()), graph: z.record(z.unknown()), architecturePlan: z.record(z.unknown()).optional(), mockData: z.record(z.unknown()).optional(), llmApiKey: z.string().optional(), openaiApiKey: z.string().optional(), llmModel: z.string().optional(), }); simulateRoute.post('/', async (req, res) => { const parsed = SimulateSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ success: false, error: 'Invalid request', details: parsed.error.flatten() }); return; } const { jobId, workflow, graph, architecturePlan } = parsed.data; const apiKey = parsed.data.llmApiKey ?? parsed.data.openaiApiKey ?? process.env['LLM_API_KEY'] ?? process.env['OPENAI_API_KEY'] ?? ''; logger.info({ jobId }, '[Simulate] Starting dry-run simulation'); try { const llm = createRequestLLM(apiKey); const simulator = new DryRunSimulator(llm); const simulationReport = await simulator.simulate( jobId, workflow as any as N8nWorkflow, graph as any as WorkflowGraph, (architecturePlan ?? {}) as any as WorkflowArchitecturePlan, ); logger.info({ jobId, passed: simulationReport.passed, score: simulationReport.readinessScore, }, '[Simulate] Complete'); res.json({ success: true, simulationReport }); } catch (err) { const message = err instanceof Error ? err.message : 'Simulation error'; logger.error({ jobId, err }, '[Simulate] Failed'); res.status(500).json({ success: false, error: message, jobId }); } });