Spaces:
Sleeping
Sleeping
| """ | |
| Validação prévia estruturada de código gerado. | |
| Verifica padrões básicos antes de executar ou chamar API novamente. | |
| """ | |
| import ast | |
| import re | |
| from typing import Tuple, List, Dict, Optional | |
| class CodeValidator: | |
| """Valida estrutura básica do código gerado""" | |
| def __init__(self): | |
| self.errors = [] | |
| self.warnings = [] | |
| def validate(self, code: str, task_description: Optional[str] = None) -> Tuple[bool, List[str], List[str]]: | |
| """ | |
| Valida código e retorna (is_valid, errors, warnings) | |
| Args: | |
| code: Código Python a ser validado | |
| task_description: Descrição da tarefa para validar quantidades | |
| Returns: | |
| (is_valid, errors, warnings) | |
| """ | |
| self.errors = [] | |
| self.warnings = [] | |
| # 1. Validação de sintaxe básica | |
| if not self._validate_syntax(code): | |
| return False, self.errors, self.warnings | |
| # 2. Validação de estrutura de classe | |
| self._validate_class_structure(code) | |
| # 3. Validação de imports | |
| self._validate_imports(code) | |
| # 4. Validação de padrões de código | |
| self._validate_code_patterns(code) | |
| # 5. Validação de quantidade (se task_description fornecido) | |
| if task_description: | |
| self._validate_quantity(code, task_description) | |
| # Se há erros críticos, código é inválido | |
| critical_errors = [e for e in self.errors if 'CRITICAL' in e or 'ERROR' in e] | |
| is_valid = len(critical_errors) == 0 | |
| return is_valid, self.errors, self.warnings | |
| def _validate_syntax(self, code: str) -> bool: | |
| """Valida sintaxe Python básica""" | |
| try: | |
| ast.parse(code) | |
| return True | |
| except SyntaxError as e: | |
| self.errors.append(f"CRITICAL: SyntaxError - {str(e)}") | |
| return False | |
| def _validate_class_structure(self, code: str): | |
| """Valida estrutura da classe Task""" | |
| tree = ast.parse(code) | |
| has_class = False | |
| has_init = False | |
| has_additional_reset = False | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.ClassDef): | |
| has_class = True | |
| # Verificar se herda de Task | |
| for base in node.bases: | |
| if isinstance(base, ast.Name) and base.id == 'Task': | |
| break | |
| else: | |
| self.errors.append("CRITICAL: Class must inherit from Task") | |
| # Verificar métodos | |
| for item in node.body: | |
| if isinstance(item, ast.FunctionDef): | |
| if item.name == '__init__': | |
| has_init = True | |
| # Verificar se chama additional_reset() | |
| for stmt in ast.walk(item): | |
| if isinstance(stmt, ast.Call): | |
| if isinstance(stmt.func, ast.Attribute): | |
| if stmt.func.attr == 'additional_reset': | |
| has_additional_reset = True | |
| if not has_class: | |
| self.errors.append("CRITICAL: No class definition found") | |
| if not has_init: | |
| self.errors.append("CRITICAL: No __init__ method found (not 'init')") | |
| if has_init and not has_additional_reset: | |
| self.warnings.append("WARNING: __init__ should call self.additional_reset()") | |
| def _validate_imports(self, code: str): | |
| """Valida imports duplicados""" | |
| import_lines = [] | |
| seen_imports = set() | |
| for line in code.split('\n'): | |
| stripped = line.strip() | |
| if stripped.startswith('import ') or stripped.startswith('from '): | |
| import_lines.append(stripped) | |
| # Normalizar para detectar duplicatas | |
| normalized = re.sub(r'\s+', ' ', stripped) | |
| if normalized in seen_imports: | |
| self.warnings.append(f"WARNING: Duplicate import: {stripped}") | |
| seen_imports.add(normalized) | |
| def _validate_code_patterns(self, code: str): | |
| """Valida padrões comuns de código""" | |
| # Verificar base_pose incorreto | |
| if re.search(r'base_pose\s*=\s*\([^)]*0\.\d+[^)]*\)\s*[^,\(]', code): | |
| # Padrão: base_pose = (0.5, 0, 0.02) sem rotação | |
| if not re.search(r'base_pose\s*=\s*\(\([^)]+\)\s*,\s*\([^)]+\)\)', code): | |
| self.errors.append("CRITICAL: base_pose must be ((x,y,z), (qx,qy,qz,qw)), not just (x,y,z)") | |
| # Verificar uso incorreto de utils.apply() | |
| # Deve retornar posição e adicionar rotação | |
| apply_patterns = re.findall(r'utils\.apply\([^)]+\)', code) | |
| for pattern in apply_patterns: | |
| # Verificar se está dentro de uma tupla com rotação | |
| context = code[max(0, code.find(pattern)-50):code.find(pattern)+len(pattern)+50] | |
| if 'base_pose[1]' not in context and '(0, 0, 0, 1)' not in context: | |
| self.warnings.append("WARNING: utils.apply() should be followed by rotation: (utils.apply(...), base_pose[1])") | |
| # Verificar p.getQuaternionFromEuler | |
| if 'p.getQuaternionFromEuler' in code: | |
| self.warnings.append("WARNING: Use (0, 0, 0, 1) instead of p.getQuaternionFromEuler()") | |
| # Verificar loops sem variável | |
| if re.search(r'for\s+in\s+range', code): | |
| self.errors.append("CRITICAL: for loop missing variable: use 'for i in range(n)'") | |
| def _validate_quantity(self, code: str, task_description: str): | |
| """Valida se a quantidade de objetos corresponde à descrição""" | |
| # Extrair número da descrição | |
| numbers = re.findall(r'\b(\d+)\s+(?:blocos?|blocks?|objetos?|objects?|esferas?|spheres?|cilindros?|cylinders?)\b', | |
| task_description.lower()) | |
| if not numbers: | |
| # Tentar padrões alternativos | |
| numbers = re.findall(r'\b(?:uma|um|one|a)\s+(?:fileira|row|linha|line)\s+de\s+(\d+)', | |
| task_description.lower()) | |
| if numbers: | |
| expected_count = int(numbers[0]) | |
| # Contar range() no código | |
| range_matches = re.findall(r'range\((\d+)\)', code) | |
| if range_matches: | |
| actual_count = max([int(m) for m in range_matches]) | |
| if actual_count != expected_count: | |
| self.errors.append( | |
| f"CRITICAL: Task asks for {expected_count} objects, but code creates {actual_count} " | |
| f"(found: range({actual_count}))" | |
| ) | |
| def validate_generated_code(code: str, task_description: Optional[str] = None) -> Tuple[bool, List[str], List[str]]: | |
| """ | |
| Função de conveniência para validar código | |
| Args: | |
| code: Código Python a ser validado | |
| task_description: Descrição da tarefa (opcional) | |
| Returns: | |
| (is_valid, errors, warnings) | |
| """ | |
| validator = CodeValidator() | |
| return validator.validate(code, task_description) | |