File size: 5,355 Bytes
7b4f5dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* ═══════════════════════════════════════════════════════════════
   Mock SSE Service β€” Replay engine for demo mode
   Replays mock_analysis.json events with realistic timing
   ═══════════════════════════════════════════════════════════════ */

const SPEED_MULTIPLIER = 1; // Set < 1 to speed up, > 1 to slow down

export class MockSSEService {
  constructor() {
    this.timeouts = [];
    this.isRunning = false;
    this.eventHandlers = {};
  }

  on(eventType, handler) {
    if (!this.eventHandlers[eventType]) {
      this.eventHandlers[eventType] = [];
    }
    this.eventHandlers[eventType].push(handler);
    return this;
  }

  emit(eventType, data) {
    const handlers = this.eventHandlers[eventType] || [];
    handlers.forEach(handler => handler(data));
  }

  async startScan(payload) {
    this.isRunning = true;
    this.timeouts = [];

    try {
      const response = await fetch('/mock_analysis.json');
      const mockData = await response.json();

      // Emit scan started
      this.emit('scan_started', {
        scanId: mockData.meta.scanId,
        filesAnalyzed: mockData.meta.filesAnalyzed,
        linesScanned: mockData.meta.linesScanned,
      });

      // ── Simulated AMD metrics (every 2 seconds) ──
      const metricsInterval = setInterval(() => {
        if (!this.isRunning) { clearInterval(metricsInterval); return; }
        this.emit('amd_metrics', {
          gpu_utilization_percent: Math.floor(78 + Math.random() * 16),
          vram_used_gb: +(44 + Math.random() * 8).toFixed(1),
          vram_total_gb: 192.0,
          temperature_c: Math.floor(58 + Math.random() * 9),
          power_draw_w: Math.floor(580 + Math.random() * 70),
          memory_bandwidth_tbs: +(4.2 + Math.random() * 0.9).toFixed(1),
          tokens_per_sec: Math.floor(1100 + Math.random() * 300),
          timestamp: new Date().toISOString(),
        });
      }, 2000);
      this.timeouts.push(metricsInterval);

      // Schedule each event with its delay
      mockData.events.forEach((event) => {
        const delay = event.delay * SPEED_MULTIPLIER;
        const timeout = setTimeout(() => {
          if (!this.isRunning) return;

          this.emit(event.type, {
            agent: event.agent,
            ...event.data,
          });

          // Also emit a generic 'event' for logging
          this.emit('event', {
            type: event.type,
            agent: event.agent,
            ...event.data,
          });
        }, delay);

        this.timeouts.push(timeout);
      });

      // ── Simulated AMD migration findings (after perf agent) ──
      const migrationDelay = (mockData.events.length > 0
        ? Math.max(...mockData.events.map(e => e.delay)) - 3000
        : 8000) * SPEED_MULTIPLIER;

      const migTimeout = setTimeout(() => {
        if (!this.isRunning) return;
        const mockMigrationFindings = [
          { id: 'AMD_M02', title: 'NVIDIA-Specific CLI Tool', description: 'nvidia-smi is NVIDIA-only and will fail on AMD hardware.', rocm_fix: "Replace nvidia-smi with rocm-smi. Example: subprocess.run(['rocm-smi', '--showmeminfo', 'vram'])", severity: 'critical', file: 'monitor.py', line: 42, code_snippet: '>>> 42 | subprocess.run(["nvidia-smi"])' },
          { id: 'AMD_M03', title: 'CUDA Device Selection Environment Variable', description: 'CUDA_VISIBLE_DEVICES is ignored on AMD/ROCm hardware.', rocm_fix: 'Replace with HIP_VISIBLE_DEVICES=0 for AMD GPU selection.', severity: 'high', file: 'config.py', line: 15, code_snippet: '>>> 15 | os.environ["CUDA_VISIBLE_DEVICES"] = "0"' },
          { id: 'AMD_M05', title: 'FP16 Precision (Suboptimal on MI300X)', description: 'FP16 works on AMD but bfloat16 is natively supported on MI300X.', rocm_fix: 'Replace .half() with .bfloat16() and torch.float16 with torch.bfloat16.', severity: 'medium', file: 'model.py', line: 28, code_snippet: '>>> 28 | model = model.half()' },
        ];
        mockMigrationFindings.forEach((f, i) => {
          setTimeout(() => {
            if (!this.isRunning) return;
            this.emit('amd_migration_finding', f);
          }, i * 300);
        });
        setTimeout(() => {
          if (!this.isRunning) return;
          this.emit('amd_migration_summary', {
            compatibility_score: 57,
            compatibility_label: 'Needs Migration Work',
            total_cuda_patterns_found: 3,
            summary: 'Found 3 CUDA-specific patterns. After applying fixes, this codebase will be fully optimized for AMD MI300X.',
          });
        }, mockMigrationFindings.length * 300 + 200);
      }, Math.max(migrationDelay, 5000));
      this.timeouts.push(migTimeout);

    } catch (error) {
      console.error('Mock SSE: Failed to load mock data', error);
      this.emit('error', { message: 'Failed to load mock analysis data' });
    }
  }

  stop() {
    this.isRunning = false;
    this.timeouts.forEach(t => clearTimeout(t));
    this.timeouts = [];
  }

  destroy() {
    this.stop();
    this.eventHandlers = {};
  }
}

export function createMockService() {
  return new MockSSEService();
}