File size: 2,494 Bytes
b340140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import json
import re
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline

MODEL_MAP = {
    "Phi-3-mini": "microsoft/Phi-3-mini-4k-instruct",
    "Mistral-7B-Instruct": "mistralai/Mistral-7B-Instruct-v0.2",
}

_pipelines = {}

def load_pipeline(model_name):
    if model_name in _pipelines:
        return _pipelines[model_name]

    model_id = MODEL_MAP[model_name]
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    model = AutoModelForCausalLM.from_pretrained(
        model_id,
        device_map="cpu",
        torch_dtype="auto"
    )

    pipe = pipeline(
        "text-generation",
        model=model,
        tokenizer=tokenizer,
        max_new_tokens=600,
        temperature=0.5,
        top_p=0.9,
        do_sample=True
    )

    _pipelines[model_name] = pipe
    return pipe


def extract_json(text):
    match = re.search(r"\{[\s\S]*\}", text)
    if not match:
        return None
    try:
        return json.loads(match.group())
    except:
        return None


def grade_submission(
    model_name,
    question_paper,
    rubric,
    student_answer,
    grading_instruction
):
    pipe = load_pipeline(model_name)

    understanding_prompt = f"""
Read the student submission and extract the key ideas and steps used to answer the questions.

Student Submission:
{student_answer}

Output STRICT JSON:
{{
  "key_points": "concise summary of the student's approach and ideas"
}}
"""

    understanding_raw = pipe(understanding_prompt)[0]["generated_text"]
    understanding = extract_json(understanding_raw)
    if understanding is None:
        understanding = {"key_points": "Unable to reliably extract"}

    grading_prompt = f"""
You are an academic autograder.

Question Paper:
{question_paper}

Rubric:
{rubric}

Grading Instruction:
{grading_instruction}

Student Key Points:
{understanding["key_points"]}

Rules:
- Follow the rubric
- Award marks for logically correct alternative solutions
- Do not penalize different notation or ordering
- Grade only what is requested
- Be fair and consistent

Output STRICT JSON ONLY:
{{
  "total_marks": number,
  "per_question": {{
    "Q1": number,
    "Q2": number
  }},
  "reasoning": "short justification"
}}
"""

    grading_raw = pipe(grading_prompt)[0]["generated_text"]
    grading = extract_json(grading_raw)

    if grading is None:
        return json.dumps({
            "error": "Failed to generate valid grading output"
        }, indent=2)

    return json.dumps(grading, indent=2)