flask_api / question_api /restructure_questions.py
vikkyblacq's picture
first commit
87112c5
Raw History Blame Contribute Delete
4.78 kB
import os
import logging
from pathlib import Path
from dotenv import load_dotenv
import google.generativeai as genai
from concurrent.futures import ThreadPoolExecutor, as_completed
import sys
# Load .env file
env_path = Path(__file__).parent.parent.parent / '.env'
if env_path.exists():
load_dotenv(env_path)
else:
load_dotenv()
sys.path.append(os.path.dirname(__file__))
from utils import parse_restructured_text
logger = logging.getLogger(__name__)
# Get Gemini API key
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if not GEMINI_API_KEY:
raise EnvironmentError("GEMINI_API_KEY not set")
# Configure Gemini
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash-latest")
def build_batch_prompt(mcqs, theories, batch_num):
"""Build OPTIMIZED prompt for one batch."""
prompt = f"""Reformat into {len(mcqs)} MCQs and {len(theories)} theory questions. Be CONCISE.
MCQs ({len(mcqs)} total):
{chr(10).join(f'{i+1}. {q}' for i, q in enumerate(mcqs))}
Theory ({len(theories)} total):
{chr(10).join(f'{i+1}. {q}' for i, q in enumerate(theories))}
FORMAT (NO extra text):
MCQ 1
Stem: [question]
Key: [correct answer]
Distractors:
- [wrong 1]
- [wrong 2]
- [wrong 3]
Theory 1
Question: [question]
Answer: [2-3 sentences]
Continue for all questions. NO commentary.
"""
return prompt
def process_batch(batch_data):
"""Process one batch through Gemini with SPEED optimization."""
batch_num, mcqs, theories = batch_data
try:
logger.info(f"Processing batch {batch_num}: {len(mcqs)} MCQs, {len(theories)} theories")
prompt = build_batch_prompt(mcqs, theories, batch_num)
# SPEED OPTIMIZED: Lower temperature, higher top_p, reduced tokens
response = model.generate_content(
prompt,
generation_config={
'temperature': 0.5, # REDUCED from 0.7 for faster, more deterministic output
'top_p': 0.9, # REDUCED from 0.95
'max_output_tokens': 3072, # REDUCED from 4096
},
request_options={'timeout': 30} # 30 second timeout per batch
)
text = getattr(response, "text", "").strip()
if not text:
raise ValueError(f"Empty response from Gemini for batch {batch_num}")
# Parse the structured output
mcqs_parsed, theories_parsed = parse_restructured_text(text)
logger.info(f"Batch {batch_num} complete: {len(mcqs_parsed)} MCQs, {len(theories_parsed)} theories")
return mcqs_parsed, theories_parsed
except Exception as e:
logger.error(f"Batch {batch_num} failed: {str(e)}")
raise
def restructure_questions_parallel(generated_mcqs, generated_theory):
"""
OPTIMIZED: Restructure 40 MCQs + 20 Theory questions faster.
Uses 2 parallel batches with optimized Gemini settings.
Target time: 20-30 seconds (down from 60+ seconds)
"""
if len(generated_mcqs) < 40:
logger.warning(f"Expected 40 MCQs, got {len(generated_mcqs)}")
if len(generated_theory) < 20:
logger.warning(f"Expected 20 theories, got {len(generated_theory)}")
# Ensure we have exactly 40 MCQs and 20 theories
generated_mcqs = generated_mcqs[:40]
generated_theory = generated_theory[:20]
# Split into 2 batches
batches = [
(1, generated_mcqs[:20], generated_theory[:10]),
(2, generated_mcqs[20:40], generated_theory[10:20]),
]
final_mcqs = []
final_theory = []
logger.info("Starting parallel Gemini processing (2 batches)")
try:
# OPTIMIZED: Process both batches in parallel with 60s total timeout
with ThreadPoolExecutor(max_workers=2) as executor:
future_to_batch = {
executor.submit(process_batch, batch): batch[0]
for batch in batches
}
for future in as_completed(future_to_batch, timeout=60): # REDUCED from 120s
batch_num = future_to_batch[future]
try:
mcqs, theories = future.result()
final_mcqs.extend(mcqs)
final_theory.extend(theories)
logger.info(f"Collected batch {batch_num} results")
except Exception as e:
logger.error(f"Failed to collect batch {batch_num}: {str(e)}")
raise
except TimeoutError:
raise RuntimeError("Gemini processing timeout after 60 seconds")
logger.info(f"Complete: {len(final_mcqs)} MCQs, {len(final_theory)} theories")
return {
"mcqs": final_mcqs,
"theory": final_theory
}