from __future__ import annotations import json from html import escape import gradio as gr from graph import run_pipeline def _format_value(value) -> str: if value is None: return "null" if isinstance(value, str): return value return json.dumps(value, ensure_ascii=True) def _render_report(report_dict: dict) -> str: suites = report_dict.get("suites", []) if not suites: return '
No test cases generated.
' sections = [] for suite in suites: student_id = suite.get("student_id", "?") cases = suite.get("cases", []) copy_lines = [f"Student {student_id}"] for idx, case in enumerate(cases, start=1): category = case.get("category", "Other") desc = case.get("explanation", "").strip() input_value = _format_value(case.get("input")) copy_lines.append(f"Test Case {idx}") copy_lines.append(f"Category: {category}") if desc: copy_lines.append(f"Description: {desc}") copy_lines.append(f"Input: {input_value}") copy_lines.append("") copy_text = "\n".join(copy_lines).strip() sections.append('
') sections.append( '
' f'
Student {escape(str(student_id))}
' '' "
" ) sections.append(f'') grouped = {} for case in cases: category = case.get("category", "Other") grouped.setdefault(category, []).append(case) for category, items in grouped.items(): sections.append(f'
{escape(category)}
') sections.append('
') for idx, case in enumerate(items, start=1): desc = escape(case.get("explanation", "")) input_value = escape(_format_value(case.get("input"))) expected_value = escape(_format_value(case.get("expected"))) sections.append( '
' f'
Test Case {idx}
' f'
Description
' f'
{desc}
' f'
Input
' f'
{input_value}
' f'
Expected Output
' f'
{expected_value}
' "
" ) sections.append("
") sections.append("
") return "".join(sections) def generate_tests( problem: str, description: str, constraints: str, code: str, language: str, student_count: int, per_category: int, ): if not problem.strip(): return '{\n "error": "Problem statement is required."\n}' report = run_pipeline( problem=problem, description=description, constraints=constraints, code=code, language=language, student_count=student_count, per_category=per_category, ) report_dict = report.model_dump() html = _render_report(report_dict) return html, json.dumps(report_dict, indent=2) with gr.Blocks(title="SpecTest-LLM") as demo: gr.Markdown( """ """ ) gr.Markdown( "# SpecTest-LLM\n" "Generate explainable, multi-category test cases using a multi-agent " "LangGraph pipeline." ) with gr.Row(): problem = gr.Textbox( label="Problem Statement", placeholder="Paste the full problem statement...", lines=8, ) code = gr.Textbox( label="Source Code (optional)", placeholder="Paste code for analysis (optional)", lines=8, ) description = gr.Textbox( label="User Description", placeholder="Add any extra context or summary...", lines=4, ) constraints = gr.Textbox( label="User Constraints", placeholder="Example: 1 <= n <= 1e5, values can be negative", lines=3, ) language = gr.Dropdown( label="Language", choices=["python", "cpp", "java", "javascript", "go", "other"], value="python", ) with gr.Row(): student_count = gr.Slider( minimum=1, maximum=50, value=5, step=1, label="Number of Students", ) per_category = gr.Slider( minimum=2, maximum=3, value=2, step=1, label="Cases per Category", ) run_btn = gr.Button("Generate Test Cases") gr.Markdown("## Generated Test Cases") cases_html = gr.HTML('
') output = gr.Code(label="Generated Report (JSON)", language="json") run_btn.click( generate_tests, inputs=[ problem, description, constraints, code, language, student_count, per_category, ], outputs=[cases_html, output], ) if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, ssr_mode=False, show_error=True, )