07-tool-using-agent / src /tools /calculator.py
FlyingNunchucks's picture
Sync from GitHub via hub-sync
6bedc74 verified
Raw History Blame Contribute Delete
2.33 kB
import ast
import operator
from typing import Any, Dict
_ALLOWED_OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def _evaluate(node: ast.AST) -> float:
if isinstance(node, ast.Expression):
return _evaluate(node.body)
if isinstance(node, ast.Constant):
if isinstance(node.value, (int, float)):
return node.value
raise ValueError("Only numeric constants are allowed.")
if isinstance(node, ast.BinOp):
operator_type = type(node.op)
if operator_type not in _ALLOWED_OPERATORS:
raise ValueError(
f"Operator {operator_type.__name__} is not allowed."
)
left = _evaluate(node.left)
right = _evaluate(node.right)
return _ALLOWED_OPERATORS[operator_type](left, right)
if isinstance(node, ast.UnaryOp):
operator_type = type(node.op)
if operator_type not in _ALLOWED_OPERATORS:
raise ValueError(
f"Operator {operator_type.__name__} is not allowed."
)
value = _evaluate(node.operand)
return _ALLOWED_OPERATORS[operator_type](value)
raise ValueError(
f"Unsupported expression element: {type(node).__name__}"
)
def calculator(expression: str) -> Dict[str, Any]:
"""
Safely evaluate a basic mathematical expression.
Supported operations:
+, -, *, /, //, %, **, unary +, unary -
"""
if not isinstance(expression, str):
raise TypeError("expression must be a string.")
expression = expression.strip()
if not expression:
raise ValueError("expression cannot be empty.")
if len(expression) > 200:
raise ValueError("expression is too long.")
try:
parsed = ast.parse(expression, mode="eval")
result = _evaluate(parsed)
except ZeroDivisionError as exc:
raise ValueError("Division by zero is not allowed.") from exc
except SyntaxError as exc:
raise ValueError("Invalid mathematical expression.") from exc
return {
"expression": expression,
"result": result,
}