From 90721b999c971967b7fddf43c25d37f2d2b590d0 Mon Sep 17 00:00:00 2001 From: piyush06singhal Date: Thu, 2 Apr 2026 17:31:09 +0530 Subject: [PATCH 1/2] add validation for llm generated questions outputs to prevent malformed responses --- backend/Generator/llm_generator.py | 11 ++- backend/Generator/output_validator.py | 127 ++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 backend/Generator/output_validator.py 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..5f6539fb --- /dev/null +++ b/backend/Generator/output_validator.py @@ -0,0 +1,127 @@ +""" +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 non-empty list + - 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 + + # 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 From baf8d716a177278c6e35926748d4a0b1d5e71ad8 Mon Sep 17 00:00:00 2001 From: piyush06singhal Date: Thu, 2 Apr 2026 17:43:06 +0530 Subject: [PATCH 2/2] addressed the nipstick errors --- backend/Generator/output_validator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/Generator/output_validator.py b/backend/Generator/output_validator.py index 5f6539fb..96d1f52a 100644 --- a/backend/Generator/output_validator.py +++ b/backend/Generator/output_validator.py @@ -16,7 +16,7 @@ def validate_mcq(questions): Validation Rules: - question field must exist and be non-empty string - - options field must exist and be non-empty list + - 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: @@ -37,7 +37,11 @@ def validate_mcq(questions): 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():