-
Notifications
You must be signed in to change notification settings - Fork 253
Issue Fixed #185 : Fill in the blank feature #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Issue Fixed #185 : Fill in the blank feature #280
Conversation
|
Warning Rate limit exceeded@yuvikaaaaaa has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 4 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughAdds end-to-end fill-in-the-blank support: new generator class producing blanked sentences, generator logic updated to emit fill-in-the-blank inputs, a backend POST route to request them, and a frontend selector to choose the question type. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Frontend
participant Backend
participant FITBGenerator
User->>Frontend: Select "Fill in the Blank", set max_questions, submit text
Frontend->>Backend: POST /get_fillintheblank { input_text, use_mediawiki, max_questions }
Backend->>Backend: (optional) fetch/process MediaWiki summary
Backend->>FITBGenerator: generate(text, num_questions)
FITBGenerator-->>Backend: [{ question: "text with ______", answer: "word" }, ...]
Backend-->>Frontend: 200 OK { output: [...] }
Frontend-->>User: Render fill-in-the-blank questions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Out-of-scope changes
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (2)
backend/Generator/fitb_generator.py (1)
11-11: Consider improving sentence filtering criteria.The current filter only checks word count > 5, but doesn't consider sentence quality. Very short sentences or those with mostly punctuation could still create poor questions.
Consider adding additional filtering:
for sentence in sentences: words = sentence.split() - if len(words) > 5: + # Filter out sentences that are too short or contain mostly punctuation + meaningful_words = [w for w in words if w.isalnum()] + if len(meaningful_words) > 5 and len(sentence.strip()) > 20:backend/server.py (1)
422-437: Consider adding a hard difficulty variant.For consistency with other question types, consider adding a
get_fillintheblank_hardendpoint to support hard difficulty fill-in-the-blank questions.You could add a hard difficulty endpoint similar to the existing ones:
@app.route("/get_fillintheblank_hard", methods=["POST"]) def get_fillintheblank_hard(): data = request.get_json() input_text = data.get("input_text", "") use_mediawiki = data.get("use_mediawiki", 0) max_questions = data.get("max_questions", 4) input_text = process_input_text(input_text, use_mediawiki) output = qg.generate( article=input_text, num_questions=max_questions, answer_style="fill_in_the_blank" ) # Apply transformation to make questions harder if needed # for item in output: # item["question"] = make_question_harder(item["question"]) return jsonify({"output": output})
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
backend/Generator/fitb_generator.py(1 hunks)backend/Generator/main.py(2 hunks)backend/server.py(1 hunks)eduaid_web/src/pages/Text_Input.jsx(3 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
eduaid_web/src/pages/Text_Input.jsx
[error] 88-88: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
🪛 Ruff (0.12.2)
backend/Generator/main.py
17-17: transformers.pipeline imported but unused
Remove unused import: transformers.pipeline
(F401)
18-18: .fitb_generator.FillInTheBlankGenerator imported but unused
Remove unused import: .fitb_generator.FillInTheBlankGenerator
(F401)
🔇 Additional comments (5)
eduaid_web/src/pages/Text_Input.jsx (3)
16-16: LGTM! Question type state management.The questionType state is properly initialized with a sensible default value and will integrate well with the existing form handling logic.
256-267: Well-implemented question type dropdown.The dropdown is properly integrated with the existing UI styling and form state management. The option values correctly match the backend endpoints.
106-115: No hard‐difficulty endpoint needed for fill-in-the-blank questionsThe only backend route for fill-in-the-blank is
/get_fillintheblank(no hard variant). Since the generator always uses the sameanswer_style="fill_in_the_blank"regardless of difficulty, mapping to a non-existent/get_fillintheblank_hardwould break. The currentgetEndpointlogic correctly returns"get_fillintheblank"for both difficulties.If you do want a separate hard-difficulty behavior for fill-in-the-blank, you’d need to add a corresponding backend route (e.g.
/get_fillintheblank_hard) and handler. Otherwise, no change is required here.backend/server.py (1)
422-437: LGTM! Well-structured fill-in-the-blank endpoint.The new endpoint follows the established pattern of existing routes and correctly integrates with the question generation system. The parameter handling and response format are consistent with other endpoints.
backend/Generator/main.py (1)
454-456: LGTM! Improved error message formatting.The conversion to f-string syntax improves readability and is more modern Python style.
backend/Generator/main.py
Outdated
| def generate_qg_inputs(self, text: str, answer_style: str) -> Tuple[List[str], List[str]]: | ||
| VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice", "fill_in_blank"] | ||
| if answer_style not in VALID_ANSWER_STYLES: | ||
| raise ValueError( | ||
| "Invalid answer style {}. Please choose from {}".format( | ||
| answer_style, VALID_ANSWER_STYLES | ||
| ) | ||
| ) | ||
| raise ValueError(f"Invalid answer style {answer_style}. Choose from {VALID_ANSWER_STYLES}") | ||
|
|
||
| inputs = [] | ||
| answers = [] | ||
| inputs, answers = [], [] | ||
|
|
||
| if answer_style == "sentences" or answer_style == "all": | ||
| if answer_style in ["sentences", "all"]: | ||
| segments = self._split_into_segments(text) | ||
|
|
||
| for segment in segments: | ||
| sentences = self._split_text(segment) | ||
| prepped_inputs, prepped_answers = self._prepare_qg_inputs( | ||
| sentences, segment | ||
| ) | ||
| prepped_inputs, prepped_answers = self._prepare_qg_inputs(sentences, segment) | ||
| inputs.extend(prepped_inputs) | ||
| answers.extend(prepped_answers) | ||
|
|
||
| if answer_style == "multiple_choice" or answer_style == "all": | ||
| if answer_style in ["multiple_choice", "all"]: | ||
| sentences = self._split_text(text) | ||
| prepped_inputs, prepped_answers = self._prepare_qg_inputs_MC(sentences) | ||
| inputs.extend(prepped_inputs) | ||
| answers.extend(prepped_answers) | ||
|
|
||
| if answer_style == "fill_in_blank": | ||
| sentences = self._split_text(text) | ||
| for sentence in sentences: | ||
| words = sentence.split() | ||
| if len(words) > 5: | ||
| blank_index = random.randint(0, len(words) - 1) | ||
| answer = words[blank_index] | ||
| words[blank_index] = "_____" | ||
| question = " ".join(words) | ||
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | ||
| answers.append({"question": question, "answer": answer}) | ||
|
|
||
| return inputs, answers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify consistency with existing question generation patterns.
The fill-in-the-blank logic should follow the same patterns as other answer styles for consistency in the question generation pipeline.
🏁 Script executed:
#!/bin/bash
# Check how other answer styles structure their responses
rg -A 10 -B 5 "answer_style.*multiple_choice" backend/Generator/
rg -A 10 -B 5 "answer_style.*sentences" backend/Generator/Length of output: 2442
🏁 Script executed:
#!/bin/bash
# Find definitions of _prepare_qg_inputs and _prepare_qg_inputs_MC
rg -n -A 5 -B 5 "def _prepare_qg_inputs" backend/Generator/main.py
rg -n -A 5 -B 5 "def _prepare_qg_inputs_MC" backend/Generator/main.pyLength of output: 1796
Refactor fill-in-the-blank logic to match other QG patterns
The fill_in_blank branch currently returns answers as dicts, whereas both _prepare_qg_inputs and _prepare_qg_inputs_MC return Tuple[List[str], List[str]] (i.e. lists of strings). Extract the inline FIB logic into its own helper that returns (inputs, answers) as lists of strings.
Affected locations:
backend/Generator/main.py, lines ~465–486: remove the inlinefill_in_blankloop.backend/Generator/main.py, after line 554: add a_prepare_qg_inputs_FIBmethod.
Example diff:
--- a/backend/Generator/main.py
+++ b/backend/Generator/main.py
@@ def generate_qg_inputs(self, text: str, answer_style: str) -> Tuple[List[str], List[str]]:
- if answer_style == "fill_in_blank":
- sentences = self._split_text(text)
- for sentence in sentences:
- words = sentence.split()
- if len(words) > 5:
- blank_index = random.randint(0, len(words) - 1)
- answer = words[blank_index]
- words[blank_index] = "_____"
- question = " ".join(words)
- inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}")
- answers.append({"question": question, "answer": answer})
+ if answer_style == "fill_in_blank":
+ sentences = self._split_text(text)
+ prepped_inputs, prepped_answers = self._prepare_qg_inputs_FIB(sentences)
+ inputs.extend(prepped_inputs)
+ answers.extend(prepped_answers)
@@
return inputs, answers
+ def _prepare_qg_inputs_FIB(
+ self, sentences: List[str]
+ ) -> Tuple[List[str], List[str]]:
+ """Creates cloze questions by blanking a random word.
+ Returns lists of model inputs and answer strings."""
+ inputs, answers = [], []
+ for sentence in sentences:
+ words = sentence.split()
+ if len(words) > 5:
+ idx = random.randint(0, len(words) - 1)
+ answer = words[idx]
+ words[idx] = "_____"
+ inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}")
+ answers.append(answer)
+ return inputs, answers📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def generate_qg_inputs(self, text: str, answer_style: str) -> Tuple[List[str], List[str]]: | |
| VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice", "fill_in_blank"] | |
| if answer_style not in VALID_ANSWER_STYLES: | |
| raise ValueError( | |
| "Invalid answer style {}. Please choose from {}".format( | |
| answer_style, VALID_ANSWER_STYLES | |
| ) | |
| ) | |
| raise ValueError(f"Invalid answer style {answer_style}. Choose from {VALID_ANSWER_STYLES}") | |
| inputs = [] | |
| answers = [] | |
| inputs, answers = [], [] | |
| if answer_style == "sentences" or answer_style == "all": | |
| if answer_style in ["sentences", "all"]: | |
| segments = self._split_into_segments(text) | |
| for segment in segments: | |
| sentences = self._split_text(segment) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs( | |
| sentences, segment | |
| ) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs(sentences, segment) | |
| inputs.extend(prepped_inputs) | |
| answers.extend(prepped_answers) | |
| if answer_style == "multiple_choice" or answer_style == "all": | |
| if answer_style in ["multiple_choice", "all"]: | |
| sentences = self._split_text(text) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs_MC(sentences) | |
| inputs.extend(prepped_inputs) | |
| answers.extend(prepped_answers) | |
| if answer_style == "fill_in_blank": | |
| sentences = self._split_text(text) | |
| for sentence in sentences: | |
| words = sentence.split() | |
| if len(words) > 5: | |
| blank_index = random.randint(0, len(words) - 1) | |
| answer = words[blank_index] | |
| words[blank_index] = "_____" | |
| question = " ".join(words) | |
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | |
| answers.append({"question": question, "answer": answer}) | |
| return inputs, answers | |
| def generate_qg_inputs(self, text: str, answer_style: str) -> Tuple[List[str], List[str]]: | |
| VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice", "fill_in_blank"] | |
| if answer_style not in VALID_ANSWER_STYLES: | |
| raise ValueError(f"Invalid answer style {answer_style}. Choose from {VALID_ANSWER_STYLES}") | |
| inputs, answers = [], [] | |
| if answer_style in ["sentences", "all"]: | |
| segments = self._split_into_segments(text) | |
| for segment in segments: | |
| sentences = self._split_text(segment) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs(sentences, segment) | |
| inputs.extend(prepped_inputs) | |
| answers.extend(prepped_answers) | |
| if answer_style in ["multiple_choice", "all"]: | |
| sentences = self._split_text(text) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs_MC(sentences) | |
| inputs.extend(prepped_inputs) | |
| answers.extend(prepped_answers) | |
| if answer_style == "fill_in_blank": | |
| sentences = self._split_text(text) | |
| prepped_inputs, prepped_answers = self._prepare_qg_inputs_FIB(sentences) | |
| inputs.extend(prepped_inputs) | |
| answers.extend(prepped_answers) | |
| return inputs, answers | |
| def _prepare_qg_inputs_FIB( | |
| self, sentences: List[str] | |
| ) -> Tuple[List[str], List[str]]: | |
| """Creates cloze questions by blanking a random word. | |
| Returns lists of model inputs and answer strings.""" | |
| inputs, answers = [], [] | |
| for sentence in sentences: | |
| words = sentence.split() | |
| if len(words) > 5: | |
| idx = random.randint(0, len(words) - 1) | |
| answer = words[idx] | |
| words[idx] = "_____" | |
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | |
| answers.append(answer) | |
| return inputs, answers |
🤖 Prompt for AI Agents
In backend/Generator/main.py around lines 465 to 486, refactor the inline
fill-in-the-blank logic inside generate_qg_inputs by extracting it into a new
helper method named _prepare_qg_inputs_FIB that returns a tuple of two lists of
strings (inputs and answers), consistent with the other _prepare_qg_inputs
methods. Remove the inline loop from generate_qg_inputs and replace it with a
call to this new helper. Add the _prepare_qg_inputs_FIB method after line 554,
implementing the fill-in-the-blank logic to produce and return lists of input
strings and answer strings.
| if answer_style == "fill_in_blank": | ||
| sentences = self._split_text(text) | ||
| for sentence in sentences: | ||
| words = sentence.split() | ||
| if len(words) > 5: | ||
| blank_index = random.randint(0, len(words) - 1) | ||
| answer = words[blank_index] | ||
| words[blank_index] = "_____" | ||
| question = " ".join(words) | ||
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | ||
| answers.append({"question": question, "answer": answer}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix potential index out of bounds and word replacement issues.
The fill-in-the-blank implementation has two critical issues:
random.randint(0, len(words) - 1)can select the first or last word, which contradicts the intention to exclude them- The answer dictionary structure is inconsistent with other answer types
Apply this diff to fix the issues:
if answer_style == "fill_in_blank":
sentences = self._split_text(text)
for sentence in sentences:
words = sentence.split()
if len(words) > 5:
- blank_index = random.randint(0, len(words) - 1)
+ blank_index = random.randint(1, len(words) - 2)
answer = words[blank_index]
words[blank_index] = "_____"
question = " ".join(words)
inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}")
- answers.append({"question": question, "answer": answer})
+ answers.append(answer)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if answer_style == "fill_in_blank": | |
| sentences = self._split_text(text) | |
| for sentence in sentences: | |
| words = sentence.split() | |
| if len(words) > 5: | |
| blank_index = random.randint(0, len(words) - 1) | |
| answer = words[blank_index] | |
| words[blank_index] = "_____" | |
| question = " ".join(words) | |
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | |
| answers.append({"question": question, "answer": answer}) | |
| if answer_style == "fill_in_blank": | |
| sentences = self._split_text(text) | |
| for sentence in sentences: | |
| words = sentence.split() | |
| if len(words) > 5: | |
| blank_index = random.randint(1, len(words) - 2) | |
| answer = words[blank_index] | |
| words[blank_index] = "_____" | |
| question = " ".join(words) | |
| inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") | |
| answers.append(answer) |
🤖 Prompt for AI Agents
In backend/Generator/main.py around lines 474 to 484, the fill-in-the-blank
logic incorrectly allows selecting the first or last word as the blank, which
should be excluded. Fix this by changing the random index selection to exclude
the first and last words, using random.randint(1, len(words) - 2). Also, update
the answer dictionary structure to be consistent with other answer types by
adjusting its keys and format accordingly.
eduaid_web/src/pages/Text_Input.jsx
Outdated
| await sendToBackend( | ||
| text, | ||
| difficulty, | ||
| questionType | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix syntax error - missing closing brace.
The code has a syntax error where the sendToBackend call is outside the handleSaveToLocalStorage function due to a missing closing brace.
Apply this diff to fix the syntax:
localStorage.setItem("selectedQuestionType", questionType);
await sendToBackend(
text,
difficulty,
questionType
);
+ } else {
+ alert("Please enter some text or provide a Google Doc URL");
+ }
+ setLoading(false);
+ };🧰 Tools
🪛 Biome (2.1.2)
[error] 88-88: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
🤖 Prompt for AI Agents
In eduaid_web/src/pages/Text_Input.jsx around lines 88 to 92, the call to
sendToBackend is placed outside the handleSaveToLocalStorage function because of
a missing closing brace. Add the missing closing brace to properly close the
handleSaveToLocalStorage function before the sendToBackend call to fix the
syntax error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
backend/Generator/main.py (2)
486-498: (Optional) Extract FIB prep into a helper for parity with other styles.To keep
generate_qg_inputssymmetrical with_prepare_qg_inputsand_prepare_qg_inputs_MC, move the FIB loop into its own helper and call it here:- if answer_style == "fill_in_blank": - sentences = self._split_text(text) - for sentence in sentences: - words = sentence.split() - if len(words) > 5: - blank_index = random.randint(1, len(words) - 2) # avoid first/last token - answer = words[blank_index] - words[blank_index] = "_____" - inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") - answers.append(answer) + if answer_style == "fill_in_blank": + sentences = self._split_text(text) + prepped_inputs, prepped_answers = self._prepare_qg_inputs_FIB(sentences) + inputs.extend(prepped_inputs) + answers.extend(prepped_answers)Add this helper near the other prep helpers:
def _prepare_qg_inputs_FIB(self, sentences: List[str]) -> Tuple[List[str], List[str]]: """Creates cloze questions by blanking a random non-edge word. Returns (model inputs, answers).""" inputs, answers = [], [] for sentence in sentences: words = sentence.split() if len(words) > 5: idx = random.randint(1, len(words) - 2) answer = words[idx] # Keep the blank only for UI construction; model input remains answer + full context: inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") answers.append(answer) return inputs, answers
486-498: Do not append mixed-type answers and avoid edge-token blanks (breaks assertion and evaluation).
- You append a dict and then a string into
answers, doubling entries and mixing types. This breaks the invariant checked on Lines 423–426 and will crash scoring/evaluation.- Index selection can pick the first token; blanking an edge token usually degrades question quality.
Apply this minimal fix:
- if answer_style == "fill_in_blank": - sentences = self._split_text(text) - for sentence in sentences: - words = sentence.split() - if len(words) > 5: - blank_index = random.randint(0, len(words) - 2) - answer = words[blank_index] - words[blank_index] = "_____" - question = " ".join(words) - inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") - answers.append({"question": question, "answer": answer}) - answers.append(answer) + if answer_style == "fill_in_blank": + sentences = self._split_text(text) + for sentence in sentences: + words = sentence.split() + if len(words) > 5: + blank_index = random.randint(1, len(words) - 2) # avoid first/last token + answer = words[blank_index] + words[blank_index] = "_____" + inputs.append(f"{self.ANSWER_TOKEN} {answer} {self.CONTEXT_TOKEN} {sentence}") + answers.append(answer)Note: The local variable
question(blanked sentence) was unused and is intentionally removed here. If you intend to surface literal cloze strings to the UI, see the architecture note in the next comment.
🧹 Nitpick comments (2)
backend/Generator/main.py (2)
418-448: Cloze vs. generated questions: wire up the blanked sentence if that’s the intended UX.Right now, even for FIB, you still call
generate_questions_from_inputs(...), which produces model-generated natural questions, not blanked sentences. If the UI expects a literal “fill ___ here” string, route FIB to use the blanked sentences as the “question” and the removed token as the “answer,” bypassing T5 for this style.One low-impact way is to special-case in
generate():- qg_inputs, qg_answers = self.generate_qg_inputs(article, answer_style) - generated_questions = self.generate_questions_from_inputs(qg_inputs) + if answer_style in ("fill_in_blank", "fill_in_the_blank"): + sentences = self._split_text(article) + fib_questions, qg_answers = [], [] + for sentence in sentences: + words = sentence.split() + if len(words) > 5: + idx = random.randint(1, len(words) - 2) + answer = words[idx] + words[idx] = "_____" + fib_questions.append(" ".join(words)) + qg_answers.append(answer) + generated_questions = fib_questions + else: + qg_inputs, qg_answers = self.generate_qg_inputs(article, answer_style) + generated_questions = self.generate_questions_from_inputs(qg_inputs)This keeps MCQ and sentence styles unchanged and makes FIB deterministic and fast. If you prefer to keep all prep inside helpers, extract a
_prepare_qg_inputs_FIB(...)that returns(fib_questions, fib_answers)and call it here.
413-416: Update the docstring to list the new answer style.The list of supported styles is outdated; add FIB (and mention the alias for clarity).
- should selected from ["all", "sentences", "multiple_choice"]. + should be selected from ["all", "sentences", "multiple_choice", "fill_in_blank"]. + Alias accepted: "fill_in_the_blank".
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
backend/Generator/main.py(2 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
backend/Generator/main.py
463-463: SyntaxError: Expected ',', found ':'
🔇 Additional comments (1)
backend/Generator/main.py (1)
420-447: Smoke-test the QG invariant and confirm endpoint wiringAfter applying the fixes, you must ensure that for every
answer_stylelen(generated_questions) == len(qg_answers)holds true. Run these greps on a simple stub text to catch any regressions:
• Locate all generator invocations in the backend:
rg -n -C2 'qg\.generate\(' backend/Generator/main.py backend/server.py• Verify the fill-in-the-blank route and its hard-coded style:
rg -n -C2 '@app\.route.*fillintheblank' backend/server.py rg -n -C2 'answer_style="fill_in_the_blank"' backend/Generator/main.py• Confirm the API handler reads the expected JSON keys:
rg -n -C2 'data\.get("input_text"' backend/server.py rg -n -C2 'data\.get("num_questions"' backend/server.py• (Optional) Ensure the front end hits the correct endpoint and preserves the selected style:
rg -n -C2 'selectedQuestionType' src/ rg -n -C2 '/get_fillintheblank' src/
| VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice"] | ||
|
|
||
| if answer_style not in VALID_ANSWER_STYLES: | ||
| raise ValueError( | ||
| "Invalid answer style {}. Please choose from {}".format( | ||
| answer_style, VALID_ANSWER_STYLES | ||
| "Invalid answer style {}. Choose from {}".format( | ||
| answer_style, VALID_ANSWER_STYLES: | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Fix syntax error and accept the new answer style (alias included).
- There’s a hard syntax error on Line 463 due to a stray colon after
VALID_ANSWER_STYLESinsideformat(...). VALID_ANSWER_STYLESdoes not include the new style. Also, the backend route (per PR summary) passesanswer_style="fill_in_the_blank"while this file checks for"fill_in_blank", causing an immediate ValueError.
Apply this diff to resolve both issues and add an alias so both spellings work:
- VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice"]
- if answer_style not in VALID_ANSWER_STYLES:
- raise ValueError(
- "Invalid answer style {}. Choose from {}".format(
- answer_style, VALID_ANSWER_STYLES:
- )
- )
+ # Accept both canonical and alias spellings for fill-in-the-blank
+ VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice", "fill_in_blank"]
+ alias_map = {"fill_in_the_blank": "fill_in_blank"}
+ answer_style = alias_map.get(answer_style, answer_style)
+ if answer_style not in VALID_ANSWER_STYLES:
+ raise ValueError(
+ "Invalid answer style {}. Choose from {}".format(
+ answer_style, VALID_ANSWER_STYLES
+ )
+ )Run this to find all occurrences and confirm the spelling used across the repo:
🏁 Script executed:
#!/bin/bash
# Verify style keys used across backend and frontend
rg -n -C2 -i 'fill[_- ]in[_- ]the[_- ]blank|fill_in_blank|fill-in-the-blank'Length of output: 243
I’ll broaden the regex and pull in the surrounding context to verify both key spellings and the actual check location:
🏁 Script executed:
#!/bin/bash
# Search for canonical and alias spellings repo-wide
rg -n -C2 -i 'fill_in_the_blank|fill-in-the-blank|fill_in_blank'
# Locate the answer_style check in backend/Generator/main.py
rg -n -C2 'answer_style' backend/Generator/main.py
# Show lines 450–480 of the file to confirm the exact snippet
sed -n '450,480p' backend/Generator/main.pyLength of output: 3899
Fix syntax error in the ValueError call and add missing “fill-in-the-blank” alias
The generator currently has two critical issues in backend/Generator/main.py around lines 459–465:
- A stray colon after
VALID_ANSWER_STYLESin the.format(...)call causes aSyntaxErrorat line 463. VALID_ANSWER_STYLESomits the"fill_in_blank"style, yet the route inbackend/server.pypasses"fill_in_the_blank", so every request with that style will raise an (incorrect)ValueError.
Apply this patch to resolve both:
--- a/backend/Generator/main.py
+++ b/backend/Generator/main.py
@@ -458,7 +458,13 @@
VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice"]
- if answer_style not in VALID_ANSWER_STYLES:
+ # Support canonical and alias spellings for fill-in-the-blank
+ VALID_ANSWER_STYLES = [
+ "all",
+ "sentences",
+ "multiple_choice",
+ "fill_in_blank",
+ ]
+ alias_map = {"fill_in_the_blank": "fill_in_blank"}
+ answer_style = alias_map.get(answer_style, answer_style)
+ if answer_style not in VALID_ANSWER_STYLES:
raise ValueError(
- "Invalid answer style {}. Choose from {}".format(
- answer_style, VALID_ANSWER_STYLES:
- )
+ "Invalid answer style {}. Choose from {}".format(
+ answer_style, VALID_ANSWER_STYLES
+ )
)• Adds "fill_in_blank" alongside an alias for "fill_in_the_blank".
• Removes the stray colon after VALID_ANSWER_STYLES in the format call.
• Ensures incoming styles from backend/server.py (answer_style="fill_in_the_blank") map correctly.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| VALID_ANSWER_STYLES = ["all", "sentences", "multiple_choice"] | |
| if answer_style not in VALID_ANSWER_STYLES: | |
| raise ValueError( | |
| "Invalid answer style {}. Please choose from {}".format( | |
| answer_style, VALID_ANSWER_STYLES | |
| "Invalid answer style {}. Choose from {}".format( | |
| answer_style, VALID_ANSWER_STYLES: | |
| ) | |
| ) | |
| # Support canonical and alias spellings for fill-in-the-blank | |
| VALID_ANSWER_STYLES = [ | |
| "all", | |
| "sentences", | |
| "multiple_choice", | |
| "fill_in_blank", | |
| ] | |
| alias_map = {"fill_in_the_blank": "fill_in_blank"} | |
| answer_style = alias_map.get(answer_style, answer_style) | |
| if answer_style not in VALID_ANSWER_STYLES: | |
| raise ValueError( | |
| "Invalid answer style {}. Choose from {}".format( | |
| answer_style, VALID_ANSWER_STYLES | |
| ) | |
| ) |
🧰 Tools
🪛 Ruff (0.12.2)
463-463: SyntaxError: Expected ',', found ':'
🤖 Prompt for AI Agents
In backend/Generator/main.py around lines 459 to 465, the .format(...) call
currently has a stray colon causing a SyntaxError and the VALID_ANSWER_STYLES
list is missing the "fill_in_blank" option while incoming requests use the alias
"fill_in_the_blank"; remove the stray colon after VALID_ANSWER_STYLES in the
ValueError .format call, add "fill_in_blank" to VALID_ANSWER_STYLES, and
normalize incoming answer_style by mapping "fill_in_the_blank" to
"fill_in_blank" (or accept both) before validation so the ValueError only fires
for truly invalid styles.
|
@Aditya062003 I have resolved the issue, Please go through it! |
|
@Aditya062003 Please do check! |
🧩 Summary
This pull request introduces a complete "Fill-in-the-Blank" question generation feature into the EduAid application. Users can now select this question type, enter custom text or use MediaWiki input, and receive intelligently generated fill-in-the-blank questions.
✨ Key Features Implemented
✅ Dropdown Selector for choosing question type (MCQ, Short Answer, Fill-in-the-Blank).
✅ LocalStorage Integration to retain the selected question type across routes.
✅ API Route /get_fillintheblank created in main.py to handle fill-in-the-blank generation using the QG model.
✅ Frontend Integration:
Question type passed dynamically based on user selection.
Output page handles and displays fill-in-the-blank format correctly.
✅ UI/UX Enhancements:
Dropdown placed below difficulty selector.
Page flow remains consistent and responsive.
🛠 Backend Updates
Added new API route /get_fillintheblank in main.py.
Used QG pipeline with answer_style="fill_in_the_blank".
Incorporated preprocessing using process_input_text() to clean and optionally fetch MediaWiki content.
🖼 Frontend Changes
Updated Text_Input.jsx to include question type dropdown and logic.
Passed question type to backend depending on selection.
Adjusted output display logic to show fill-in-the-blank formatted results.
✅ Testing Done
Tested with both custom input and MediaWiki content.
Verified all 3 types of questions (MCQ, Short Answer, Fill-in-the-Blank).
Confirmed output formatting and route integration.
📂 Files Changed
main.py
Text_Input.jsx
App.js (unchanged, shown here for routing reference)
🔗 Related Issue
Closes: #185
Summary by CodeRabbit
New Features
Enhancements