🚀 feat(langflow): rename PythonFunction to PythonFunctionTool for better semantics

🚀 feat(langflow): add PythonFunctionToolNode to the frontend node tools
🚀 feat(langflow): add PythonFunctionTool to the custom tools
🚀 feat(langflow): add get_function to importing utils to get the function from code
🚀 feat(langflow): add func parameter to PythonFunctionTool to store the function
🚀 feat(langflow): add name and description parameters to PythonFunctionTool
🚀 feat(langflow): update instantiate_tool to use PythonFunctionTool instead of PythonFunction
🚀 feat(langflow): update constants to use PythonFunctionTool instead of PythonFunction
🚀 feat(langflow): update custom.py to use PythonFunctionTool instead of PythonFunction
🚀 feat(langflow): update loading.py to use get_function and PythonFunctionTool
🚀 feat(langflow): update utils.py to use get_function
🚀 feat(langflow): update test_custom_types.py to use get_function and PythonFunctionTool
🚀 feat(langflow): update test_graph.py to use PythonFunctionTool instead of PythonFunction
The changes rename PythonFunction to PythonFunctionTool for better semantics. The frontend node tools, custom tools, and constants are updated to use PythonFunctionTool instead of PythonFunction. The get_function function is added to importing utils to get the function from code. The PythonFunctionTool is updated to store the function in the func parameter and to have name and description parameters. The instantiate_tool, loading.py, and utils.py are updated to use get_function and PythonFunctionTool. The test_custom_types.py and test_graph.py are updated to use PythonFunctionTool instead of PythonFunction.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-05-31 15:41:16 -03:00
commit be09849081
10 changed files with 73 additions and 31 deletions

View file

@ -79,7 +79,7 @@ tools:
- Calculator
- Serper Search
- Tool
- PythonFunction
- PythonFunctionTool
- JsonSpec
- News API
- TMDB API

View file

@ -4,7 +4,7 @@ from langflow.template import frontend_node
CUSTOM_NODES = {
"prompts": {"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode()},
"tools": {
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
"Tool": frontend_node.tools.ToolNode(),
},
"agents": {

View file

@ -9,6 +9,7 @@ from langchain.base_language import BaseLanguageModel
from langchain.chains.base import Chain
from langchain.chat_models.base import BaseChatModel
from langchain.tools import BaseTool
from langflow.utils import validate
def import_module(module_path: str) -> Any:
@ -147,3 +148,10 @@ def import_utility(utility: str) -> Any:
if utility == "SQLDatabase":
return import_class(f"langchain.sql_database.{utility}")
return import_class(f"langchain.utilities.{utility}")
def get_function(code):
"""Get the function"""
function_name = validate.extract_function_name(code)
return validate.create_function(code, function_name)

View file

@ -20,12 +20,12 @@ from langchain.llms.loading import load_llm_from_config
from pydantic import ValidationError
from langflow.interface.agents.custom import CUSTOM_AGENTS
from langflow.interface.importing.utils import import_by_type
from langflow.interface.importing.utils import get_function, import_by_type
from langflow.interface.run import fix_memory_inputs
from langflow.interface.toolkits.base import toolkits_creator
from langflow.interface.types import get_type_list
from langflow.interface.utils import load_file_into_dict
from langflow.utils import util, validate
from langflow.utils import util
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
@ -99,11 +99,9 @@ def instantiate_tool(node_type, class_object, params):
if node_type == "JsonSpec":
params["dict_"] = load_file_into_dict(params.pop("path"))
return class_object(**params)
elif node_type == "PythonFunction":
function_string = params["code"]
if isinstance(function_string, str):
return validate.eval_function(function_string)
raise ValueError("Function should be a string")
elif node_type == "PythonFunctionTool":
params["func"] = get_function(params.get("code"))
return class_object(**params)
elif node_type.lower() == "tool":
return class_object(**params)
return class_object(**params)

View file

@ -71,7 +71,8 @@ class ToolCreator(LangChainTypeCreator):
for tool, tool_fcn in ALL_TOOLS_NAMES.items():
tool_params = get_tool_params(tool_fcn)
tool_name = tool_params.get("name", tool)
tool_name = tool_params.get("name") or tool
if tool_name in settings.tools or settings.dev:
if tool_name == "JsonSpec":

View file

@ -9,10 +9,10 @@ from langchain.agents.load_tools import (
from langchain.tools.json.tool import JsonSpec
from langflow.interface.importing.utils import import_class
from langflow.interface.tools.custom import PythonFunction
from langflow.interface.tools.custom import PythonFunctionTool
FILE_TOOLS = {"JsonSpec": JsonSpec}
CUSTOM_TOOLS = {"Tool": Tool, "PythonFunction": PythonFunction}
CUSTOM_TOOLS = {"Tool": Tool, "PythonFunctionTool": PythonFunctionTool}
OTHER_TOOLS = {tool: import_class(f"langchain.tools.{tool}") for tool in tools.__all__}

View file

@ -1,13 +1,14 @@
from typing import Callable, Optional
from typing import Optional
from langflow.interface.importing.utils import get_function
from pydantic import BaseModel, validator
from langflow.utils import validate
from langchain.agents.tools import Tool
class Function(BaseModel):
code: str
function: Optional[Callable] = None
imports: Optional[str] = None
# Eval code and store the function
@ -24,14 +25,17 @@ class Function(BaseModel):
return v
def get_function(self):
"""Get the function"""
function_name = validate.extract_function_name(self.code)
return validate.create_function(self.code, function_name)
class PythonFunction(Function):
class PythonFunctionTool(Function, Tool):
"""Python function"""
name: str = "Custom Tool"
description: str
code: str
def ___init__(self, name: str, description: str, code: str):
self.name = name
self.description = description
self.code = code
self.func = get_function(self.code)
super().__init__(name=name, description=description, func=self.func)

View file

@ -59,11 +59,33 @@ class ToolNode(FrontendNode):
return super().to_dict()
class PythonFunctionNode(FrontendNode):
name: str = "PythonFunction"
class PythonFunctionToolNode(FrontendNode):
name: str = "PythonFunctionTool"
template: Template = Template(
type_name="python_function",
type_name="PythonFunctionTool",
fields=[
TemplateField(
field_type="str",
required=True,
placeholder="",
is_list=False,
show=True,
multiline=False,
value="",
name="name",
advanced=False,
),
TemplateField(
field_type="str",
required=True,
placeholder="",
is_list=False,
show=True,
multiline=False,
value="",
name="description",
advanced=False,
),
TemplateField(
field_type="code",
required=True,
@ -73,11 +95,11 @@ class PythonFunctionNode(FrontendNode):
value=DEFAULT_PYTHON_FUNCTION,
name="code",
advanced=False,
)
),
],
)
description: str = "Python function to be executed."
base_classes: list[str] = ["function"]
base_classes: list[str] = ["Tool"]
def to_dict(self):
return super().to_dict()