Spaces:
Sleeping
Sleeping
File size: 4,781 Bytes
87112c5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 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
} |