File size: 7,318 Bytes
38b27cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
"""
Streamlit UI for the Code Explainer + Bug Fixer.
 
Run:
    pip install streamlit
    streamlit run streamlit_app.py
"""
 
import streamlit as st
import torch
from transformers import AutoTokenizer, T5ForConditionalGeneration
from line_explainer import explain_line_by_line
from bug_checker import detect_bugs, check_style
 
st.set_page_config(page_title="Code Explainer & Bug Fixer", page_icon="πŸ› οΈ", layout="wide")
 
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
EXPLAIN_MODEL_NAME = "Salesforce/codet5-base-multi-sum"
 
 
@st.cache_resource(show_spinner="Loading explanation model... (first run only, this takes a minute)")
def load_explain_model():
    explain_tokenizer = AutoTokenizer.from_pretrained(EXPLAIN_MODEL_NAME, use_fast=False)
    explain_model = T5ForConditionalGeneration.from_pretrained(EXPLAIN_MODEL_NAME).to(DEVICE)
    return explain_tokenizer, explain_model
 
 
explain_tokenizer, explain_model = load_explain_model()
 
 
def explain_code(code: str) -> str:
    inputs = explain_tokenizer(code, return_tensors="pt", truncation=True, max_length=512).to(DEVICE)
    output_ids = explain_model.generate(**inputs, max_length=64, num_beams=5, early_stopping=True)
    return explain_tokenizer.decode(output_ids[0], skip_special_tokens=True)
 
 
# --------------------------- Language config ---------------------------
# codet5-base-multi-sum was trained on 6 languages for the EXPLANATION step.
# line_explainer.py and bug_checker.py are Python-only (built on Python's ast
# module), so those two features are gated to Python only below.
LANGUAGE_OPTIONS = {
    "Python": {"extensions": ["py"], "st_lang": "python", "supports_static_analysis": True},
    "Java": {"extensions": ["java"], "st_lang": "java", "supports_static_analysis": False},
    "JavaScript": {"extensions": ["js"], "st_lang": "javascript", "supports_static_analysis": False},
    "PHP": {"extensions": ["php"], "st_lang": "php", "supports_static_analysis": False},
    "Ruby": {"extensions": ["rb"], "st_lang": "ruby", "supports_static_analysis": False},
    "Go": {"extensions": ["go"], "st_lang": "go", "supports_static_analysis": False},
}
 
 
# --------------------------- UI ---------------------------
 
st.title("πŸ› οΈ Code Explainer & Bug Checker")
st.caption("Paste code or upload a file. It will be explained in plain English and checked for bugs/style issues.")
 
col_lang, col_upload = st.columns([1, 2])
with col_lang:
    language = st.selectbox("Language:", list(LANGUAGE_OPTIONS.keys()))
with col_upload:
    all_extensions = [ext for cfg in LANGUAGE_OPTIONS.values() for ext in cfg["extensions"]] + ["txt"]
    uploaded_file = st.file_uploader("Or upload a code file:", type=all_extensions)
 
lang_cfg = LANGUAGE_OPTIONS[language]
 
if not lang_cfg["supports_static_analysis"]:
    st.info(
        f"Line-by-line breakdown and bug/style checking currently support Python only. "
        f"For {language}, you'll get the AI explanation only."
    )
 
default_code = """def add_numbers(a, b)
    return a + b"""
 
if uploaded_file is not None:
    file_bytes = uploaded_file.read()
    try:
        starter_code = file_bytes.decode("utf-8")
    except UnicodeDecodeError:
        st.error("Couldn't decode the uploaded file as text (UTF-8). Please upload a plain text code file.")
        starter_code = default_code
else:
    starter_code = default_code
 
code_input = st.text_area("Your code:", value=starter_code, height=250, placeholder="Paste your code here...")
 
col1, col2 = st.columns(2)
with col1:
    run_explain = st.button("πŸ” Analyze Code", type="primary", use_container_width=True)
with col2:
    clear = st.button("πŸ—‘οΈ Clear", use_container_width=True)
 
if clear:
    st.rerun()
 
if run_explain:
    if not code_input.strip():
        st.warning("Please paste some code first.")
    else:
        report_sections = []  # collects (title, text) for the downloadable report
 
        with st.spinner("Analyzing..."):
            explanation = explain_code(code_input)
 
        st.subheader("πŸ“ Explanation")
        st.info(explanation)
        report_sections.append(("Explanation", explanation))
 
        if lang_cfg["supports_static_analysis"]:
            st.subheader("πŸ“‹ Line-by-line breakdown")
            ok, breakdown = explain_line_by_line(code_input)
            if ok:
                breakdown_text = "\n".join(breakdown)
                st.code(breakdown_text, language="text")
                report_sections.append(("Line-by-line breakdown", breakdown_text))
            else:
                st.warning(breakdown)
                report_sections.append(("Line-by-line breakdown", breakdown))
 
            bug_result = detect_bugs(code_input)
            style_issues = check_style(code_input) if bug_result["syntax_valid"] else []
 
            st.subheader("πŸ› Bug Check (static analysis)")
            st.caption("Checks for real errors: syntax mistakes, undefined variables, unused imports. Based on Python's own parser + pyflakes β€” deterministic, not a guess.")
            if not bug_result["syntax_valid"]:
                st.error(bug_result["syntax_error"])
                bug_report_text = bug_result["syntax_error"]
            elif bug_result["has_bug"]:
                st.error(bug_result["summary"])
                for issue in bug_result["static_issues"]:
                    st.write(f"- {issue}")
                bug_report_text = bug_result["summary"] + "\n" + "\n".join(f"- {i}" for i in bug_result["static_issues"])
            else:
                st.success(bug_result["summary"])
                bug_report_text = bug_result["summary"]
            report_sections.append(("Bug Check", bug_report_text))
 
            st.subheader("🎨 Style Check (PEP8)")
            st.caption("Checks formatting conventions (line length, spacing, naming) β€” not correctness. Code with style warnings can still run perfectly fine; this just flags deviations from Python's standard style guide.")
            if style_issues:
                st.warning(f"{len(style_issues)} style issue(s) found.")
                for issue in style_issues:
                    st.write(f"- {issue}")
                style_report_text = "\n".join(f"- {i}" for i in style_issues)
            else:
                st.success("No PEP8 style issues found.")
                style_report_text = "No PEP8 style issues found."
            report_sections.append(("Style Check (PEP8)", style_report_text))
 
        # --------------------------- Build downloadable report ---------------------------
        report_lines = [f"# Code Analysis Report\n", f"**Language:** {language}\n", "## Original Code\n", f"```{lang_cfg['st_lang']}\n{code_input}\n```\n"]
        for title, text in report_sections:
            report_lines.append(f"## {title}\n")
            report_lines.append(f"{text}\n")
        report_text = "\n".join(report_lines)
 
        st.divider()
        st.download_button(
            label="⬇️ Download Full Report (.md)",
            data=report_text,
            file_name="code_analysis_report.md",
            mime="text/markdown",
        )
 
st.divider()
st.caption(
    "Bug detection uses real static analysis (Python's ast parser + pyflakes), "
    "so results are accurate and deterministic β€” not a model's guess."
)