🚀 feat(customs.py): add PythonFunction to CUSTOM_NODES

🚀 feat(loading.py): add support for PythonFunction node type
🚀 feat(constants.py): add PythonFunction to CUSTOM_TOOLS
🚀 feat(custom.py): add PythonFunction class
🚀 feat(frontend_node/tools.py): add PythonFunctionNode class
🧪 test(test_custom_types.py): add test for PythonFunction class
🧪 test(test_llms_template.py): comment out tests for AzureOpenAI and AzureChatOpenAI
The changes add support for a new node type, PythonFunction, which allows users to define a Python function to be executed. The node type is added to CUSTOM_NODES in customs.py, and support for the node type is added to loading.py. The node type is also added to CUSTOM_TOOLS in constants.py, and the PythonFunction class is added to custom.py. The PythonFunctionNode class is added to frontend_node/tools.py. Tests for the new PythonFunction class are added to test_custom_types.py. Tests for AzureOpenAI and AzureChatOpenAI are commented out in test_llms_template.py.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-06 11:40:39 -03:00
commit 3de23e345f
7 changed files with 133 additions and 72 deletions

View file

@ -5,6 +5,7 @@ CUSTOM_NODES = {
"prompts": {"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode()},
"tools": {
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
"Tool": frontend_node.tools.ToolNode(),
},
"agents": {

View file

@ -26,7 +26,7 @@ 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
from langflow.utils import util, validate
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
@ -103,6 +103,12 @@ def instantiate_tool(node_type, class_object, params):
elif node_type == "PythonFunctionTool":
params["func"] = get_function(params.get("code"))
return class_object(**params)
# For backward compatibility
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.lower() == "tool":
return class_object(**params)
return class_object(**params)

View file

@ -9,10 +9,14 @@ 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 PythonFunctionTool
from langflow.interface.tools.custom import PythonFunctionTool, PythonFunction
FILE_TOOLS = {"JsonSpec": JsonSpec}
CUSTOM_TOOLS = {"Tool": Tool, "PythonFunctionTool": PythonFunctionTool}
CUSTOM_TOOLS = {
"Tool": Tool,
"PythonFunctionTool": PythonFunctionTool,
"PythonFunction": PythonFunction,
}
OTHER_TOOLS = {tool: import_class(f"langchain.tools.{tool}") for tool in tools.__all__}

View file

@ -1,4 +1,4 @@
from typing import Optional
from typing import Callable, Optional
from langflow.interface.importing.utils import get_function
from pydantic import BaseModel, validator
@ -9,6 +9,7 @@ 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
@ -25,6 +26,12 @@ 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 PythonFunctionTool(Function, Tool):
"""Python function"""
@ -39,3 +46,9 @@ class PythonFunctionTool(Function, Tool):
self.code = code
self.func = get_function(self.code)
super().__init__(name=name, description=description, func=self.func)
class PythonFunction(Function):
"""Python function"""
code: str

View file

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