ziyan14 commited on
Commit
c079794
Β·
verified Β·
1 Parent(s): 04243df

Added the demo code

Browse files
Files changed (1) hide show
  1. app.py +253 -0
app.py CHANGED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Python Code Evaluator β€” SE-Group1
3
+ COS60011 Technology Design Project
4
+
5
+ Implements the 5-module architecture from the design document:
6
+ 1. UI Module β€” Gradio web interface
7
+ 2. Validation & Flow β€” Input validation and routing
8
+ 3. Pre-processing β€” 6-Element structured prompt builder
9
+ 4. Generation β€” Gemma-4 LLM via Hugging Face
10
+ 5. Output β€” Parse raw LLM output into structured results
11
+ """
12
+
13
+ import re
14
+ import torch
15
+ import gradio as gr
16
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # MODULE 4 β€” Generation Module (LLM setup)
20
+ # ---------------------------------------------------------------------------
21
+ MODEL_ID = "google/gemma-4-1b-it" # swap for "google/gemma-4-9b-it" if VRAM allows
22
+
23
+ print(f"[Generation] Loading model: {MODEL_ID}")
24
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
25
+ model = AutoModelForCausalLM.from_pretrained(
26
+ MODEL_ID,
27
+ torch_dtype=torch.bfloat16, # efficient on modern GPUs/CPUs
28
+ device_map="auto", # auto-detect GPU/CPU
29
+ )
30
+ pipe = pipeline(
31
+ "text-generation",
32
+ model=model,
33
+ tokenizer=tokenizer,
34
+ max_new_tokens=512,
35
+ do_sample=False, # deterministic output
36
+ )
37
+ print("[Generation] Model ready.")
38
+
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # MODULE 3 β€” Pre-processing Module (6-Element Framework)
42
+ # ---------------------------------------------------------------------------
43
+ def build_prompt(description: str, code: str) -> str:
44
+ """
45
+ Constructs a structured prompt using the 6-Element Framework:
46
+ 1. Role β€” Who the LLM is
47
+ 2. Context β€” Background information
48
+ 3. Input Data β€” The user's description and code
49
+ 4. Task β€” What the LLM must do
50
+ 5. Constraintsβ€” Boundaries for the response
51
+ 6. Output β€” Expected format
52
+ """
53
+ prompt = f"""<start_of_turn>user
54
+ ### ROLE
55
+ You are an expert Python code reviewer. Your sole task is to determine whether the
56
+ provided Python code correctly implements the behaviour described in the user's
57
+ requirements description.
58
+
59
+ ### CONTEXT
60
+ Developers sometimes write code that does not fully satisfy the requirements they
61
+ were given. You will analyse the semantic relationship between a natural-language
62
+ description and a Python code snippet, then produce a structured evaluation report.
63
+
64
+ ### INPUT DATA
65
+ **Requirements Description:**
66
+ {description.strip()}
67
+
68
+ **Python Code:**
69
+ ```python
70
+ {code.strip()}
71
+ ```
72
+
73
+ ### TASK
74
+ 1. Read the requirements description carefully.
75
+ 2. Analyse the Python code line by line.
76
+ 3. Determine whether the code fulfils ALL requirements stated in the description.
77
+ 4. Estimate an accuracy percentage (0–100) reflecting how completely the code
78
+ matches the description.
79
+ 5. List any specific requirements that are missing or incorrectly implemented.
80
+
81
+ ### CONSTRAINTS
82
+ - Do NOT execute the code.
83
+ - Base your evaluation solely on static code analysis and logical reasoning.
84
+ - Keep feedback concise, clear, and actionable.
85
+ - Your response MUST follow the output format exactly.
86
+
87
+ ### OUTPUT FORMAT
88
+ Respond only with the following structure β€” no extra text before or after:
89
+
90
+ RESULT: <PASS or FAIL>
91
+ ACCURACY: <integer 0-100>%
92
+ SUMMARY: <one sentence overall assessment>
93
+ ISSUES:
94
+ - <issue 1, or "None" if code fully matches the description>
95
+ - <issue 2>
96
+ ...
97
+ <end_of_turn>
98
+ <start_of_turn>model
99
+ """
100
+ return prompt
101
+
102
+
103
+ # ---------------------------------------------------------------------------
104
+ # MODULE 5 β€” Output Module
105
+ # ---------------------------------------------------------------------------
106
+ def parse_output(raw: str) -> dict:
107
+ """
108
+ Extracts structured fields from the LLM's raw text response.
109
+ Returns a dict with keys: result, accuracy, summary, issues.
110
+ Falls back gracefully if parsing fails.
111
+ """
112
+ # Strip any echoed prompt (model sometimes repeats <start_of_turn>)
113
+ if "<start_of_turn>model" in raw:
114
+ raw = raw.split("<start_of_turn>model")[-1]
115
+
116
+ result_match = re.search(r"RESULT:\s*(PASS|FAIL)", raw, re.IGNORECASE)
117
+ accuracy_match = re.search(r"ACCURACY:\s*(\d{1,3})%?", raw, re.IGNORECASE)
118
+ summary_match = re.search(r"SUMMARY:\s*(.+)", raw, re.IGNORECASE)
119
+ issues_match = re.search(r"ISSUES:\s*([\s\S]+)", raw, re.IGNORECASE)
120
+
121
+ result = result_match.group(1).upper() if result_match else "UNKNOWN"
122
+ accuracy = int(accuracy_match.group(1)) if accuracy_match else -1
123
+ summary = summary_match.group(1).strip() if summary_match else "Could not extract summary."
124
+
125
+ if issues_match:
126
+ raw_issues = issues_match.group(1).strip()
127
+ issues = [
128
+ line.lstrip("-β€’* ").strip()
129
+ for line in raw_issues.splitlines()
130
+ if line.strip() and line.strip() not in ("-", "β€’")
131
+ ]
132
+ else:
133
+ issues = ["Could not extract issues from model response."]
134
+
135
+ return {
136
+ "result": result,
137
+ "accuracy": accuracy,
138
+ "summary": summary,
139
+ "issues": issues,
140
+ "raw": raw.strip(),
141
+ }
142
+
143
+
144
+ def format_for_display(parsed: dict) -> tuple[str, str, str]:
145
+ """
146
+ Converts the parsed dict into three Gradio-friendly strings:
147
+ - verdict (shown in a highlighted Textbox)
148
+ - metrics (accuracy + summary)
149
+ - issues_text (bullet list)
150
+ """
151
+ emoji = "βœ…" if parsed["result"] == "PASS" else ("❌" if parsed["result"] == "FAIL" else "⚠️")
152
+ verdict = f"{emoji} {parsed['result']}"
153
+
154
+ acc_str = f"{parsed['accuracy']}%" if parsed["accuracy"] >= 0 else "N/A"
155
+ metrics = f"Accuracy: {acc_str}\n\nSummary: {parsed['summary']}"
156
+
157
+ issues_text = "\n".join(f"β€’ {issue}" for issue in parsed["issues"])
158
+ return verdict, metrics, issues_text
159
+
160
+
161
+ # ---------------------------------------------------------------------------
162
+ # MODULE 4 β€” Generation Module (inference call)
163
+ # ---------------------------------------------------------------------------
164
+ def generate(prompt: str) -> str:
165
+ outputs = pipe(prompt, return_full_text=False)
166
+ return outputs[0]["generated_text"]
167
+
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # MODULE 2 β€” Validation & Flow Management Module
171
+ # ---------------------------------------------------------------------------
172
+ def validate_and_evaluate(description: str, code: str):
173
+ """
174
+ Entry point called by the UI module.
175
+ Returns (verdict, metrics, issues, error_message).
176
+ """
177
+ # --- Validation ---
178
+ if not description or not description.strip():
179
+ return "", "", "", "⚠️ Please provide a requirements description."
180
+ if not code or not code.strip():
181
+ return "", "", "", "⚠️ Please provide Python code to evaluate."
182
+
183
+ # --- Pre-processing ---
184
+ prompt = build_prompt(description, code)
185
+
186
+ # --- Generation ---
187
+ try:
188
+ raw_output = generate(prompt)
189
+ except Exception as exc:
190
+ return "", "", "", f"❌ Model error: {exc}"
191
+
192
+ # --- Output ---
193
+ parsed = parse_output(raw_output)
194
+ verdict, metrics, issues = format_for_display(parsed)
195
+
196
+ return verdict, metrics, issues, "" # empty error = success
197
+
198
+
199
+ # ---------------------------------------------------------------------------
200
+ # MODULE 1 β€” UI Module (Gradio)
201
+ # ---------------------------------------------------------------------------
202
+ with gr.Blocks(
203
+ title="Python Code Evaluator β€” SE-Group1",
204
+ theme=gr.themes.Soft(primary_hue="blue"),
205
+ ) as demo:
206
+
207
+ gr.Markdown(
208
+ """
209
+ # 🐍 Python Code Evaluator
210
+ **COS60011 β€” SE-Group1** | Powered by Gemma-4 via Hugging Face
211
+
212
+ Enter a **requirements description** and your **Python code**.
213
+ The system will evaluate whether the code correctly implements the described behaviour.
214
+ """
215
+ )
216
+
217
+ with gr.Row():
218
+ with gr.Column(scale=1):
219
+ description_input = gr.Textbox(
220
+ label="πŸ“‹ Requirements Description",
221
+ placeholder="Describe what the Python code should do...",
222
+ lines=8,
223
+ )
224
+ code_input = gr.Code(
225
+ label="🐍 Python Code",
226
+ language="python",
227
+ lines=15,
228
+ value='def add(a, b):\n return a + b\n',
229
+ )
230
+ submit_btn = gr.Button("β–Ά Evaluate", variant="primary", size="lg")
231
+
232
+ with gr.Column(scale=1):
233
+ error_output = gr.Textbox(label="⚠️ Validation Error", visible=True, interactive=False)
234
+ verdict_output = gr.Textbox(label="🏁 Verdict", interactive=False, lines=1)
235
+ metrics_output = gr.Textbox(label="πŸ“Š Metrics & Summary", interactive=False, lines=4)
236
+ issues_output = gr.Textbox(label="πŸ” Issues Found", interactive=False, lines=8)
237
+
238
+ submit_btn.click(
239
+ fn=validate_and_evaluate,
240
+ inputs=[description_input, code_input],
241
+ outputs=[verdict_output, metrics_output, issues_output, error_output],
242
+ )
243
+
244
+ gr.Markdown(
245
+ """
246
+ ---
247
+ > **Note:** This tool performs *static analysis* only β€” it does not execute the code.
248
+ > Results should be treated as supplementary feedback, not a replacement for unit testing.
249
+ """
250
+ )
251
+
252
+ if __name__ == "__main__":
253
+ demo.launch(share=False)