Spaces:
Sleeping
Sleeping
Cyril Dupland
Add batch_calculator tool for multiple arithmetic operations and update A3 prompts to reflect new usage guidelines. Replace calculator tool with batch_calculator in workflows for improved efficiency in data analysis.
397ec1c | """Calculator tool for mathematical operations.""" | |
| from langchain_core.tools import tool | |
| def calculator(expression: str) -> str: | |
| """ | |
| Effectue des calculs arithmétiques (ratios, agrégations, comparaisons). | |
| ⚠️ NE PAS UTILISER pour : | |
| - Les variations N vs N-1 (déjà fournies dans les données via "Var. : X%") | |
| - Les ratios déjà calculés (champs pré-calculés dans les indicateurs) | |
| ✅ UTILISER uniquement pour : | |
| - Calculs manuels demandés explicitement (ex: heures phytos / heures totales) | |
| - Calculs inter-enjeux après récupération via get_site_indicators | |
| - Agrégations spécifiques non disponibles dans les données | |
| Exemples d'expressions valides : | |
| - "850 / 4108" → ratio heures phytos / heures totales | |
| - "150 + 200 + 75" → somme de valeurs | |
| - "2317 / 2157" → ratio inter-indicateurs | |
| Args: | |
| expression: Expression mathématique à évaluer (ex: "850 / 4108") | |
| Returns: | |
| Résultat du calcul sous forme de chaîne, arrondi à 2 décimales | |
| """ | |
| try: | |
| # Use numexpr for safe evaluation (no arbitrary code execution). | |
| # If numexpr is installed but fails at runtime (env incompat), fallback to AST eval. | |
| try: | |
| import numexpr # type: ignore | |
| result = numexpr.evaluate(expression).item() | |
| # Round to 2 decimal places for readability | |
| if isinstance(result, float): | |
| result = round(result, 2) | |
| return str(result) | |
| except ImportError: | |
| return _safe_eval(expression) | |
| except Exception: | |
| return _safe_eval(expression) | |
| except ImportError: | |
| # Fallback to ast-based safe evaluation if numexpr not available | |
| return _safe_eval(expression) | |
| except Exception as e: | |
| return f"Erreur de calcul: {str(e)}" | |
| def _safe_eval(expression: str) -> str: | |
| """ | |
| Safe evaluation fallback using ast module. | |
| Only allows basic arithmetic operations. | |
| """ | |
| import ast | |
| import operator | |
| # Allowed operators | |
| operators = { | |
| ast.Add: operator.add, | |
| ast.Sub: operator.sub, | |
| ast.Mult: operator.mul, | |
| ast.Div: operator.truediv, | |
| ast.Pow: operator.pow, | |
| ast.USub: operator.neg, | |
| ast.UAdd: operator.pos, | |
| } | |
| def _eval(node): | |
| if isinstance(node, ast.Constant): | |
| return node.value | |
| elif isinstance(node, ast.BinOp): | |
| left = _eval(node.left) | |
| right = _eval(node.right) | |
| op = operators.get(type(node.op)) | |
| if op is None: | |
| raise ValueError(f"Opérateur non supporté: {type(node.op).__name__}") | |
| return op(left, right) | |
| elif isinstance(node, ast.UnaryOp): | |
| operand = _eval(node.operand) | |
| op = operators.get(type(node.op)) | |
| if op is None: | |
| raise ValueError(f"Opérateur non supporté: {type(node.op).__name__}") | |
| return op(operand) | |
| elif isinstance(node, ast.Expression): | |
| return _eval(node.body) | |
| else: | |
| raise ValueError(f"Expression non supportée: {type(node).__name__}") | |
| try: | |
| tree = ast.parse(expression, mode='eval') | |
| result = _eval(tree) | |
| if isinstance(result, float): | |
| result = round(result, 2) | |
| return str(result) | |
| except Exception as e: | |
| return f"Erreur de calcul: {str(e)}" | |