File size: 5,281 Bytes
217e8ef
253bd94
 
 
 
 
 
 
 
 
 
217e8ef
253bd94
217e8ef
 
 
 
 
253bd94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217e8ef
 
 
 
 
 
 
 
 
 
 
 
 
253bd94
 
 
 
217e8ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253bd94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217e8ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
CodeMentor Pro - Multi-type Error Detection
Now routes Python, JavaScript, C++, and Java through REAL execution:
  - Python -> local subprocess sandbox (default; set
    config.PYTHON_EXECUTION_ENGINE=judge0 to route it through Judge0 too)
  - JavaScript/C++/Java -> Judge0 (real compile/interpret + execute)

Whenever a real engine can't be used for a given submission (Judge0 down,
rate-limited, or no engine mapped for the language) we transparently fall
back to the same LLM static-estimate prompt used before, so the app never
just fails a submission outright - it degrades honestly instead.
"""
from sandbox import execute_code
from store import record_submission, get_weakness_note
from llm import call_groq_json
from prompts import LOGICAL_ERROR_PROMPT, STATIC_ANALYSIS_PROMPT


def _run_static_estimate(code: str, language: str) -> dict:
    """LLM-based fallback when no real execution happened for this submission."""
    out = {
        "syntax": {"found": False, "message": ""},
        "runtime": {"found": False, "message": ""},
        "tle": {"found": False, "message": "", "runtime_seconds": None},
        "syntax_err": False,
        "tle_err": False,
    }
    try:
        static_result = call_groq_json(STATIC_ANALYSIS_PROMPT, f"Language: {language}\n\nCode:\n{code}")
    except Exception as e:
        out["syntax"]["message"] = f"{language} static analysis unavailable: {e}"
        return out

    if static_result.get("syntax_error_found"):
        out["syntax"] = {
            "found": True,
            "message": static_result.get(
                "syntax_error_message", "Potential syntax issue detected (unverified β€” static estimate)."
            ),
        }
        out["syntax_err"] = True
    else:
        out["syntax"]["message"] = (
            f"No obvious syntax issues found for {language} "
            "(unverified β€” static LLM estimate, not real execution)."
        )

    if static_result.get("runtime_error_found"):
        out["runtime"] = {"found": True, "message": static_result.get("runtime_error_message", "")}

    if static_result.get("tle_risk_found"):
        out["tle"] = {
            "found": True,
            "message": static_result.get(
                "tle_risk_message", "Potential performance risk (unverified β€” static estimate)."
            ),
            "runtime_seconds": None,
        }
        out["tle_err"] = True

    return out


def detect_errors(code: str, language: str) -> dict:
    result = {
        "syntax": {"found": False, "message": ""},
        "runtime": {"found": False, "message": ""},
        "tle": {"found": False, "message": "", "runtime_seconds": None},
        "logical": {"found": False, "issues": []},
        "weakness_note": "",
        "verified_by_execution": False,
    }

    syntax_err = False
    tle_err = False

    exec_result = execute_code(code, language)

    if not exec_result.engine_failure:
        # Real execution happened - Python (local) or C++/Java (Judge0).
        result["verified_by_execution"] = True
        result["tle"]["runtime_seconds"] = exec_result.runtime_seconds

        if exec_result.error_type == "syntax":
            result["syntax"] = {"found": True, "message": exec_result.error_message}
            syntax_err = True
        elif exec_result.error_type == "runtime":
            result["runtime"] = {"found": True, "message": exec_result.error_message}
        elif exec_result.error_type == "tle":
            result["tle"] = {
                "found": True,
                "message": exec_result.error_message,
                "runtime_seconds": exec_result.runtime_seconds,
            }
            tle_err = True
    else:
        # No engine available for this submission (unmapped language, or a
        # live Judge0 failure) - fall back to the LLM static estimate.
        static = _run_static_estimate(code, language)
        result["syntax"] = static["syntax"]
        result["runtime"] = static["runtime"]
        result["tle"] = static["tle"]
        syntax_err = static["syntax_err"]
        tle_err = static["tle_err"]

        if language in ("python", "javascript", "cpp", "java"):
            # This language normally HAS a real engine, so engine_failure here
            # means Judge0 failed just for this run - worth surfacing clearly
            # rather than silently looking identical to "no engine exists".
            result["syntax"]["message"] = (
                f"⚠️ Real execution unavailable this run ({exec_result.error_message}) β€” "
                f"showing an unverified static estimate instead. {result['syntax']['message']}"
            )

    logical_err = False
    if not syntax_err:
        try:
            llm_result = call_groq_json(LOGICAL_ERROR_PROMPT, f"Language: {language}\n\nCode:\n{code}")
            if llm_result.get("logical_errors_found"):
                logical_err = True
                result["logical"] = {"found": True, "issues": llm_result.get("issues", [])}
        except Exception as e:
            result["logical"] = {"found": False, "issues": [], "error": str(e)}

    record_submission(language, code, syntax_error=syntax_err, logical_error=logical_err, tle=tle_err)
    result["weakness_note"] = get_weakness_note(language)
    return result