File size: 3,314 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
"""
Performance Agent — detects N+1 queries, memory leaks,
unoptimized tensor ops, and redundant re-renders.
"""

import asyncio
import re
from typing import AsyncGenerator

PERF_PATTERNS = [
    {
        "id": "PERF-001",
        "name": "N+1 Query Pattern",
        "pattern": r'for.*(await|async).*query|forEach.*db\.|for.*execute\(',
        "severity": "high",
        "suggestion": "Use a single JOIN or batch query to eliminate N+1.",
        "description": "Database queries inside loops cause N+1 performance degradation.",
    },
    {
        "id": "PERF-002",
        "name": "Memory Leak (Missing Cleanup)",
        "pattern": r'addEventListener|setInterval|setTimeout(?!.*clearTimeout)',
        "severity": "medium",
        "suggestion": "Add cleanup functions to remove event listeners and clear timers.",
        "description": "Event listeners or timers without cleanup cause memory leaks over time.",
    },
    {
        "id": "PERF-003",
        "name": "CPU Tensor Operation (use GPU)",
        "pattern": r"\.to\(['\"]cpu['\"]\)|\.cpu\(\)|device=['\"]cpu['\"]",
        "severity": "high",
        "suggestion": "Move tensor operations to GPU with .to('cuda') and use torch.no_grad() for inference.",
        "description": "Tensor ops on CPU when GPU is available slows inference significantly.",
    },
    {
        "id": "PERF-004",
        "name": "Missing React Memoization",
        "pattern": r'const \w+ = \(\{.*\}\) =>|function \w+\(\{.*\}\)',
        "severity": "low",
        "suggestion": "Wrap expensive components with React.memo() and use useCallback/useMemo.",
        "description": "Missing memoization causes unnecessary re-renders on every parent update.",
    },
]


class PerformanceAgent:
    async def analyze(self, code: str, language: str) -> AsyncGenerator[tuple[str, dict], None]:
        lines = code.split("\n")
        found = 0

        for i, pattern_def in enumerate(PERF_PATTERNS):
            await asyncio.sleep(0.6)

            pct = int((i / len(PERF_PATTERNS)) * 100)
            yield "progress", {
                "agent": "performance",
                "percent": pct,
                "filesScanned": i + 1,
                "message": f"Checking for {pattern_def['name']}...",
            }

            for line_num, line in enumerate(lines, 1):
                if re.search(pattern_def["pattern"], line, re.IGNORECASE):
                    found += 1
                    yield "finding", {
                        "agent": "performance",
                        "id": pattern_def["id"],
                        "title": pattern_def["name"],
                        "severity": pattern_def["severity"],
                        "cwe": None,
                        "description": pattern_def["description"],
                        "file": "uploaded_code.py",
                        "line": line_num,
                        "code": line.strip(),
                        "suggestion": pattern_def["suggestion"],
                        "fixAvailable": False,
                    }
                    break

        yield "progress", {
            "agent": "performance",
            "percent": 100,
            "filesScanned": len(PERF_PATTERNS),
            "message": f"Performance analysis complete — {found} issues found",
        }