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."
) |