File size: 4,054 Bytes
4a62ede
 
 
9721299
 
4a62ede
 
 
aaa470c
4a62ede
 
 
 
c76704b
 
 
7577eec
c76704b
 
7577eec
9336c43
7577eec
9336c43
7577eec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c76704b
 
 
 
 
 
 
7577eec
c76704b
 
 
 
7577eec
4a62ede
 
3b83d24
4a62ede
 
 
 
 
 
c76704b
4a62ede
 
 
 
 
6db22a1
3cf8485
 
 
 
 
 
6db22a1
 
 
 
 
 
 
 
3b83d24
 
6db22a1
3cf8485
6db22a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b83d24
 
 
 
 
6db22a1
3b83d24
 
3cf8485
3b83d24
4a62ede
6db22a1
 
 
 
ea4b12c
6db22a1
 
 
 
 
4a62ede
3b83d24
4a62ede
 
3b83d24
6db22a1
3b83d24
4a62ede
 
 
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import os
import json
import gradio as gr
from mistralai.client import Mistral


api_key = os.environ["MISTRAL_API_KEY"]
client = Mistral(api_key=api_key)
model_name = "mistral-large-latest"


def generate_MCQquestion(text):
    prompt = f"""
    You are a strict Arabic exam generator.
    You MUST use ONLY the information explicitly written in the Context.
    DO NOT use any external knowledge.

    Context:
    {text}

    Generate MCQs in Arabic based on the length of the Context.

    Question count rules:
    - Very short context (< 100 words): 3 questions.
    - Short context (100–300 words): 3–5 questions.
    - Medium context (300–700 words): 5–7 questions.
    - Long context (> 700 words): 7–10 questions.

    STRICT RULES FOR OPTIONS & ANSWERS:
    1. **Randomize Answer Position:** The correct answer index (0-3) MUST be distributed randomly across the questions. Do NOT always place the correct answer at index 0.
    2. **Length Consistency:** All 4 options for a single question should have roughly the same length. Do NOT make the correct answer significantly longer or more detailed than the distractors.
    3. **Plausible Distractors:** Incorrect options must be highly plausible and derived from the context's vocabulary to challenge the student.
    4. **Source Integrity:** Every correct answer and its "answer_source" MUST exist explicitly in the text.

    STRICT QUALITY RULES:
    - Quality over quantity.
    - No outside knowledge or general info.
    - If the context doesn't support the required number of questions, stop at what is factual.

    Return ONLY valid JSON:
    {{
      "mcqs": [
        {{
          "question": "string",
          "options": ["opt1", "opt2", "opt3", "opt4"],
          "answer_index": integer (0 to 3),
          "answer_source": "exact sentence from context"
        }}
      ]
    }}
    """

    try:

        response = client.chat.complete(
            model=model_name,
            messages=[
                {"role": "user", "content": prompt}
            ],
            response_format={"type": "json_object"},
            temperature=0.2
        )

        content = response.choices[0].message.content
        data = json.loads(content)

        # handle both dict and list formats
        if isinstance(data, dict):
            mcqs = data.get("mcqs", [])
        elif isinstance(data, list):
            mcqs = data
        else:
            return json.dumps({"error": "Invalid response format"}, ensure_ascii=False)

        # enforce limits
        if len(mcqs) < 3:
            return json.dumps({"error": "Model generated fewer than 3 questions"}, ensure_ascii=False)

        if len(mcqs) > 10:
            mcqs = mcqs[:10]

        questions = []
        answers = []

        for i, item in enumerate(mcqs, start=1):

            # safety checks
            if (
                "question" not in item or
                "options" not in item or
                "answer_index" not in item
            ):
                continue

            if not isinstance(item["options"], list) or len(item["options"]) != 4:
                continue

            if item["answer_index"] < 0 or item["answer_index"] > 3:
                continue

            questions.append({
                "id": i,
                "question": item["question"],
                "options": item["options"]
            })

            answers.append({
                "question_id": i,
                "correct_answer_index": item["answer_index"] + 1
            })

        final_output = {
            "questions": questions,
            "answers": answers
        }

        return json.dumps(final_output, ensure_ascii=False, indent=2)

    except Exception as e:
        return json.dumps({"error": str(e)}, ensure_ascii=False, indent=2)


# UI
demo = gr.Interface(
    fn=generate_MCQquestion,
    inputs=gr.Textbox(lines=10, label="أدخل النص"),
    outputs=gr.Code(language="json", label="JSON Output"),
    title="Arabic MCQ Generator API Ready",
)

demo.launch()