Spaces:
Sleeping
Sleeping
| # core/branch_executor.py | |
| """ | |
| Exécuteur de branches – isole chaque hypothèse dans un sandbox. | |
| Si le code est vide, évalue la description sémantiquement. | |
| """ | |
| import logging | |
| import time | |
| import asyncio | |
| from typing import Dict, Any, Optional | |
| from concurrent.futures import ThreadPoolExecutor | |
| from sandbox.secure_executor import execute_safe | |
| from core.hypothesis_tree import HypothesisTree, Branch, BranchStatus | |
| logger = logging.getLogger("vortex.branch_executor") | |
| class BranchExecutor: | |
| """ | |
| Exécute une branche (hypothèse) dans un sandbox isolé. | |
| Retourne le résultat, le score et les éventuelles erreurs. | |
| """ | |
| def __init__(self, timeout: int = 30): | |
| self.timeout = timeout | |
| self.executor = ThreadPoolExecutor(max_workers=4) | |
| async def execute_branch(self, branch: Branch, tree: HypothesisTree) -> Dict: | |
| """ | |
| Exécute une branche et met à jour l'arbre. | |
| Retourne un dict avec le résultat. | |
| """ | |
| branch_id = branch.id | |
| tree.update_branch(branch_id, BranchStatus.RUNNING) | |
| # Si le code est vide, évaluer uniquement la description | |
| if not branch.code.strip(): | |
| score = self._evaluate_hypothesis_text(branch.hypothesis, tree.task) | |
| tree.update_branch( | |
| branch_id, | |
| BranchStatus.SUCCESS, | |
| score=score, | |
| result={"evaluation": "descriptive", "score": score}, | |
| execution_time=0.0 | |
| ) | |
| return {"branch_id": branch_id, "success": True, "score": score, "result": "Évalué par description"} | |
| # Sinon, exécuter le code dans le sandbox | |
| t0 = time.time() | |
| try: | |
| result = await asyncio.get_event_loop().run_in_executor( | |
| self.executor, | |
| execute_safe, | |
| branch.code, | |
| self.timeout | |
| ) | |
| execution_time = time.time() - t0 | |
| # Évaluation du résultat | |
| score = self._evaluate_result(result, branch.hypothesis) | |
| tree.update_branch( | |
| branch_id, | |
| BranchStatus.SUCCESS, | |
| score=score, | |
| result=result, | |
| execution_time=execution_time | |
| ) | |
| return {"branch_id": branch_id, "success": True, "score": score, "result": result} | |
| except Exception as e: | |
| execution_time = time.time() - t0 | |
| error_msg = str(e) | |
| tree.update_branch( | |
| branch_id, | |
| BranchStatus.FAILED, | |
| score=0.0, | |
| error=error_msg, | |
| execution_time=execution_time | |
| ) | |
| return {"branch_id": branch_id, "success": False, "error": error_msg} | |
| def _evaluate_hypothesis_text(self, text: str, task: str) -> float: | |
| """ | |
| Évalue une hypothèse basée sur le texte seul (pas de code exécuté). | |
| Critères : pertinence, couverture des mots-clés, longueur. | |
| """ | |
| score = 0.3 # base | |
| text_lower = text.lower() | |
| task_lower = task.lower() | |
| # Mots-clés importants pour la tâche actuelle (peut être adapté) | |
| keywords = [ | |
| "compétences", "skill", "json", "visualisation", "arbre", "cerveau", | |
| "three", "vis", "chromadb", "injection", "prompt", "structure", | |
| "validation", "pydantic", "schema", "plugin", "module" | |
| ] | |
| matched = sum(1 for kw in keywords if kw in text_lower) | |
| score += min(0.4, matched * 0.08) | |
| # Longueur (bonus si > 200 caractères) | |
| if len(text) > 200: | |
| score += 0.15 | |
| if len(text) > 500: | |
| score += 0.15 | |
| # Pertinence par rapport à la tâche (mots communs) | |
| common = set(task_lower.split()) & set(text_lower.split()) | |
| if common: | |
| score += min(0.2, len(common) * 0.05) | |
| return min(1.0, round(score, 3)) | |
| def _evaluate_result(self, result: Any, hypothesis: str) -> float: | |
| """ | |
| Heuristique d'évaluation d'un résultat (0‑1) lorsque le code a été exécuté. | |
| """ | |
| if result is None: | |
| return 0.0 | |
| if isinstance(result, dict): | |
| if "result" in result or "output" in result or "success" in result: | |
| if result.get("success", False): | |
| return 0.8 | |
| return 0.3 | |
| if isinstance(result, str): | |
| if len(result) > 100: | |
| return 0.7 | |
| return 0.4 | |
| return 0.5 |