File size: 2,059 Bytes
dd480ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 * 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 });
  }
});