Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import io | |
| import re | |
| import time | |
| import json | |
| import random | |
| import builtins | |
| import subprocess | |
| import concurrent.futures | |
| import google.generativeai as genai | |
| from datasets import load_dataset | |
| from google.api_core import exceptions as google_exceptions | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY_PS") | |
| if not GEMINI_API_KEY: | |
| print("β ERROR: GEMINI_API_KEY_PS is missing!") | |
| else: | |
| GEMINI_API_KEY = GEMINI_API_KEY.strip().strip('"').strip("'") | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| model = genai.GenerativeModel('gemini-2.5-flash') | |
| LEVEL_MAP = { | |
| "Fresh": "A", | |
| "Junior": "B", | |
| "Senior": "C" | |
| } | |
| # --------------------------------------------------------- | |
| # 2. HELPER: SAFE API CALL WITH RATE LIMIT HANDLING | |
| # --------------------------------------------------------- | |
| def generate_content_safe(prompt): | |
| retries = 0 | |
| max_retries = 7 | |
| while retries < max_retries: | |
| try: | |
| return model.generate_content(prompt) | |
| except google_exceptions.TooManyRequests: | |
| wait = min(60 * (2 ** retries), 300) | |
| print(f"β οΈ Rate Limit Hit. Cooling down for {wait}s... ({retries+1}/{max_retries})") | |
| time.sleep(wait) | |
| retries += 1 | |
| except Exception as e: | |
| err = str(e) | |
| if "429" in err or "resource_exhausted" in err.lower(): | |
| wait = min(60 * (2 ** retries), 300) | |
| print(f"β οΈ Rate Limit (429). Cooling down for {wait}s... ({retries+1}/{max_retries})") | |
| time.sleep(wait) | |
| retries += 1 | |
| elif "503" in err or "service unavailable" in err.lower(): | |
| wait = (2 ** retries) * 5 | |
| print(f"β οΈ Server Busy (503). Waiting {wait}s... ({retries+1}/{max_retries})") | |
| time.sleep(wait) | |
| retries += 1 | |
| else: | |
| print(f"β API Error: {e}") | |
| return None | |
| return None | |
| # --------------------------------------------------------- | |
| # 3. TIMEOUT RUNNER | |
| # --------------------------------------------------------- | |
| def run_func_with_timeout(func, *args, timeout_sec=2.0): | |
| with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: | |
| future = executor.submit(func, *args) | |
| try: | |
| return future.result(timeout=timeout_sec) | |
| except concurrent.futures.TimeoutError: | |
| raise TimeoutError("Execution timed out.") | |
| # --------------------------------------------------------- | |
| # 4. ORACLE RUNNER (runs original dataset solution as ground truth) | |
| # --------------------------------------------------------- | |
| def run_oracle_subprocess(code_str, input_str): | |
| """Fallback: run original solution as real subprocess.""" | |
| try: | |
| proc = subprocess.run( | |
| [sys.executable, "-c", code_str], | |
| input=input_str, | |
| capture_output=True, | |
| text=True, | |
| timeout=5.0 | |
| ) | |
| if proc.returncode != 0: | |
| return f"ORACLE_ERROR: {proc.stderr.strip()}" | |
| return proc.stdout.strip() | |
| except subprocess.TimeoutExpired: | |
| return "ORACLE_ERROR: subprocess timeout" | |
| except Exception as e: | |
| return f"ORACLE_ERROR: {e}" | |
| def create_oracle_runner(code_str): | |
| """ | |
| Creates a callable that runs the original dataset solution | |
| against any input string and returns its output. | |
| """ | |
| if not code_str or not str(code_str).strip(): | |
| return None, "No original solution provided." | |
| try: | |
| compiled_code = compile(str(code_str), '<oracle>', 'exec') | |
| except Exception as e: | |
| return None, f"Failed to compile oracle: {e}" | |
| def run_oracle(input_str): | |
| input_buffer = io.StringIO(input_str) | |
| output_buffer = io.StringIO() | |
| class FakeBuffer: | |
| def read(self): | |
| return input_str.encode('utf-8') | |
| def readline(self): | |
| return input_buffer.readline().encode('utf-8') | |
| def fileno(self): | |
| raise io.UnsupportedOperation("fileno") | |
| def mocked_input(prompt=""): | |
| line = input_buffer.readline() | |
| if not line: | |
| raise EOFError("Oracle read more input than provided.") | |
| return line.strip('\n') | |
| old_stdin = sys.stdin | |
| old_stdout = sys.stdout | |
| sys.stdin = input_buffer | |
| sys.stdin.buffer = FakeBuffer() | |
| sys.stdout = output_buffer | |
| namespace = { | |
| "__builtins__": builtins, | |
| "__name__": "__main__", | |
| "sys": sys, | |
| "io": io, | |
| "math": __import__('math'), | |
| "input": mocked_input, | |
| "raw_input": mocked_input, | |
| "exit": lambda *a: (_ for _ in ()).throw(SystemExit(0)), | |
| "quit": lambda *a: (_ for _ in ()).throw(SystemExit(0)), | |
| } | |
| try: | |
| exec(compiled_code, namespace) | |
| return output_buffer.getvalue().strip() | |
| except SystemExit: | |
| return output_buffer.getvalue().strip() | |
| except io.UnsupportedOperation: | |
| print(" β οΈ Oracle needs real stdin β switching to subprocess fallback.") | |
| return run_oracle_subprocess(str(code_str), input_str) | |
| except EOFError as e: | |
| return f"ORACLE_ERROR: {e}" | |
| except Exception as e: | |
| return f"ORACLE_ERROR: {e}" | |
| finally: | |
| sys.stdin = old_stdin | |
| sys.stdout = old_stdout | |
| return run_oracle, "Success" | |
| # --------------------------------------------------------- | |
| # 5. FETCH SEED PROBLEM FROM DEEPMIND/CODE_CONTESTS | |
| def get_random_seed_problem(target_difficulty_char): | |
| print(f"π Searching for a Level '{target_difficulty_char}' seed from DeepMind/code_contests...") | |
| try: | |
| # Load the stream INSIDE the function so it doesn't block server startup | |
| dataset = load_dataset("deepmind/code_contests", split="train", streaming=True) | |
| # Small buffer (50) so it starts instantly | |
| shuffled = dataset.shuffle(seed=random.randint(0, 100000), buffer_size=50) | |
| iterator = iter(shuffled) | |
| attempts = 0 | |
| max_attempts = 2000 | |
| while attempts < max_attempts: | |
| try: | |
| p = next(iterator) | |
| attempts += 1 | |
| # ββ Filter 1: Codeforces source only | |
| if p.get('source') != 2: | |
| continue | |
| # ββ Filter 2: Correct difficulty index | |
| if p.get('cf_index', '') != target_difficulty_char: | |
| continue | |
| # ββ Filter 3: Description long enough | |
| desc = p.get('description', '') | |
| if len(desc) < 300: | |
| continue | |
| # ββ Filter 4: Skip image-based problems | |
| if '<image>' in desc.lower(): | |
| continue | |
| # ββ Filter 5: Must have at least one Python solution (FIXED) | |
| solutions = p.get('solutions', {}) | |
| original_solution = None | |
| # Codeforces IDs for Python: 3 (Py3), 2 (Py2), 31 (Py3.4), 41 (PyPy3), 70 (PyPy3.7) | |
| python_ids = {2, 3, 31, 41, 70} | |
| if isinstance(solutions, dict) and 'language' in solutions and 'solution' in solutions: | |
| for lang_id, sol_code in zip(solutions['language'], solutions['solution']): | |
| if lang_id in python_ids: | |
| original_solution = sol_code | |
| break | |
| elif isinstance(solutions, list): | |
| for sol in solutions: | |
| if isinstance(sol, dict) and sol.get('language') in python_ids: | |
| original_solution = sol.get('solution', '') | |
| break | |
| if not original_solution: | |
| continue | |
| print(f"β Found Seed: {p['name']} (Difficulty: {target_difficulty_char}) after {attempts} attempts") | |
| return { | |
| "name": p['name'], | |
| "description": desc, | |
| "solution": original_solution, | |
| } | |
| except StopIteration: | |
| break | |
| except Exception: | |
| continue | |
| except Exception as e: | |
| print(f"β Dataset Error: {e}") | |
| print(f"β Could not find a valid seed.") | |
| return None | |
| # --------------------------------------------------------- | |
| # 6. REWRITE PROBLEM STORY WITH GEMINI | |
| # --------------------------------------------------------- | |
| def generate_problem_data(seed): | |
| """ | |
| Sends the original problem + solution to Gemini. | |
| Gemini rewrites the story (theme, names, context) but keeps the | |
| exact same algorithmic logic and I/O format. | |
| """ | |
| print(f"βοΈ Rewriting story for: '{seed['name']}'...") | |
| prompt = f""" | |
| You are a Competitive Programming Problem Setter. | |
| Below is an original problem and its verified correct solution. | |
| Your job is to create a BRAND NEW problem by changing ONLY the story/theme β | |
| keep the exact same algorithmic logic, input/output format, and constraints. | |
| === ORIGINAL PROBLEM === | |
| {seed['description'][:2000]} | |
| === ORIGINAL SOLUTION (keep the logic identical) === | |
| ```python | |
| {seed['solution'][:1500]} | |
| ``` | |
| INSTRUCTIONS: | |
| 1. Change the background story completely (e.g. space, magic, robots, cooking). | |
| 2. Keep every constraint, formula, and edge case exactly the same. | |
| 3. Keep the input/output format exactly the same (same number of lines, same types). | |
| 4. Do NOT reveal the algorithm name in the problem text. | |
| Output STRICTLY in this format (no extra text outside tags): | |
| [TITLE_START]...[TITLE_END] | |
| [DESC_START]...[DESC_END] | |
| [INPUT_START]...[INPUT_END] | |
| [OUTPUT_START]...[OUTPUT_END] | |
| """ | |
| response = generate_content_safe(prompt) | |
| if not response: | |
| return None | |
| text = response.text | |
| try: | |
| return { | |
| "title": text.split("[TITLE_START]")[1].split("[TITLE_END]")[0].strip(), | |
| "description": text.split("[DESC_START]")[1].split("[DESC_END]")[0].strip(), | |
| "input_fmt": text.split("[INPUT_START]")[1].split("[INPUT_END]")[0].strip(), | |
| "output_fmt": text.split("[OUTPUT_START]")[1].split("[OUTPUT_END]")[0].strip(), | |
| } | |
| except Exception: | |
| print("β οΈ Failed to parse rewritten problem format.") | |
| return None | |
| # --------------------------------------------------------- | |
| # 7. GENERATE CODE COMPONENTS (with oracle hint + error feedback) | |
| # --------------------------------------------------------- | |
| def generate_code_components(problem_data, original_solution="", previous_error=""): | |
| """ | |
| Asks Gemini to generate: | |
| solve_optimized, solve_brute, | |
| generate_small_test, generate_large_test, generate_edge_test, | |
| validate_output | |
| Using the original solution as the logic source of truth. | |
| """ | |
| print("βοΈ Generating Logic Components (Python)...") | |
| oracle_hint = "" | |
| if original_solution and original_solution.strip(): | |
| oracle_hint = f""" | |
| === REFERENCE SOLUTION (YOUR LOGIC SOURCE OF TRUTH) === | |
| The following is the verified correct solution from the dataset. | |
| Your `solve_optimized` AND `solve_brute` MUST produce EXACTLY the same | |
| output as this code for every possible input. | |
| Do NOT invent new logic. Only adapt the I/O to accept a plain string argument. | |
| ```python | |
| {original_solution} | |
| ``` | |
| ======================================================== | |
| """ | |
| error_hint = "" | |
| if previous_error: | |
| error_hint = f""" | |
| === PREVIOUS ATTEMPT FAILED β FIX THIS === | |
| The last generated code failed with: | |
| \"\"\"{previous_error}\"\"\" | |
| Common causes: | |
| - solve_optimized returned a different value than the oracle on some edge case. | |
| - Integer division done with / instead of //. | |
| - A string literal like "No"/"Yes"/"Impossible" was changed. | |
| - generate_small_test or generate_edge_test produced malformed input. | |
| - This is a constructive problem β make sure validate_output checks ALL problem | |
| rules, not just one specific answer. | |
| Study the error carefully and fix the root cause. | |
| ========================================== | |
| """ | |
| prompt = f""" | |
| Problem Title: {problem_data['title']} | |
| Description: {problem_data['description']} | |
| Input Format: {problem_data['input_fmt']} | |
| {oracle_hint} | |
| {error_hint} | |
| Generate a SINGLE Python code block with these 6 functions: | |
| 1. `def solve_optimized(input_str):` | |
| - Efficient O(N) or O(N log N) solution. | |
| - Read from `input_str` argument (NOT stdin). | |
| 2. `def solve_brute(input_str):` | |
| - Simple correct brute-force for verification. | |
| - Read from `input_str` argument (NOT stdin). | |
| === CRITICAL RULES FOR TEST GENERATORS === | |
| - DATA INTEGRITY: If format requires N then N integers, generate EXACTLY N integers. | |
| - TYPE STRICTNESS: If format requires integers, do not generate floats or letters. | |
| - FORMAT EXACTNESS: Use \\n to separate lines exactly as the Input Format specifies. | |
| Do NOT add trailing newlines or extra spaces at the end. | |
| ========================================== | |
| 3. `def generate_small_test():` | |
| - Returns a RANDOM input string based on the format. N = 1 to 10. | |
| - MUST use `random` so every call produces a DIFFERENT output. | |
| 4. `def generate_large_test():` | |
| - Returns a RANDOM input string. N = 100 to 1000. | |
| - MUST use `random` module to ensure uniqueness. | |
| 5. `def generate_edge_test():` | |
| - Returns a RANDOM edge case input string. | |
| - Examples: N=1, all identical elements, minimum possible values. | |
| - Keep N VERY SMALL (N < 20) to prevent brute-force from freezing. | |
| - Use `random.choice` to pick between different edge case types. | |
| 6. `def validate_output(input_str, output_str):` | |
| - Many problems have MULTIPLE valid answers (constructive problems). | |
| - Check whether output_str is ANY valid answer per the problem rules. | |
| - Parse input_str and output_str, verify ALL constraints. | |
| - Return True if valid, False otherwise. | |
| - For single-answer problems a basic sanity check is fine. | |
| IMPORTANT: | |
| - Import `random` inside the code block. | |
| - Do NOT use external libraries (like numpy). | |
| - Wrap the entire code in ONE ```python``` block. | |
| """ | |
| response = generate_content_safe(prompt) | |
| if not response: | |
| return None | |
| text = response.text | |
| match = re.search(r"```python(.*?)```", text, re.DOTALL) | |
| return match.group(1).strip() if match else text.strip() | |
| # --------------------------------------------------------- | |
| # 8. VERIFY & JUDGE (oracle-based) | |
| # --------------------------------------------------------- | |
| def verify_and_judge(code_block, original_solution=""): | |
| """ | |
| Verifies the generated logic against the original solution (oracle). | |
| Returns (is_good, status_message, namespace, hidden_tests). | |
| """ | |
| print("π¬ Running Verification Loop (Oracle + Brute Force)...") | |
| oracle_runner, oracle_status = create_oracle_runner(original_solution) | |
| if not oracle_runner: | |
| print(f" β οΈ No oracle available ({oracle_status}). Falling back to brute-only check.") | |
| namespace = {} | |
| try: | |
| exec(code_block, namespace) | |
| solve_opt = namespace.get('solve_optimized') | |
| solve_brute = namespace.get('solve_brute') | |
| gen_small = namespace.get('generate_small_test') | |
| gen_large = namespace.get('generate_large_test') | |
| gen_edge = namespace.get('generate_edge_test') | |
| validate_fn = namespace.get('validate_output') | |
| if not all([solve_opt, solve_brute, gen_small, gen_large, gen_edge]): | |
| return False, "Missing required functions in generated code.", None, [] | |
| hidden_tests = [] | |
| seen_inputs = set() | |
| # ββ Token-level comparison (handles float answers) | |
| def compare_tokens(a, b): | |
| ta, tb = a.split(), b.split() | |
| if len(ta) != len(tb): | |
| return False | |
| for x, y in zip(ta, tb): | |
| try: | |
| if abs(float(x) - float(y)) > 1e-6: | |
| return False | |
| except ValueError: | |
| if x != y: | |
| return False | |
| return True | |
| def is_correct(inp, candidate, oracle_out): | |
| """True if candidate is a valid answer for inp.""" | |
| if compare_tokens(candidate, oracle_out): | |
| return True | |
| if validate_fn: | |
| try: | |
| return bool(run_func_with_timeout(validate_fn, inp, candidate, timeout_sec=2.0)) | |
| except Exception: | |
| return False | |
| return False | |
| def test_one(inp, check_brute=True): | |
| try: | |
| out_opt = str(run_func_with_timeout(solve_opt, inp, timeout_sec=2.0)).strip() | |
| except TimeoutError: | |
| return False, "TIMEOUT: solve_optimized", "" | |
| # ββ Compare against oracle if available | |
| if oracle_runner: | |
| try: | |
| out_oracle = str(run_func_with_timeout(oracle_runner, inp, timeout_sec=5.0)).strip() | |
| except TimeoutError: | |
| return False, "TIMEOUT: oracle", "" | |
| if "ORACLE_ERROR" in out_oracle: | |
| return False, f"Oracle crashed: {out_oracle}", "" | |
| if not is_correct(inp, out_opt, out_oracle): | |
| preview = inp[:300].replace('\n', '\\n') | |
| return False, ( | |
| f"Mismatch vs Oracle!\n" | |
| f" Input : {preview}\n" | |
| f" Oracle : {out_oracle[:120]}\n" | |
| f" Optimized: {out_opt[:120]}" | |
| ), out_oracle # return oracle output as expected | |
| expected = out_oracle # oracle is ground truth | |
| else: | |
| expected = out_opt # no oracle β use optimized as reference | |
| # ββ Also check brute force matches | |
| if check_brute: | |
| try: | |
| out_brute = str(run_func_with_timeout(solve_brute, inp, timeout_sec=4.0)).strip() | |
| except TimeoutError: | |
| return False, "TIMEOUT: solve_brute", "" | |
| if not is_correct(inp, out_brute, expected): | |
| preview = inp[:300].replace('\n', '\\n') | |
| return False, ( | |
| f"Brute Mismatch!\n" | |
| f" Input : {preview}\n" | |
| f" Expected : {expected[:120]}\n" | |
| f" Brute : {out_brute[:120]}" | |
| ), "" | |
| return True, "Passed", expected | |
| # ββ Test generation plan: (generator_fn, count, check_brute?) | |
| print(" Generating tests (Small x5, Large x5, Edge x3)...") | |
| plan = [ | |
| (gen_small, 5, True), | |
| (gen_large, 5, False), | |
| (gen_edge, 3, True), | |
| ] | |
| for gen_fn, target_count, check_b in plan: | |
| collected = 0 | |
| attempts = 0 | |
| while collected < target_count and attempts < 40: | |
| attempts += 1 | |
| try: | |
| inp = str(run_func_with_timeout(gen_fn, timeout_sec=2.0)).strip() | |
| if not inp or inp in seen_inputs: | |
| continue | |
| passed, msg, expected = test_one(inp, check_brute=check_b) | |
| if not passed: | |
| return False, msg, None, [] | |
| seen_inputs.add(inp) | |
| hidden_tests.append({ | |
| "id": len(hidden_tests), | |
| "input": inp, | |
| "expected_output": expected | |
| }) | |
| collected += 1 | |
| except Exception: | |
| continue | |
| print(f" β Total Tests Verified: {len(hidden_tests)}") | |
| return True, "Verified", namespace, hidden_tests | |
| except Exception as e: | |
| return False, f"Exec Error: {e}", None, [] | |
| # --------------------------------------------------------- | |
| # 9. GENERATE SAMPLE TESTS (visible to user) | |
| # --------------------------------------------------------- | |
| def create_sample_tests(namespace): | |
| """Creates 3 visible sample tests from the small test generator.""" | |
| samples = [] | |
| seen = set() | |
| gen_fn = namespace.get('generate_small_test') | |
| solve_fn = namespace.get('solve_optimized') | |
| if not gen_fn or not solve_fn: | |
| return samples | |
| print(" Generating 3 sample tests...") | |
| tries = 0 | |
| while len(samples) < 3 and tries < 25: | |
| tries += 1 | |
| try: | |
| inp = str(gen_fn()).strip() | |
| if inp in seen: | |
| continue | |
| out = str(solve_fn(inp)).strip() | |
| seen.add(inp) | |
| samples.append({"input": inp, "output": out}) | |
| except Exception: | |
| continue | |
| return samples | |
| # --------------------------------------------------------- | |
| # 10. GENERATE TARGET SOLUTIONS (Python, C++, JS) | |
| # --------------------------------------------------------- | |
| def generate_target_solution(problem_data, verified_python_code): | |
| """ | |
| Generates clean, runnable solutions for Python 3, C++, and JavaScript | |
| from the verified internal logic. | |
| """ | |
| print("π Generating Target Solutions (Python / C++ / JS)...") | |
| prompt = f""" | |
| I have a competitive programming problem and its verified internal logic. | |
| Write a clean, optimal, STANDALONE solution in Python 3, C++, and JavaScript (Node.js). | |
| PROBLEM DESCRIPTION: | |
| {problem_data['description']} | |
| INPUT FORMAT: {problem_data['input_fmt']} | |
| OUTPUT FORMAT: {problem_data['output_fmt']} | |
| INTERNAL LOGIC (SOURCE OF TRUTH β translate this exactly): | |
| {verified_python_code} | |
| CRITICAL TRANSLATION RULES: | |
| 1. EXACT LOGIC: 1:1 translation of `solve_optimized`. Do NOT invent new logic. | |
| 2. EXACT OUTPUT: Look at string literals in `solve_optimized` (e.g. "No collision", | |
| "Impossible"). ALL languages MUST print that EXACT string. | |
| 3. EXACT FORMAT: Space-separated numbers stay space-separated. No [] or commas. | |
| 4. OVERFLOW: C++ MUST use `long long`. JS MUST use BigInt if numbers exceed 2^53. | |
| 5. JAVASCRIPT RULES (CRITICAL): | |
| - Integer division: `Math.floor(A / B)` β NEVER plain `A / B`. | |
| - Sorting: `.sort((a, b) => a - b)` β NEVER empty `.sort()`. | |
| - Parsing: `const tokens = input.trim().split(/\\s+/).filter(Boolean);` | |
| 6. INPUT READING: | |
| - JavaScript: `fs.readFileSync(0, 'utf-8')`. | |
| - Python: `sys.stdin.read()`. | |
| 7. CLEANUP: Only the final runnable solution. No test generators, no `import random`. | |
| OUTPUT STRICTLY IN THIS FORMAT (@@@ as separator): | |
| @@@PYTHON@@@ | |
| <python code here> | |
| @@@CPP@@@ | |
| <cpp code here> | |
| @@@JS@@@ | |
| <javascript code here> | |
| """ | |
| response = generate_content_safe(prompt) | |
| solutions = { | |
| "python": verified_python_code, | |
| "cpp": "// C++ solution failed to generate.", | |
| "js": "// JavaScript solution failed to generate." | |
| } | |
| if not response: | |
| return solutions | |
| text = response.text | |
| def clean(raw): | |
| raw = re.sub(r"```[a-zA-Z+]*\n?", "", raw) | |
| return re.sub(r"```", "", raw).strip() | |
| py_m = re.search(r"@@@\s*PYTHON\s*@@@(.*?)(?=@@@\s*(?:CPP|JS)\s*@@@|$)", text, re.DOTALL | re.IGNORECASE) | |
| cpp_m = re.search(r"@@@\s*CPP\s*@@@(.*?)(?=@@@\s*(?:JS|PYTHON)\s*@@@|$)", text, re.DOTALL | re.IGNORECASE) | |
| js_m = re.search(r"@@@\s*JS\s*@@@(.*?)(?=@@@\s*(?:PYTHON|CPP)\s*@@@|$)", text, re.DOTALL | re.IGNORECASE) | |
| if py_m: solutions["python"] = clean(py_m.group(1)) | |
| if cpp_m: solutions["cpp"] = clean(cpp_m.group(1)) | |
| if js_m: solutions["js"] = clean(js_m.group(1)) | |
| if not any([py_m, cpp_m, js_m]): | |
| print("β οΈ generate_target_solution: No @@@ markers found in response.") | |
| return solutions | |
| # --------------------------------------------------------- | |
| # 11. API ENTRY POINT | |
| # --------------------------------------------------------- | |
| def generate_problem_for_api(level: str, lang: str) -> dict | None: | |
| """ | |
| Main function called by FastAPI. | |
| Returns a complete problem dictionary or None on failure. | |
| """ | |
| target_char = LEVEL_MAP.get(level, "A") | |
| # ββ Step 1: Fetch seed from DeepMind dataset | |
| seed = get_random_seed_problem(target_char) | |
| if not seed: | |
| print("β Could not fetch a valid seed problem.") | |
| return None | |
| # ββ Step 2: Rewrite story with Gemini | |
| new_prob = generate_problem_data(seed) | |
| if not new_prob: | |
| print("β Story rewrite failed.") | |
| return None | |
| # ββ Step 3: Generate & verify logic with retry loop | |
| MAX_ATTEMPTS = 3 | |
| last_error = "" | |
| code_block = None | |
| namespace = None | |
| hidden_tests = [] | |
| is_valid = False | |
| for attempt in range(MAX_ATTEMPTS): | |
| print(f"\nπ Code generation attempt {attempt + 1}/{MAX_ATTEMPTS}...") | |
| code_block = generate_code_components( | |
| new_prob, | |
| original_solution=seed['solution'], | |
| previous_error=last_error | |
| ) | |
| if not code_block: | |
| last_error = "generate_code_components returned None (API error)." | |
| print(f" β οΈ Empty code block β waiting 15s before retry...") | |
| time.sleep(15) | |
| continue | |
| is_good, status, namespace, hidden_tests = verify_and_judge( | |
| code_block, original_solution=seed['solution'] | |
| ) | |
| if is_good: | |
| is_valid = True | |
| print(f" β Verified on attempt {attempt + 1}.") | |
| break | |
| last_error = status | |
| print(f" β Attempt {attempt + 1} failed: {status[:200]}") | |
| if attempt < MAX_ATTEMPTS - 1: | |
| time.sleep(5) | |
| if not is_valid: | |
| print(f"β All {MAX_ATTEMPTS} attempts failed. Aborting.") | |
| return None | |
| # ββ Step 4: Generate target solutions | |
| target_sols = generate_target_solution(new_prob, code_block) | |
| # ββ Step 5: Build and return result | |
| return { | |
| "title": new_prob['title'], | |
| "description": new_prob['description'], | |
| "input_format": new_prob['input_fmt'], | |
| "output_format": new_prob['output_fmt'], | |
| "samples": create_sample_tests(namespace), | |
| "test_cases": hidden_tests, | |
| "solution_code": target_sols, # dict: {python, cpp, js} | |
| } |