Spaces:
Runtime error
Runtime error
| # src/core/ai_debugger.py | |
| import os | |
| import json | |
| from pathlib import Path | |
| from google import genai | |
| from google.genai import types | |
| from src.config import GEMINI_MODEL_NAME | |
| from src.utils.smart_logger import logger | |
| class AIDebugger: | |
| def __init__(self): | |
| self.gemini_client = None | |
| self.model_name = GEMINI_MODEL_NAME | |
| # Don't initialize here - will be initialized when needed | |
| def init_gemini(self): | |
| api_key = os.environ.get("GEMINI_API_KEY") | |
| if not api_key: | |
| logger.quick_log("error", "GEMINI_API_KEY not found") | |
| return | |
| try: | |
| self.gemini_client = genai.Client(api_key=api_key) | |
| logger.quick_log("success", "AI Debugger initialized") | |
| except Exception as e: | |
| logger.error_with_context("Failed to initialize Gemini", str(e)) | |
| def debug_and_fix(self, file_path: str, error_message: str, scene_name: str, project_config: dict) -> str: | |
| """Returns the path to the fixed file, or None if fixing failed""" | |
| # Try to initialize if not already done | |
| if not self.gemini_client: | |
| self.init_gemini() | |
| if not self.gemini_client: | |
| logger.quick_log("error", "AI Debugger not initialized") | |
| return None | |
| logger.start_operation("debug_fix", f"🔧 AI debugging {scene_name}") | |
| try: | |
| with open(file_path, 'r', encoding='utf-8') as f: | |
| original_code = f.read() | |
| except Exception as e: | |
| logger.error_with_context(f"Could not read file {file_path}", str(e)) | |
| return None | |
| prompt = f""" | |
| You are an expert Manim programmer and debugger. Fix this Python code error. | |
| **ERROR MESSAGE:** | |
| {error_message} | |
| **ORIGINAL CODE:** | |
| ```python | |
| {original_code} | |
| ``` | |
| **CRITICAL RULES:** | |
| - NEVER use MathTex, Tex, or LaTeX objects (LaTeX not installed) | |
| - Use Text() for all text including math formulas | |
| - Use only basic Manim shapes: Circle, Rectangle, Square, Triangle, etc. | |
| - For math, use Text("x² + y² = z²") instead of MathTex | |
| - ONLY colors: RED, BLUE, GREEN, YELLOW, WHITE, BLACK, GRAY, ORANGE, PURPLE, PINK | |
| **JSON OUTPUT FORMAT:** | |
| ```json | |
| {{ | |
| "error_pattern": "A unique substring from the error message", | |
| "regex_fix": {{ | |
| "pattern": "The Python regex pattern to find the error", | |
| "replacement": "The Python regex replacement string" | |
| }}, | |
| "fixed_code": "The complete, corrected Python code" | |
| }} | |
| ``` | |
| """ | |
| try: | |
| contents = [ | |
| types.Content( | |
| role="user", | |
| parts=[ | |
| types.Part.from_text(text=prompt), | |
| ], | |
| ), | |
| ] | |
| generate_content_config = types.GenerateContentConfig( | |
| temperature=0.1, | |
| response_mime_type="application/json" | |
| ) | |
| response = self.gemini_client.models.generate_content( | |
| model=self.model_name, | |
| contents=contents, | |
| config=generate_content_config, | |
| ) | |
| response_data = json.loads(response.text) | |
| fixed_code = response_data.get("fixed_code") | |
| regex_fix = response_data.get("regex_fix") | |
| error_pattern = response_data.get("error_pattern") | |
| if not all([fixed_code, regex_fix, error_pattern]): | |
| raise ValueError("AI response missing required fields") | |
| # Create a NEW file with the fixed code (don't overwrite the original) | |
| import tempfile | |
| temp_dir = Path(file_path).parent | |
| with tempfile.NamedTemporaryFile(mode='w+', suffix='.py', delete=False, dir=temp_dir) as fixed_file: | |
| fixed_file.write(fixed_code) | |
| fixed_file_path = fixed_file.name | |
| # Update project config to point to the new fixed file | |
| if scene_name in project_config["scenes"]: | |
| project_config["scenes"][scene_name]["code_file"] = fixed_file_path | |
| logger.end_operation("debug_fix", f"✅ NEW fixed file created: {Path(fixed_file_path).name}") | |
| return fixed_file_path | |
| except Exception as e: | |
| logger.error_with_context("❌ AI Debugger failed", str(e)) | |
| logger.end_operation("debug_fix", "❌ Debug failed") | |
| return None | |