File size: 5,307 Bytes
6fc3143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Code Validator - Validates generated Manim code before rendering
"""

import ast
import re
from typing import List, Tuple, Optional


class CodeValidator:
    """Validates Manim code for common issues"""
    
    def validate(self, code: str) -> Tuple[bool, List[str]]:
        """
        Validate code and return validation result.
        
        Args:
            code: Python code to validate
            
        Returns:
            Tuple of (is_valid, list_of_errors)
        """
        errors = []
        
        # 1. Syntax validation
        syntax_valid, syntax_errors = self._validate_syntax(code)
        if not syntax_valid:
            errors.extend(syntax_errors)
            return (False, errors)  # Can't continue if syntax is invalid
        
        # 2. Import validation
        import_errors = self._validate_imports(code)
        errors.extend(import_errors)
        
        # 3. Structure validation
        structure_errors = self._validate_structure(code)
        errors.extend(structure_errors)
        
        # 4. Variable/color validation
        variable_errors = self._validate_variables(code)
        errors.extend(variable_errors)
        
        # 5. Voiceover validation
        voiceover_errors = self._validate_voiceover(code)
        errors.extend(voiceover_errors)
        
        is_valid = len(errors) == 0
        return (is_valid, errors)
    
    def _validate_syntax(self, code: str) -> Tuple[bool, List[str]]:
        """Validate Python syntax"""
        try:
            ast.parse(code)
            return (True, [])
        except SyntaxError as e:
            return (False, [f"Syntax error at line {e.lineno}: {e.msg}"])
        except Exception as e:
            return (False, [f"Parse error: {str(e)}"])
    
    def _validate_imports(self, code: str) -> List[str]:
        """Check for required imports"""
        errors = []
        
        # Check for manim import
        if 'from manim import *' not in code and 'import manim' not in code:
            errors.append("Missing required import: from manim import *")
        
        # Check for VoiceoverScene import (from our custom implementation)
        if 'from manimator.scene.voiceover_scene import VoiceoverScene' not in code:
            errors.append("Missing required import: from manimator.scene.voiceover_scene import VoiceoverScene")
        
        # Check for voiceover service import
        if 'from manimator.services.voiceover import SimpleElevenLabsService' not in code:
            errors.append("Missing required import: from manimator.services.voiceover import SimpleElevenLabsService")
        
        # Check for pathlib import (needed for cache_dir)
        if 'from pathlib import Path' not in code:
            errors.append("Missing required import: from pathlib import Path")
        
        return errors
    
    def _validate_structure(self, code: str) -> List[str]:
        """Validate code structure (class, construct method, etc.)"""
        errors = []
        
        # Check for Scene class
        if 'class' not in code:
            errors.append("No class definition found")
            return errors
        
        # Check for VoiceoverScene inheritance
        if 'VoiceoverScene' not in code:
            errors.append("Scene class must inherit from VoiceoverScene")
        
        # Check for construct method
        if 'def construct(self):' not in code and 'def construct(self):' not in code:
            errors.append("No construct() method found")
        
        return errors
    
    def _validate_variables(self, code: str) -> List[str]:
        """Check for undefined variables and colors"""
        errors = []
        
        # Check for undefined color constants
        undefined_colors = re.findall(r'\b(ORANGE|RED|BLUE|GREEN|YELLOW|PURPLE|PINK|TEAL|GRAY)_[A-Z]\b', code)
        if undefined_colors:
            errors.append(f"Undefined color constants found: {', '.join(set(undefined_colors))}")
        
        return errors
    
    def _validate_voiceover(self, code: str) -> List[str]:
        """Validate voiceover setup"""
        errors = []
        
        # Check for voiceover service setup
        if 'set_speech_service' not in code:
            errors.append("Voiceover service not initialized (missing set_speech_service)")
        
        # Check for SimpleElevenLabsService (the actual service we use)
        if 'SimpleElevenLabsService' not in code:
            errors.append("SimpleElevenLabsService not imported or used")
        
        # Check for voiceover blocks
        if 'with self.voiceover' not in code:
            errors.append("No voiceover blocks found (use 'with self.voiceover(text=...)' for narration)")
        
        return errors
    
    def get_fixable_errors(self, errors: List[str]) -> List[str]:
        """Identify errors that can be auto-fixed"""
        fixable_patterns = [
            'Missing required import',
            'Undefined color constants',
            'Voiceover service not initialized',
        ]
        
        fixable = []
        for error in errors:
            for pattern in fixable_patterns:
                if pattern in error:
                    fixable.append(error)
                    break
        
        return fixable