""" option_generator.py Generates the 4-option list for an MCQ. Changes from original: - Accepts pos_tag and forwards it to generate_distractors so the auto_register / cluster routing uses POS for better sub-cluster matching. - All options (including the answer) are root forms — lemmatization has already happened upstream in process_pipeline.py. """ import random import sys import os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from question_generation.cluster_store import generate_distractors def generate_options(answer_word: str, sentence_words: list[str], pos_tags: list[tuple] | None = None, n: int = 3, domain: str | None = None, question: str = "", context: str = "") -> list[str]: """ Return a shuffled list of 4 options (3 distractors + 1 correct answer). All items are root forms. Parameters ---------- answer_word : root form of the correct answer sentence_words : tokenised words from the sentence (used to avoid accidentally selecting a word already in the question) pos_tags : list of (word, upos) pairs from Stanza — used to look up the POS of answer_word for better cluster routing n : number of distractors to generate (default 3) question : MCQ question string (for keyword-based cluster detection) context : original sentence (for domain detection) """ # Derive POS tag for the answer word from pos_tags list pos_tag = "NOUN" # safe default if pos_tags: pos_lookup = {word: tag for word, tag in pos_tags} # pos_tags uses surface (inflected) forms; answer_word is root form. # Try direct match first, then check if any surface form shares the root. if answer_word in pos_lookup: pos_tag = pos_lookup[answer_word] else: # answer_word is the root; surface form may differ — use first NOUN/PROPN for word, tag in pos_tags: if tag in ("NOUN", "PROPN"): pos_tag = tag break distractors = generate_distractors( answer=answer_word, question=question, context=context, k=n, ) options = distractors[:n] + [answer_word] random.shuffle(options) return options