Skip to content

Conversation

@yuvikaaaaaa
Copy link

@yuvikaaaaaa yuvikaaaaaa commented Jul 29, 2025

🧩 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

    • Generate fill-in-the-blank questions from input text.
    • New API endpoint to request fill-in-the-blank questions.
    • UI dropdown to choose question type (MCQ, Short Answer, Fill in the Blank).
  • Enhancements

    • Fill-in-the-blank option added alongside existing styles with input validation and configurable question count.

@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 776dc2d and 4b277c3.

📒 Files selected for processing (1)
  • backend/Generator/main.py (2 hunks)

Walkthrough

Adds 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

Cohort / File(s) Change Summary
Fill-in-the-Blank Generator Logic
backend/Generator/fitb_generator.py
Added FillInTheBlankGenerator with generate(self, text, num_questions=4) that validates input and returns blanked sentence dictionaries {"question": ..., "answer": ...}.
Question Generation Logic Update
backend/Generator/main.py
generate_qg_inputs extended to handle answer_style == "fill_in_blank": splits text into sentences, creates blanked sentences, appends generator inputs with ANSWER_TOKEN/CONTEXT_TOKEN, and records answers; minor whitespace adjustments.
Backend API Endpoint
backend/server.py
New POST route /get_fillintheblank (get_fillintheblank) accepting {input_text, use_mediawiki, max_questions} and returning generated output under "output".
Frontend UI and State
eduaid_web/src/pages/Text_Input.jsx
Added questionType state and dropdown to select MCQ / Short Answer / Fill in the Blank; selection integrated into request flow.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Add a new question type for fill-in-the-blank questions (#185)
Update the user interface to allow users to select and create fill-in-the-blank questions (#185)
Implement backend logic to handle the generation and validation of fill-in-the-blank questions (#185)
Ensure compatibility with existing features such as quiz history and answer generation (#185) No changes to persistence, DB schema, or quiz-history related code are present in this diff.

Out-of-scope changes

Code Change (file_path) Explanation
Appends raw answer separately to answers list (backend/Generator/main.py) Adds an extra raw-answer entry alongside structured question/answer dicts; not specified in #185 and may affect consumers expecting only dict entries.

"I nibble lines and pluck a word,
I leave a blank — now say the third.
From prompt to patch, the questions spring,
A rabbit’s nose and joyous ping.
Hop, fill, and learn — let knowledge sing!" 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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_hard endpoint 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

📥 Commits

Reviewing files that changed from the base of the PR and between 122eefb and 48f4e1a.

📒 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 questions

The only backend route for fill-in-the-blank is /get_fillintheblank (no hard variant). Since the generator always uses the same answer_style="fill_in_the_blank" regardless of difficulty, mapping to a non-existent /get_fillintheblank_hard would break. The current getEndpoint logic 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.

Comment on lines 453 to 486
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
Copy link

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.py

Length 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 inline fill_in_blank loop.
  • backend/Generator/main.py, after line 554: add a _prepare_qg_inputs_FIB method.

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.

Suggested change
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.

Comment on lines 474 to 484
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})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential index out of bounds and word replacement issues.

The fill-in-the-blank implementation has two critical issues:

  1. random.randint(0, len(words) - 1) can select the first or last word, which contradicts the intention to exclude them
  2. 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.

Suggested change
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.

Comment on lines 88 to 92
await sendToBackend(
text,
difficulty,
questionType
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Copy link

@coderabbitai coderabbitai bot left a 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_inputs symmetrical with _prepare_qg_inputs and _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.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a79f1c and 77f32d2.

📒 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 wiring

After applying the fixes, you must ensure that for every answer_style

len(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/

Comment on lines 459 to 465
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:
)
)
Copy link

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_STYLES inside format(...).
  • VALID_ANSWER_STYLES does not include the new style. Also, the backend route (per PR summary) passes answer_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.py

Length 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_STYLES in the .format(...) call causes a SyntaxError at line 463.
  • VALID_ANSWER_STYLES omits the "fill_in_blank" style, yet the route in backend/server.py passes "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.

Suggested change
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.

@yuvikaaaaaa
Copy link
Author

@Aditya062003 I have resolved the issue, Please go through it!

@yuvikaaaaaa
Copy link
Author

@Aditya062003 Please do check!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Enhancement: Support for Fill-in-the-Blank Questions

1 participant