Spaces:
Sleeping
Sleeping
markdown
Browse files
app.py
CHANGED
|
@@ -1,34 +1,39 @@
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
| 3 |
import google.generativeai as genai
|
| 4 |
-
from
|
| 5 |
-
from reportlab.lib.styles import getSampleStyleSheet
|
| 6 |
-
from reportlab.lib.pagesizes import A4
|
| 7 |
|
| 8 |
# -------------------- CONFIG --------------------
|
| 9 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 10 |
|
| 11 |
# ---------- PROMPTS ----------
|
| 12 |
-
TRANSCRIPTION_PROMPT = """
|
| 13 |
Primary Objective: Preserve the author's intended solution path while filtering out all mistakes, corrections, and extraneous marks. The final output must be perfectly formatted and easy to follow.
|
|
|
|
| 14 |
Core Instructions:
|
| 15 |
Hierarchical Structure:
|
| 16 |
-
Identify all questions and subquestions based on their numbering (e.g., 1. a),
|
| 17 |
-
Use ## for main questions (e.g., ## Question 1).
|
| 18 |
-
Use ### for subquestions (e.g., ### a), ### i)).
|
| 19 |
-
If a question number appears out of its logical sequence, transcribe it with the label provided in the source.
|
|
|
|
| 20 |
What to Exclude (Content Filtration):
|
| 21 |
-
Mistakes: Completely ignore and do not transcribe any number, variable, or expression that has been struck through, scribbled over, or crossed out. Transcribe only the corrected, final version.
|
| 22 |
-
Extraneous Marks: Do not include any doodles, underlines (unless part of a fraction), or stray marks not relevant to the solution.
|
|
|
|
| 23 |
Crucial Distinction: Cancellations vs. Step Cuts:
|
| 24 |
-
Term Cancellation: This is a valid mathematical step where terms cancel each other out (e.g., +2x and -2x, or a term divided by itself).
|
| 25 |
-
Action: Transcribe the step where the cancellation occurs. Immediately after that line, add a concise, bracketed note explaining what was cancelled.
|
| 26 |
-
Step Cut: This is when the author skips intermediate algebraic or arithmetic steps (e.g., jumping from 2b = 2 directly to b = 1).
|
| 27 |
-
Action: Transcribe the steps exactly as they appear. Do not invent or add the missing steps. The logical jump in the transcribed output serves to represent the step cut.
|
|
|
|
| 28 |
Formatting and Special Cases:
|
| 29 |
-
Equations: Enclose all mathematical equations and multi-line calculations in Markdown code blocks for clarity and proper rendering.
|
| 30 |
-
Illegibility: If a specific word or number is impossible to read, use the placeholder [illegible].
|
| 31 |
-
Graphs: Do not attempt to recreate graphs. Instead, describe them textually. Note the type of curve (e.g., parabola, polynomial) and list any labeled key points like intercepts, vertices, or asymptotes.
|
|
|
|
|
|
|
|
|
|
| 32 |
GRADING_PROMPT = """Instructions to Examiners
|
| 33 |
|
| 34 |
Abbreviations:
|
|
@@ -108,16 +113,13 @@ Implied marks appear in brackets, e.g. (M1), and can only be awarded if correct
|
|
| 108 |
- More than one solution: mark only the first response unless candidate specifies otherwise.
|
| 109 |
"""
|
| 110 |
|
| 111 |
-
|
| 112 |
-
# ---------- HELPER: Save to PDF ----------
|
| 113 |
def save_as_pdf(text, filename="output.pdf"):
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
doc.build(story)
|
| 118 |
return filename
|
| 119 |
|
| 120 |
-
|
| 121 |
# ---------- STEP 1: TRANSCRIPTION ----------
|
| 122 |
def transcribe(ans_file):
|
| 123 |
try:
|
|
@@ -135,7 +137,6 @@ def transcribe(ans_file):
|
|
| 135 |
except Exception as e:
|
| 136 |
return f"❌ Error during transcription: {e}", None
|
| 137 |
|
| 138 |
-
|
| 139 |
# ---------- STEP 2: GRADING ----------
|
| 140 |
def grade(qp_file, ms_file, transcription):
|
| 141 |
try:
|
|
@@ -143,18 +144,39 @@ def grade(qp_file, ms_file, transcription):
|
|
| 143 |
ms_uploaded = genai.upload_file(path=ms_file, display_name="Marking Scheme")
|
| 144 |
model = genai.GenerativeModel("gemini-2.5-pro", generation_config={"temperature": 0})
|
| 145 |
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
pdf_path = save_as_pdf(grading, "grading.pdf")
|
| 153 |
return grading, pdf_path
|
| 154 |
except Exception as e:
|
| 155 |
return f"❌ Error during grading: {e}", None
|
| 156 |
|
| 157 |
-
|
| 158 |
# ---------- GRADIO APP ----------
|
| 159 |
with gr.Blocks(title="LeadIB AI Grading") as demo:
|
| 160 |
gr.Markdown("## LeadIB AI Grading\nUpload exam documents to transcribe and grade student answers step by step.")
|
|
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
| 3 |
import google.generativeai as genai
|
| 4 |
+
from markdown_pdf import MarkdownPdf, Section
|
|
|
|
|
|
|
| 5 |
|
| 6 |
# -------------------- CONFIG --------------------
|
| 7 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 8 |
|
| 9 |
# ---------- PROMPTS ----------
|
| 10 |
+
TRANSCRIPTION_PROMPT = """Your Role: You are an expert technical transcriber specializing in mathematical and scientific documents. Your mission is to convert handwritten solutions from a provided image or PDF into a clean, accurate, and logically structured Markdown format.
|
| 11 |
Primary Objective: Preserve the author's intended solution path while filtering out all mistakes, corrections, and extraneous marks. The final output must be perfectly formatted and easy to follow.
|
| 12 |
+
|
| 13 |
Core Instructions:
|
| 14 |
Hierarchical Structure:
|
| 15 |
+
- Identify all questions and subquestions based on their numbering (e.g., 1. a), i)).
|
| 16 |
+
- Use ## for main questions (e.g., ## Question 1).
|
| 17 |
+
- Use ### for subquestions (e.g., ### a), ### i)).
|
| 18 |
+
- If a question number appears out of its logical sequence, transcribe it with the label provided in the source.
|
| 19 |
+
|
| 20 |
What to Exclude (Content Filtration):
|
| 21 |
+
- Mistakes: Completely ignore and do not transcribe any number, variable, or expression that has been struck through, scribbled over, or crossed out. Transcribe only the corrected, final version.
|
| 22 |
+
- Extraneous Marks: Do not include any doodles, underlines (unless part of a fraction), or stray marks not relevant to the solution.
|
| 23 |
+
|
| 24 |
Crucial Distinction: Cancellations vs. Step Cuts:
|
| 25 |
+
- Term Cancellation: This is a valid mathematical step where terms cancel each other out (e.g., +2x and -2x, or a term divided by itself).
|
| 26 |
+
Action: Transcribe the step where the cancellation occurs. Immediately after that line, add a concise, bracketed note explaining what was cancelled.
|
| 27 |
+
- Step Cut: This is when the author skips intermediate algebraic or arithmetic steps (e.g., jumping from 2b = 2 directly to b = 1).
|
| 28 |
+
Action: Transcribe the steps exactly as they appear. Do not invent or add the missing steps. The logical jump in the transcribed output serves to represent the step cut.
|
| 29 |
+
|
| 30 |
Formatting and Special Cases:
|
| 31 |
+
- Equations: Enclose all mathematical equations and multi-line calculations in Markdown code blocks for clarity and proper rendering.
|
| 32 |
+
- Illegibility: If a specific word or number is impossible to read, use the placeholder [illegible].
|
| 33 |
+
- Graphs: Do not attempt to recreate graphs. Instead, describe them textually. Note the type of curve (e.g., parabola, polynomial) and list any labeled key points like intercepts, vertices, or asymptotes.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
# Full 9-rule grading prompt (point 9 is Presentation; "Calculators" section removed)
|
| 37 |
GRADING_PROMPT = """Instructions to Examiners
|
| 38 |
|
| 39 |
Abbreviations:
|
|
|
|
| 113 |
- More than one solution: mark only the first response unless candidate specifies otherwise.
|
| 114 |
"""
|
| 115 |
|
| 116 |
+
# ---------- HELPER: Save to PDF using markdown-pdf ----------
|
|
|
|
| 117 |
def save_as_pdf(text, filename="output.pdf"):
|
| 118 |
+
pdf = MarkdownPdf()
|
| 119 |
+
pdf.add_section(Section(text, toc=False))
|
| 120 |
+
pdf.save(filename)
|
|
|
|
| 121 |
return filename
|
| 122 |
|
|
|
|
| 123 |
# ---------- STEP 1: TRANSCRIPTION ----------
|
| 124 |
def transcribe(ans_file):
|
| 125 |
try:
|
|
|
|
| 137 |
except Exception as e:
|
| 138 |
return f"❌ Error during transcription: {e}", None
|
| 139 |
|
|
|
|
| 140 |
# ---------- STEP 2: GRADING ----------
|
| 141 |
def grade(qp_file, ms_file, transcription):
|
| 142 |
try:
|
|
|
|
| 144 |
ms_uploaded = genai.upload_file(path=ms_file, display_name="Marking Scheme")
|
| 145 |
model = genai.GenerativeModel("gemini-2.5-pro", generation_config={"temperature": 0})
|
| 146 |
|
| 147 |
+
# Prompt that embeds the full 9 rules and enforces a structured grading table
|
| 148 |
+
structured_instructions = (
|
| 149 |
+
"You are an official examiner. Use the following grading rules strictly:\n\n"
|
| 150 |
+
f"{GRADING_PROMPT}\n\n"
|
| 151 |
+
"OUTPUT FORMAT (use GitHub-flavored Markdown table):\n\n"
|
| 152 |
+
"| Student wrote | Marks Awarded | Reason (reference the rules; specify error type: A : All Good , B : Silly Mistake , C : Conceptual Error , D : Hard question , E : Not Applicable) |\n"
|
| 153 |
+
"|---|---|---|\n"
|
| 154 |
+
"Then, after the table, provide a short 'Summary & Final Mark' section with totals and any FT usage noted.\n\n"
|
| 155 |
+
"Guidelines:\n"
|
| 156 |
+
"1) Apply marks exactly as per the markscheme.\n"
|
| 157 |
+
"2) Justify each awarded or withheld mark with explicit references to the numbered rules.\n"
|
| 158 |
+
"3) Classify all errors (Conceptual Error, Silly Mistake, Misread, or None).\n"
|
| 159 |
+
"4) Enforce dependency between M and A marks (no A awarded if M not earned) and indicate FT when applied.\n"
|
| 160 |
+
"5) Do not invent marks that are not present in the markscheme.\n"
|
| 161 |
+
"6) Provide step-by-step reasoning for each mark awarded or withheld.\n"
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
response = model.generate_content([
|
| 165 |
+
structured_instructions,
|
| 166 |
+
qp_uploaded, # uploaded question paper
|
| 167 |
+
ms_uploaded, # uploaded marking scheme
|
| 168 |
+
transcription # student's transcription
|
| 169 |
+
])
|
| 170 |
+
|
| 171 |
+
grading = getattr(response, "text", None)
|
| 172 |
+
if not grading and response.candidates:
|
| 173 |
+
grading = response.candidates[0].content.parts[0].text
|
| 174 |
|
| 175 |
pdf_path = save_as_pdf(grading, "grading.pdf")
|
| 176 |
return grading, pdf_path
|
| 177 |
except Exception as e:
|
| 178 |
return f"❌ Error during grading: {e}", None
|
| 179 |
|
|
|
|
| 180 |
# ---------- GRADIO APP ----------
|
| 181 |
with gr.Blocks(title="LeadIB AI Grading") as demo:
|
| 182 |
gr.Markdown("## LeadIB AI Grading\nUpload exam documents to transcribe and grade student answers step by step.")
|