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,
)