featur: Add CalculatorToolComponent (#3772)

* feat: Add CalculatorToolComponent to langflow/components/tools

This commit adds the CalculatorToolComponent to the langflow/components/tools directory. The CalculatorToolComponent allows users to perform basic arithmetic operations on a given expression. It includes an input for the expression and returns the result or an error message.

Ref: #3757

* lint

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
anovazzi1 2024-09-11 18:48:24 -03:00 • committed by GitHub
commit 92f59ab9e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 17 deletions

5
poetry.lock generated
View file

@ -10605,11 +10605,6 @@ files = [
{file = "triton-3.0.0-1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e509deb77f1c067d8640725ef00c5cbfcb2052a1a3cb6a6d343841f92624eb"}, {file = "triton-3.0.0-1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e509deb77f1c067d8640725ef00c5cbfcb2052a1a3cb6a6d343841f92624eb"},
{file = "triton-3.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bcbf3b1c48af6a28011a5c40a5b3b9b5330530c3827716b5fbf6d7adcc1e53e9"}, {file = "triton-3.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bcbf3b1c48af6a28011a5c40a5b3b9b5330530c3827716b5fbf6d7adcc1e53e9"},
{file = "triton-3.0.0-1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6e5727202f7078c56f91ff13ad0c1abab14a0e7f2c87e91b12b6f64f3e8ae609"}, {file = "triton-3.0.0-1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6e5727202f7078c56f91ff13ad0c1abab14a0e7f2c87e91b12b6f64f3e8ae609"},
{file = "triton-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b052da883351fdf6be3d93cedae6db3b8e3988d3b09ed221bccecfa9612230"},
{file = "triton-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd34f19a8582af96e6291d4afce25dac08cb2a5d218c599163761e8e0827208e"},
{file = "triton-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d5e10de8c011adeb7c878c6ce0dd6073b14367749e34467f1cff2bde1b78253"},
{file = "triton-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8903767951bf86ec960b4fe4e21bc970055afc65e9d57e916d79ae3c93665e3"},
{file = "triton-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41004fb1ae9a53fcb3e970745feb87f0e3c94c6ce1ba86e95fa3b8537894bef7"},
] ]
[package.dependencies] [package.dependencies]

View file

@ -0,0 +1,82 @@
import ast
import operator
from typing import List
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.inputs import MessageTextInput
from langflow.schema import Data
from langflow.field_typing import Tool
from langchain.tools import StructuredTool
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._evaluate_expression,
args_schema=self.CalculatorToolSchema,
)
def _evaluate_expression(self, expression: str) -> List[Data]:
try:
# 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,
}
def eval_expr(node):
if isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return operators[type(node.op)](eval_expr(node.left), eval_expr(node.right))
elif isinstance(node, ast.UnaryOp):
return operators[type(node.op)](eval_expr(node.operand))
else:
raise TypeError(node)
# Parse the expression and evaluate it
tree = ast.parse(expression, mode="eval")
result = 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: {str(e)}"
self.status = error_message
return [Data(data={"error": error_message})]
except ZeroDivisionError:
error_message = "Error: Division by zero"
self.status = error_message
return [Data(data={"error": error_message})]
except Exception as e:
error_message = f"Error: {str(e)}"
self.status = error_message
return [Data(data={"error": error_message})]

View file

@ -10,6 +10,7 @@ from .SearXNGTool import SearXNGToolComponent
from .SerpAPI import SerpAPIComponent from .SerpAPI import SerpAPIComponent
from .WikipediaAPI import WikipediaAPIComponent from .WikipediaAPI import WikipediaAPIComponent
from .WolframAlphaAPI import WolframAlphaAPIComponent from .WolframAlphaAPI import WolframAlphaAPIComponent
from .Calculator import CalculatorToolComponent
__all__ = [ __all__ = [
@ -25,4 +26,5 @@ __all__ = [
"SerpAPIComponent", "SerpAPIComponent",
"WikipediaAPIComponent", "WikipediaAPIComponent",
"WolframAlphaAPIComponent", "WolframAlphaAPIComponent",
"CalculatorToolComponent",
] ]

View file

@ -1159,7 +1159,7 @@
"description": "Perform basic arithmetic operations on a given expression.", "description": "Perform basic arithmetic operations on a given expression.",
"display_name": "Calculator", "display_name": "Calculator",
"documentation": "", "documentation": "",
"edited": true, "edited": false,
"field_order": [ "field_order": [
"expression" "expression"
], ],
@ -1176,8 +1176,7 @@
"name": "api_run_model", "name": "api_run_model",
"selected": "Data", "selected": "Data",
"types": [ "types": [
"Data", "Data"
"list"
], ],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
@ -1188,8 +1187,7 @@
"name": "api_build_tool", "name": "api_build_tool",
"selected": "Tool", "selected": "Tool",
"types": [ "types": [
"Tool", "Tool"
"Sequence"
], ],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
@ -1213,7 +1211,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "code", "type": "code",
"value": "import ast\r\nimport operator\r\nfrom typing import List\r\nfrom pydantic import BaseModel, Field\r\nfrom langflow.base.langchain_utilities.model import LCToolComponent\r\nfrom langflow.inputs import MessageTextInput\r\nfrom langflow.schema import Data\r\nfrom langflow.field_typing import Tool\r\nfrom langchain.tools import StructuredTool\r\n\r\nclass CalculatorToolComponent(LCToolComponent):\r\n display_name = \"Calculator\"\r\n description = \"Perform basic arithmetic operations on a given expression.\"\r\n icon = \"calculator\"\r\n name = \"CalculatorTool\"\r\n\r\n inputs = [\r\n MessageTextInput(\r\n name=\"expression\",\r\n display_name=\"Expression\",\r\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\r\n ),\r\n ]\r\n\r\n class CalculatorToolSchema(BaseModel):\r\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\r\n\r\n def run_model(self) -> List[Data]:\r\n return self._evaluate_expression(self.expression)\r\n\r\n def build_tool(self) -> Tool:\r\n return StructuredTool.from_function(\r\n name=\"calculator\",\r\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\r\n func=self._evaluate_expression,\r\n args_schema=self.CalculatorToolSchema,\r\n )\r\n\r\n def _evaluate_expression(self, expression: str) -> List[Data]:\r\n try:\r\n # Define the allowed operators\r\n operators = {\r\n ast.Add: operator.add,\r\n ast.Sub: operator.sub,\r\n ast.Mult: operator.mul,\r\n ast.Div: operator.truediv,\r\n ast.Pow: operator.pow,\r\n }\r\n\r\n def eval_expr(node):\r\n if isinstance(node, ast.Num):\r\n return node.n\r\n elif isinstance(node, ast.BinOp):\r\n return operators[type(node.op)](eval_expr(node.left), eval_expr(node.right))\r\n elif isinstance(node, ast.UnaryOp):\r\n return operators[type(node.op)](eval_expr(node.operand))\r\n else:\r\n raise TypeError(node)\r\n\r\n # Parse the expression and evaluate it\r\n tree = ast.parse(expression, mode='eval')\r\n result = eval_expr(tree.body)\r\n\r\n # Format the result to a reasonable number of decimal places\r\n formatted_result = f\"{result:.6f}\".rstrip('0').rstrip('.')\r\n\r\n self.status = formatted_result\r\n return [Data(data={\"result\": formatted_result})]\r\n\r\n except (SyntaxError, TypeError, KeyError) as e:\r\n error_message = f\"Invalid expression: {str(e)}\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]\r\n except ZeroDivisionError:\r\n error_message = \"Error: Division by zero\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]\r\n except Exception as e:\r\n error_message = f\"Error: {str(e)}\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]" "value": "import ast\nimport operator\nfrom typing import List\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> List[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._evaluate_expression,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _evaluate_expression(self, expression: str) -> List[Data]:\n try:\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n def eval_expr(node):\n if isinstance(node, ast.Num):\n return node.n\n elif isinstance(node, ast.BinOp):\n return operators[type(node.op)](eval_expr(node.left), eval_expr(node.right))\n elif isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](eval_expr(node.operand))\n else:\n raise TypeError(node)\n\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {str(e)}\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n except Exception as e:\n error_message = f\"Error: {str(e)}\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n"
}, },
"expression": { "expression": {
"_input_type": "MessageTextInput", "_input_type": "MessageTextInput",

View file

@ -2258,7 +2258,7 @@
"description": "Perform basic arithmetic operations on a given expression.", "description": "Perform basic arithmetic operations on a given expression.",
"display_name": "Calculator", "display_name": "Calculator",
"documentation": "", "documentation": "",
"edited": true, "edited": false,
"field_order": [ "field_order": [
"expression" "expression"
], ],
@ -2275,8 +2275,7 @@
"name": "api_run_model", "name": "api_run_model",
"selected": "Data", "selected": "Data",
"types": [ "types": [
"Data", "Data"
"list"
], ],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
@ -2287,8 +2286,7 @@
"name": "api_build_tool", "name": "api_build_tool",
"selected": "Tool", "selected": "Tool",
"types": [ "types": [
"Tool", "Tool"
"Sequence"
], ],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
@ -2312,7 +2310,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "code", "type": "code",
"value": "import ast\r\nimport operator\r\nfrom typing import List\r\nfrom pydantic import BaseModel, Field\r\nfrom langflow.base.langchain_utilities.model import LCToolComponent\r\nfrom langflow.inputs import MessageTextInput\r\nfrom langflow.schema import Data\r\nfrom langflow.field_typing import Tool\r\nfrom langchain.tools import StructuredTool\r\n\r\nclass CalculatorToolComponent(LCToolComponent):\r\n display_name = \"Calculator\"\r\n description = \"Perform basic arithmetic operations on a given expression.\"\r\n icon = \"calculator\"\r\n name = \"CalculatorTool\"\r\n\r\n inputs = [\r\n MessageTextInput(\r\n name=\"expression\",\r\n display_name=\"Expression\",\r\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\r\n ),\r\n ]\r\n\r\n class CalculatorToolSchema(BaseModel):\r\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\r\n\r\n def run_model(self) -> List[Data]:\r\n return self._evaluate_expression(self.expression)\r\n\r\n def build_tool(self) -> Tool:\r\n return StructuredTool.from_function(\r\n name=\"calculator\",\r\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\r\n func=self._evaluate_expression,\r\n args_schema=self.CalculatorToolSchema,\r\n )\r\n\r\n def _evaluate_expression(self, expression: str) -> List[Data]:\r\n try:\r\n # Define the allowed operators\r\n operators = {\r\n ast.Add: operator.add,\r\n ast.Sub: operator.sub,\r\n ast.Mult: operator.mul,\r\n ast.Div: operator.truediv,\r\n ast.Pow: operator.pow,\r\n }\r\n\r\n def eval_expr(node):\r\n if isinstance(node, ast.Num):\r\n return node.n\r\n elif isinstance(node, ast.BinOp):\r\n return operators[type(node.op)](eval_expr(node.left), eval_expr(node.right))\r\n elif isinstance(node, ast.UnaryOp):\r\n return operators[type(node.op)](eval_expr(node.operand))\r\n else:\r\n raise TypeError(node)\r\n\r\n # Parse the expression and evaluate it\r\n tree = ast.parse(expression, mode='eval')\r\n result = eval_expr(tree.body)\r\n\r\n # Format the result to a reasonable number of decimal places\r\n formatted_result = f\"{result:.6f}\".rstrip('0').rstrip('.')\r\n\r\n self.status = formatted_result\r\n return [Data(data={\"result\": formatted_result})]\r\n\r\n except (SyntaxError, TypeError, KeyError) as e:\r\n error_message = f\"Invalid expression: {str(e)}\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]\r\n except ZeroDivisionError:\r\n error_message = \"Error: Division by zero\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]\r\n except Exception as e:\r\n error_message = f\"Error: {str(e)}\"\r\n self.status = error_message\r\n return [Data(data={\"error\": error_message})]" "value": "import ast\nimport operator\nfrom typing import List\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> List[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._evaluate_expression,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _evaluate_expression(self, expression: str) -> List[Data]:\n try:\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n def eval_expr(node):\n if isinstance(node, ast.Num):\n return node.n\n elif isinstance(node, ast.BinOp):\n return operators[type(node.op)](eval_expr(node.left), eval_expr(node.right))\n elif isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](eval_expr(node.operand))\n else:\n raise TypeError(node)\n\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {str(e)}\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n except Exception as e:\n error_message = f\"Error: {str(e)}\"\n self.status = error_message\n return [Data(data={\"error\": error_message})]\n"
}, },
"expression": { "expression": {
"_input_type": "MessageTextInput", "_input_type": "MessageTextInput",