capradeepgujaran commited on
Commit
5301f93
1 Parent(s): 91ac2b1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -72
app.py CHANGED
@@ -8,6 +8,7 @@ import tempfile
8
  from typing import List, Dict, Tuple, Optional
9
  from dataclasses import dataclass
10
  import subprocess
 
11
 
12
  @dataclass
13
  class Question:
@@ -26,15 +27,16 @@ class QuizGenerator:
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",
@@ -42,112 +44,148 @@ class QuizGenerator:
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:
 
8
  from typing import List, Dict, Tuple, Optional
9
  from dataclasses import dataclass
10
  import subprocess
11
+ import re
12
 
13
  @dataclass
14
  class Question:
 
27
  self.client = Groq(api_key=api_key)
28
 
29
  def generate_questions(self, text: str, num_questions: int) -> List[Question]:
30
+ """Generate quiz questions using llama-3.2-3b-preview model"""
31
  prompt = self._create_prompt(text, num_questions)
32
 
33
  try:
34
+ # API call with simplified parameters
35
  response = self.client.chat.completions.create(
36
  messages=[
37
  {
38
  "role": "system",
39
+ "content": "You are a quiz generator. Generate multiple choice questions that are clear and focused."
 
40
  },
41
  {
42
  "role": "user",
 
44
  }
45
  ],
46
  model="llama-3.2-3b-preview",
47
+ temperature=0.1,
48
+ max_tokens=1024
49
  )
50
 
51
+ # Extract content safely
52
+ content = response.choices[0].message.content
53
+ if not content:
54
+ raise ValueError("Empty response content")
55
+
56
+ # Parse and validate questions
57
+ questions = self._parse_response(content)
58
+ validated = self._validate_questions(questions, num_questions)
59
+
60
+ if not validated:
61
+ raise ValueError("No valid questions generated")
62
+
63
+ return validated
64
 
65
  except Exception as e:
66
  print(f"Error in generate_questions: {str(e)}")
67
+ if 'response' in locals():
68
+ print("Response content:", content if 'content' in locals() else None)
69
  raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
70
 
71
  def _create_prompt(self, text: str, num_questions: int) -> str:
72
+ """Create a simple, clear prompt optimized for llama-3.2-3b-preview"""
73
+ return f"""Create {num_questions} multiple choice questions about this text. Return only the JSON array in this exact format:
 
 
 
 
 
74
  [
75
+ {{
76
+ "question": "Write the question here?",
77
+ "options": [
78
+ "First option",
79
+ "Second option",
80
+ "Third option",
81
+ "Fourth option"
82
+ ],
83
+ "correct_answer": 0
84
+ }}
85
  ]
86
 
87
+ Rules:
88
+ 1. Return only the JSON array
89
+ 2. Each question must have exactly 4 options
90
+ 3. correct_answer must be 0, 1, 2, or 3
91
+ 4. No explanations or additional text
92
+
93
+ Text to use:
94
+ {text.strip()}"""
95
 
 
 
96
  def _parse_response(self, response_text: str) -> List[Dict]:
97
  """Parse response with improved error handling"""
98
  try:
99
  # Clean up the response text
100
+ cleaned = response_text.strip()
101
+
102
+ # Remove any markdown formatting
103
+ cleaned = cleaned.replace('```json', '').replace('```', '').strip()
104
 
105
  # Find the JSON array
106
+ start = cleaned.find('[')
107
+ end = cleaned.rfind(']') + 1
108
+
109
+ if start == -1 or end == 0:
110
+ raise ValueError("No JSON array found in response")
111
 
112
+ json_str = cleaned[start:end]
 
113
 
114
+ # Remove any trailing commas before closing brackets
115
+ json_str = re.sub(r',(\s*})', r'\1', json_str)
116
+ json_str = re.sub(r',(\s*])', r'\1', json_str)
117
 
118
+ # Try to parse the cleaned JSON
119
  try:
120
+ return json.loads(json_str)
121
+ except json.JSONDecodeError:
122
+ # If that fails, try using ast.literal_eval as a fallback
123
+ import ast
124
+ return ast.literal_eval(json_str)
125
 
126
  except Exception as e:
127
+ print(f"Parse error details: {str(e)}")
128
+ print(f"Attempted to parse: {response_text}")
129
+
130
+ # Last resort: try to fix the JSON manually
131
+ try:
132
+ # Remove any trailing commas and fix newlines
133
+ fixed = re.sub(r',(\s*[}\]])', r'\1', response_text)
134
+ fixed = fixed.replace('}\n{', '},{')
135
+ fixed = fixed.strip()
136
+ if not fixed.startswith('['):
137
+ fixed = '[' + fixed
138
+ if not fixed.endswith(']'):
139
+ fixed = fixed + ']'
140
+ return json.loads(fixed)
141
+ except:
142
+ raise ValueError(f"Failed to parse response: {str(e)}")
143
 
144
  def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
145
+ """Validate questions with strict checking"""
146
  validated = []
147
 
148
+ for q in questions[:num_questions]:
149
  try:
150
+ # Skip invalid questions
151
+ if not isinstance(q, dict):
152
+ continue
153
+
154
+ # Check required fields
155
+ if not all(key in q for key in ['question', 'options', 'correct_answer']):
156
  continue
157
+
158
+ # Validate options
159
+ if not isinstance(q['options'], list) or len(q['options']) != 4:
160
+ continue
161
+
162
+ # Validate correct_answer
163
+ try:
164
+ correct_idx = int(q['correct_answer'])
165
+ if not 0 <= correct_idx <= 3:
166
+ continue
167
+ except (ValueError, TypeError):
168
+ continue
169
+
170
+ # Create validated Question object
171
  validated.append(Question(
172
+ question=str(q['question']).strip(),
173
+ options=[str(opt).strip() for opt in q['options']],
174
+ correct_answer=correct_idx
175
  ))
176
+
177
  except Exception as e:
178
+ print(f"Validation error: {str(e)}")
179
  continue
180
 
181
+ return validated
182
+
183
+ def _is_valid_json(self, json_str: str) -> bool:
184
+ """Check if a string is valid JSON"""
 
 
 
185
  try:
186
+ json.loads(json_str)
187
+ return True
188
+ except:
 
 
 
 
 
 
 
 
 
189
  return False
190
 
191
  class FontManager: