Tai Truong
fix readme
d202ada
import ast
import operator
from langchain.tools import StructuredTool
from langchain_core.tools import ToolException
from loguru import logger
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs import MessageTextInput
from langflow.schema import Data
class CalculatorToolComponent(LCToolComponent):
display_name = "Calculator"
description = "Perform basic arithmetic operations on a given expression."
icon = "calculator"
name = "CalculatorTool"
inputs = [
MessageTextInput(
name="expression",
display_name="Expression",
info="The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').",
),
]
class CalculatorToolSchema(BaseModel):
expression: str = Field(..., description="The arithmetic expression to evaluate.")
def run_model(self) -> list[Data]:
return self._evaluate_expression(self.expression)
def build_tool(self) -> Tool:
return StructuredTool.from_function(
name="calculator",
description="Evaluate basic arithmetic expressions. Input should be a string containing the expression.",
func=self._eval_expr_with_error,
args_schema=self.CalculatorToolSchema,
)
def _eval_expr(self, node):
# Define the allowed operators
operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
}
if isinstance(node, ast.Num):
return node.n
if isinstance(node, ast.BinOp):
return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))
if isinstance(node, ast.UnaryOp):
return operators[type(node.op)](self._eval_expr(node.operand))
if isinstance(node, ast.Call):
msg = (
"Function calls like sqrt(), sin(), cos() etc. are not supported. "
"Only basic arithmetic operations (+, -, *, /, **) are allowed."
)
raise TypeError(msg)
msg = f"Unsupported operation or expression type: {type(node).__name__}"
raise TypeError(msg)
def _eval_expr_with_error(self, expression: str) -> list[Data]:
try:
return self._evaluate_expression(expression)
except Exception as e:
raise ToolException(str(e)) from e
def _evaluate_expression(self, expression: str) -> list[Data]:
try:
# Parse the expression and evaluate it
tree = ast.parse(expression, mode="eval")
result = self._eval_expr(tree.body)
# Format the result to a reasonable number of decimal places
formatted_result = f"{result:.6f}".rstrip("0").rstrip(".")
self.status = formatted_result
return [Data(data={"result": formatted_result})]
except (SyntaxError, TypeError, KeyError) as e:
error_message = f"Invalid expression: {e}"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]
except ZeroDivisionError:
error_message = "Error: Division by zero"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error evaluating expression")
error_message = f"Error: {e}"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]