Spaces:
Sleeping
Sleeping
Download question_api/restructure_questions.py from vikkyblacq/flask_api: direct link, hf CLI and curl.
- Browser
- Download file 4.78 kB
-
https://huggingface.co/spaces/vikkyblacq/flask_api/resolve/main/question_api/restructure_questions.py
- Command line
-
hf download hf://spaces/vikkyblacq/flask_api/question_api/restructure_questions.py
-
curl -L -o restructure_questions.py https://huggingface.co/spaces/vikkyblacq/flask_api/resolve/main/question_api/restructure_questions.py
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 | |
| } |