diff --git a/backend/Generator/llm_generator.py b/backend/Generator/llm_generator.py index e6333030..a363ccb3 100644 --- a/backend/Generator/llm_generator.py +++ b/backend/Generator/llm_generator.py @@ -2,7 +2,7 @@ import re import threading from llama_cpp import Llama - +from .output_validator import validate_mcq, validate_shortq, validate_boolq class LLMQuestionGenerator: """Generates various types of questions using Qwen3-0.6B via llama.cpp. @@ -73,7 +73,8 @@ def generate_short_questions(self, input_text, max_questions=4): return [] raw = choices[0].get("message", {}).get("content", "") - return self._parse_response(raw, max_questions) + parsed_questions = self._parse_response(raw, max_questions) + return validate_shortq(parsed_questions) except (AttributeError, TypeError, ValueError): return [] @@ -113,7 +114,8 @@ def generate_mcq_questions(self, input_text, max_questions=4): return [] raw = choices[0].get("message", {}).get("content", "") - return self._parse_mcq_response(raw, max_questions) + parsed_questions = self._parse_mcq_response(raw, max_questions) + return validate_mcq(parsed_questions) except (AttributeError, TypeError, ValueError): return [] @@ -152,7 +154,8 @@ def generate_boolean_questions(self, input_text, max_questions=4): return [] raw = choices[0].get("message", {}).get("content", "") - return self._parse_bool_response(raw, max_questions) + parsed_questions = self._parse_bool_response(raw, max_questions) + return validate_boolq(parsed_questions) except (AttributeError, TypeError, ValueError): return [] diff --git a/backend/Generator/output_validator.py b/backend/Generator/output_validator.py new file mode 100644 index 00000000..96d1f52a --- /dev/null +++ b/backend/Generator/output_validator.py @@ -0,0 +1,131 @@ +""" +Output validation for LLM-generated questions. +Ensures that only well-formed, complete questions are returned to API consumers. +""" + + +def validate_mcq(questions): + """ + Validate multiple-choice questions. + + Args: + questions: List of MCQ question dictionaries + + Returns: + List of valid MCQ questions (filtered) + + Validation Rules: + - question field must exist and be non-empty string + - options field must exist and be a list with at least 2 elements + - correct_answer field must exist and be non-empty string + """ + if not questions: + return [] + + valid_questions = [] + + for q in questions: + if not isinstance(q, dict): + continue + + # Check question field + question = q.get("question") + if not question or not isinstance(question, str) or not question.strip(): + continue + + # Check options field + options = q.get("options") + if not options or not isinstance(options, list) or len(options) < 2: + continue + + # Verify all options are non-empty strings + if not all(isinstance(opt, str) and opt.strip() for opt in options): + continue + + # Check correct_answer field + correct_answer = q.get("correct_answer") + if not correct_answer or not isinstance(correct_answer, str) or not correct_answer.strip(): + continue + + # All checks passed - this is a valid question + valid_questions.append(q) + + return valid_questions + + +def validate_shortq(questions): + """ + Validate short answer questions. + + Args: + questions: List of short answer question dictionaries + + Returns: + List of valid short answer questions (filtered) + + Validation Rules: + - question field must exist and be non-empty string + - answer field must exist and be non-empty string + """ + if not questions: + return [] + + valid_questions = [] + + for q in questions: + if not isinstance(q, dict): + continue + + # Check question field + question = q.get("question") + if not question or not isinstance(question, str) or not question.strip(): + continue + + # Check answer field + answer = q.get("answer") + if not answer or not isinstance(answer, str) or not answer.strip(): + continue + + # All checks passed - this is a valid question + valid_questions.append(q) + + return valid_questions + + +def validate_boolq(questions): + """ + Validate boolean (true/false) questions. + + Args: + questions: List of boolean question dictionaries + + Returns: + List of valid boolean questions (filtered) + + Validation Rules: + - question field must exist and be non-empty string + - answer field must exist and be boolean type (True or False) + """ + if not questions: + return [] + + valid_questions = [] + + for q in questions: + if not isinstance(q, dict): + continue + + # Check question field + question = q.get("question") + if not question or not isinstance(question, str) or not question.strip(): + continue + + # Check answer field - must be boolean type, not string or int + answer = q.get("answer") + if not isinstance(answer, bool): + continue + + # All checks passed - this is a valid question + valid_questions.append(q) + + return valid_questions