| """ |
| 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_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}, |
| } |
| |
| |
| |
| |
| 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 = [] |
| |
| 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)) |
| |
| |
| 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." |
| ) |