capradeepgujaran commited on
Commit
3576103
·
verified ·
1 Parent(s): 41fc389

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -63
app.py CHANGED
@@ -24,131 +24,158 @@ class QuizFeedback:
24
  class QuizGenerator:
25
  def __init__(self, api_key: str):
26
  self.client = Groq(api_key=api_key)
27
-
28
  def generate_questions(self, text: str, num_questions: int) -> List[Question]:
 
29
  prompt = self._create_prompt(text, num_questions)
30
 
31
  try:
 
32
  response = self.client.chat.completions.create(
33
  messages=[
34
  {
35
  "role": "system",
36
- "content": """You are a quiz generator. Create clear questions with concise answer options.
37
- Always return valid JSON array of questions. Format answers as short, clear phrases."""
 
 
 
38
  },
39
  {
40
  "role": "user",
41
  "content": prompt
42
  }
43
  ],
44
- model="llama-3.2-3b-preview",
45
- temperature=0,
46
- max_tokens=2048
 
47
  )
48
 
 
 
 
 
49
  questions = self._parse_response(response.choices[0].message.content)
50
- return self._validate_questions(questions, num_questions)
 
 
 
 
 
51
 
52
  except Exception as e:
53
  print(f"Error in generate_questions: {str(e)}")
54
- print(f"Response content: {response.choices[0].message.content if response else 'No response'}")
 
 
 
 
55
  raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
56
 
57
  def _create_prompt(self, text: str, num_questions: int) -> str:
58
- return f"""Generate exactly {num_questions} multiple choice questions based on the following text. Follow these rules strictly:
 
 
 
 
 
 
 
 
59
 
60
- 1. Each question must be clear and focused
61
- 2. Provide exactly 4 options for each question
62
- 3. Mark the correct answer with index (0-3)
63
- 4. Keep all options concise (under 10 words)
64
- 5. Return ONLY a JSON array with this exact format:
65
  [
66
  {{
67
- "question": "Clear question text here?",
68
  "options": [
69
- "Brief option 1",
70
- "Brief option 2",
71
- "Brief option 3",
72
- "Brief option 4"
73
  ],
74
  "correct_answer": 0
75
  }}
76
  ]
77
 
 
 
 
78
  Text to generate questions from:
79
- {text}
80
 
81
- Important: Return only the JSON array, no other text or formatting."""
82
-
83
  def _parse_response(self, response_text: str) -> List[Dict]:
84
- """Parse response with improved error handling"""
85
  try:
86
  # Clean up the response text
87
- cleaned_text = response_text.strip()
88
- cleaned_text = cleaned_text.replace('```json', '').replace('```', '').strip()
 
89
 
90
  # Find the JSON array
91
- start_idx = cleaned_text.find('[')
92
- end_idx = cleaned_text.rfind(']')
93
-
94
- if start_idx == -1 or end_idx == -1:
95
- raise ValueError("No valid JSON array found in response")
96
 
97
- json_text = cleaned_text[start_idx:end_idx + 1]
 
 
 
98
 
99
- # Attempt to parse the JSON
100
  try:
101
- return json.loads(json_text)
102
  except json.JSONDecodeError as e:
103
  print(f"JSON Parse Error: {str(e)}")
104
- print(f"Attempted to parse: {json_text}")
105
  raise
106
 
 
 
 
 
 
107
  except Exception as e:
108
  print(f"Error parsing response: {str(e)}")
109
  print(f"Original response: {response_text}")
110
- raise ValueError(f"Failed to parse response: {str(e)}")
111
 
112
  def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
113
- """Validate questions with improved error checking"""
114
  validated = []
115
 
116
- for q in questions:
117
  try:
118
- if not self._is_valid_question(q):
119
- print(f"Invalid question format: {q}")
120
  continue
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  validated.append(Question(
123
- question=q["question"].strip(),
124
- options=[str(opt).strip()[:100] for opt in q["options"]],
125
- correct_answer=int(q["correct_answer"]) % 4
126
  ))
 
127
  except Exception as e:
128
  print(f"Error validating question: {str(e)}")
129
  continue
130
 
131
- if not validated:
132
- raise ValueError("No valid questions after validation")
133
-
134
- return validated[:num_questions]
135
-
136
- def _is_valid_question(self, question: Dict) -> bool:
137
- """Check if question format is valid"""
138
- try:
139
- return (
140
- isinstance(question, dict) and
141
- all(key in question for key in ["question", "options", "correct_answer"]) and
142
- isinstance(question["question"], str) and
143
- isinstance(question["options"], list) and
144
- len(question["options"]) == 4 and
145
- all(isinstance(opt, str) for opt in question["options"]) and
146
- isinstance(question["correct_answer"], (int, str)) and
147
- int(question["correct_answer"]) in range(4)
148
- )
149
- except Exception as e:
150
- print(f"Question validation error: {str(e)}")
151
- return False
152
 
153
  class FontManager:
154
  """Manages font installation and loading for the certificate generator"""
 
24
  class QuizGenerator:
25
  def __init__(self, api_key: str):
26
  self.client = Groq(api_key=api_key)
27
+
28
  def generate_questions(self, text: str, num_questions: int) -> List[Question]:
29
+ """Generate quiz questions from input text"""
30
  prompt = self._create_prompt(text, num_questions)
31
 
32
  try:
33
+ # Make API request with improved error handling
34
  response = self.client.chat.completions.create(
35
  messages=[
36
  {
37
  "role": "system",
38
+ "content": """You are a quiz generator. Generate multiple choice questions based on the given text.
39
+ Each question must have exactly 4 options and one correct answer.
40
+ Return ONLY a valid JSON array of questions in the specified format.
41
+ Make questions clear, focused and directly related to the input text.
42
+ Avoid subjective or ambiguous questions."""
43
  },
44
  {
45
  "role": "user",
46
  "content": prompt
47
  }
48
  ],
49
+ model="llama2-70b-4096", # Using a more capable model
50
+ temperature=0.2, # Lower temperature for more consistent output
51
+ max_tokens=2048,
52
+ stop=None # Let the model complete its output
53
  )
54
 
55
+ if not response or not response.choices or not response.choices[0].message.content:
56
+ raise ValueError("Empty response from API")
57
+
58
+ # Parse and validate response
59
  questions = self._parse_response(response.choices[0].message.content)
60
+ validated = self._validate_questions(questions, num_questions)
61
+
62
+ if not validated:
63
+ raise ValueError("No valid questions generated")
64
+
65
+ return validated
66
 
67
  except Exception as e:
68
  print(f"Error in generate_questions: {str(e)}")
69
+ print(f"Full error details: ", {
70
+ "error": str(e),
71
+ "response": getattr(response, 'choices', [{}])[0].get('message', {}).get('content', 'No content')
72
+ if 'response' in locals() else 'No response'
73
+ })
74
  raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
75
 
76
  def _create_prompt(self, text: str, num_questions: int) -> str:
77
+ """Create a clear, structured prompt for question generation"""
78
+ return f"""Generate exactly {num_questions} multiple choice questions based on this text. For each question:
79
+
80
+ 1. Create a clear, specific question focusing on key information
81
+ 2. Provide exactly 4 answer options
82
+ 3. Ensure one and only one option is correct
83
+ 4. Keep options concise (under 10 words each)
84
+ 5. Avoid subjective or ambiguous questions
85
+ 6. Make all options plausible but only one correct
86
 
87
+ Return ONLY a JSON array in this exact format:
 
 
 
 
88
  [
89
  {{
90
+ "question": "Question text here?",
91
  "options": [
92
+ "Option 1",
93
+ "Option 2",
94
+ "Option 3",
95
+ "Option 4"
96
  ],
97
  "correct_answer": 0
98
  }}
99
  ]
100
 
101
+ The correct_answer should be the index (0-3) of the correct option.
102
+ Return only the JSON array, no other text.
103
+
104
  Text to generate questions from:
105
+ {text.strip()}"""
106
 
 
 
107
  def _parse_response(self, response_text: str) -> List[Dict]:
108
+ """Parse API response with robust error handling"""
109
  try:
110
  # Clean up the response text
111
+ cleaned = response_text.strip()
112
+ # Remove any markdown code block markers
113
+ cleaned = cleaned.replace('```json', '').replace('```', '').strip()
114
 
115
  # Find the JSON array
116
+ start = cleaned.find('[')
117
+ end = cleaned.rfind(']') + 1
 
 
 
118
 
119
+ if start == -1 or end == 0:
120
+ raise ValueError("No JSON array found in response")
121
+
122
+ json_str = cleaned[start:end]
123
 
124
+ # Parse JSON
125
  try:
126
+ questions = json.loads(json_str)
127
  except json.JSONDecodeError as e:
128
  print(f"JSON Parse Error: {str(e)}")
129
+ print(f"Attempted to parse: {json_str}")
130
  raise
131
 
132
+ if not isinstance(questions, list):
133
+ raise ValueError("Response is not a list of questions")
134
+
135
+ return questions
136
+
137
  except Exception as e:
138
  print(f"Error parsing response: {str(e)}")
139
  print(f"Original response: {response_text}")
140
+ raise ValueError(f"Failed to parse questions: {str(e)}")
141
 
142
  def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
143
+ """Validate and convert questions to Question objects"""
144
  validated = []
145
 
146
+ for q in questions[:num_questions]:
147
  try:
148
+ # Validate question structure
149
+ if not isinstance(q, dict):
150
  continue
151
 
152
+ required_keys = {"question", "options", "correct_answer"}
153
+ if not all(key in q for key in required_keys):
154
+ continue
155
+
156
+ if not isinstance(q["options"], list) or len(q["options"]) != 4:
157
+ continue
158
+
159
+ # Convert correct_answer to int and validate
160
+ try:
161
+ correct_idx = int(q["correct_answer"])
162
+ if not 0 <= correct_idx < 4:
163
+ continue
164
+ except (ValueError, TypeError):
165
+ continue
166
+
167
+ # Create Question object
168
  validated.append(Question(
169
+ question=str(q["question"]).strip(),
170
+ options=[str(opt).strip() for opt in q["options"]],
171
+ correct_answer=correct_idx
172
  ))
173
+
174
  except Exception as e:
175
  print(f"Error validating question: {str(e)}")
176
  continue
177
 
178
+ return validated
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  class FontManager:
181
  """Manages font installation and loading for the certificate generator"""