diff --git a/src/backend/langflow/api/extract_info_from_class.py b/src/backend/langflow/api/extract_info_from_class.py index b1923068b..032652b44 100644 --- a/src/backend/langflow/api/extract_info_from_class.py +++ b/src/backend/langflow/api/extract_info_from_class.py @@ -71,10 +71,14 @@ class ClassCodeExtractor: self.function_entrypoint_name), None ) - funtion_args = build_function.get("arguments", None) - return_type = build_function.get("return_type", None) + if build_function: + function_args = build_function.get("arguments", None) + return_type = build_function.get("return_type", None) + else: + function_args = None + return_type = None - return funtion_args, return_type + return function_args, return_type def is_valid_class_template(code: dict): diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index 26fec2be3..0896bb77d 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -95,6 +95,7 @@ tools: - Calculator - Serper Search - Tool + - CustomComponent - PythonFunctionTool - PythonFunction - JsonSpec diff --git a/src/backend/langflow/custom/customs.py b/src/backend/langflow/custom/customs.py index fb6c1da16..a6ecb75f7 100644 --- a/src/backend/langflow/custom/customs.py +++ b/src/backend/langflow/custom/customs.py @@ -8,6 +8,7 @@ CUSTOM_NODES = { "tools": { "PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(), "PythonFunction": frontend_node.tools.PythonFunctionNode(), + "CustomComponent": frontend_node.tools.CustomComponentNode(), "Tool": frontend_node.tools.ToolNode(), }, "agents": { diff --git a/src/backend/langflow/interface/initialize/loading.py b/src/backend/langflow/interface/initialize/loading.py index bbaa1f131..1ddae19b7 100644 --- a/src/backend/langflow/interface/initialize/loading.py +++ b/src/backend/langflow/interface/initialize/loading.py @@ -131,7 +131,7 @@ 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 == "PythonFunctionTool": + elif node_type in ["PythonFunctionTool", "CustomComponent"]: params["func"] = get_function(params.get("code")) return class_object(**params) # For backward compatibility @@ -243,7 +243,8 @@ def replace_zero_shot_prompt_with_prompt_template(nodes): if tool["type"] != "chatOutputNode" and "Tool" in tool["data"]["node"]["base_classes"] ] - node["data"] = build_prompt_template(prompt=node["data"], tools=tools) + node["data"] = build_prompt_template( + prompt=node["data"], tools=tools) break return nodes @@ -260,7 +261,8 @@ def load_agent_executor(agent_class: type[agent_module.Agent], params, **kwargs) tool_names = [tool.name for tool in allowed_tools] # Agent class requires an output_parser but Agent classes # have a default output_parser. - agent = agent_class(allowed_tools=tool_names, llm_chain=llm_chain) # type: ignore + agent = agent_class(allowed_tools=tool_names, + llm_chain=llm_chain) # type: ignore return AgentExecutor.from_agent_and_tools( agent=agent, tools=allowed_tools, diff --git a/src/backend/langflow/interface/tools/constants.py b/src/backend/langflow/interface/tools/constants.py index fea3c5237..fa2ce87a4 100644 --- a/src/backend/langflow/interface/tools/constants.py +++ b/src/backend/langflow/interface/tools/constants.py @@ -9,16 +9,22 @@ 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, PythonFunction +from langflow.interface.tools.custom import ( + PythonFunctionTool, + PythonFunction, + CustomComponent +) FILE_TOOLS = {"JsonSpec": JsonSpec} CUSTOM_TOOLS = { "Tool": Tool, + "CustomComponent": CustomComponent, "PythonFunctionTool": PythonFunctionTool, "PythonFunction": PythonFunction, } -OTHER_TOOLS = {tool: import_class(f"langchain.tools.{tool}") for tool in tools.__all__} +OTHER_TOOLS = {tool: import_class(f"langchain.tools.{tool}") + for tool in tools.__all__} ALL_TOOLS_NAMES = { **_BASE_TOOLS, diff --git a/src/backend/langflow/interface/tools/custom.py b/src/backend/langflow/interface/tools/custom.py index 0e2e5ff57..6c6703f36 100644 --- a/src/backend/langflow/interface/tools/custom.py +++ b/src/backend/langflow/interface/tools/custom.py @@ -52,3 +52,9 @@ class PythonFunction(Function): """Python function""" code: str + + +class CustomComponent(Function): + """Python function""" + + code: str diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index 17c1562e4..28c3a8840 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -12,6 +12,9 @@ from langflow.interface.utilities.base import utility_creator from langflow.interface.vector_store.base import vectorstore_creator from langflow.interface.wrappers.base import wrapper_creator +from langflow.template.field.base import TemplateField +from langflow.template.frontend_node.tools import CustomComponentNode + def get_type_list(): """Get a list of all langchain types""" @@ -54,6 +57,7 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union return all_types +# TODO: Move to correct place def find_class_type(class_name, classes_dict): return next( ( @@ -65,10 +69,23 @@ def find_class_type(class_name, classes_dict): ) -def build_langchain_template_custom_component(raw_code, function_args, function_return_type): - type_list = get_type_list() - type_and_class = find_class_type("Tool", type_list) +# TODO: Move to correct place +def add_new_custom_field(template, field_name: str, field_type: str): + new_field = TemplateField( + name=field_name, + field_type=field_type, + show=True, + advanced=False + ) + template.get('template')[field_name] = new_field.to_dict() + template.get('custom_fields').append(field_name) + return template + +# TODO: Move to correct place + + +def add_code_field(template, raw_code): # Field with the Python code to allow update code_field = { "code": { @@ -84,13 +101,47 @@ def build_langchain_template_custom_component(raw_code, function_args, function_ "list": False } } + template.get('template')['code'] = code_field.get('code') + + return template + + +def build_langchain_template_custom_component(raw_code, function_args, function_return_type): + # type_list = get_type_list() + # type_and_class = find_class_type("Tool", type_list) + # node = get_custom_nodes(node_type: str) + + # TODO: Build base template + template = llm_creator.to_dict()['llms']['ChatOpenAI'] + + template = CustomComponentNode().to_dict().get('CustomComponent') # TODO: Add extra fields + template = add_new_custom_field( + template, + "my_id", + "str" + ) - # TODO: Build template result - template = chain_creator.to_dict()['chains']['ConversationChain'] + template = add_new_custom_field( + template, + "year", + "int" + ) - template.get('template')['code'] = code_field.get('code') + template = add_new_custom_field( + template, + "other_field", + "bool" + ) + + template = add_code_field( + template, + raw_code + ) + + # criar um vertex + # olhar loading.py return template # return globals()['tool_creator'].to_dict()[type_and_class['type']][type_and_class['class']] diff --git a/src/backend/langflow/template/frontend_node/tools.py b/src/backend/langflow/template/frontend_node/tools.py index fa3942bd2..bfde54cb5 100644 --- a/src/backend/langflow/template/frontend_node/tools.py +++ b/src/backend/langflow/template/frontend_node/tools.py @@ -1,7 +1,10 @@ from langflow.template.field.base import TemplateField from langflow.template.frontend_node.base import FrontendNode from langflow.template.template.base import Template -from langflow.utils.constants import DEFAULT_PYTHON_FUNCTION +from langflow.utils.constants import ( + DEFAULT_PYTHON_FUNCTION, + DEFAULT_CUSTOM_COMPONENT_CODE +) class ToolNode(FrontendNode): @@ -137,3 +140,27 @@ class PythonFunctionNode(FrontendNode): def to_dict(self): return super().to_dict() + + +class CustomComponentNode(FrontendNode): + name: str = "CustomComponent" + template: Template = Template( + type_name="CustomComponent", + fields=[ + TemplateField( + field_type="code", + required=True, + placeholder="", + is_list=False, + show=True, + value=DEFAULT_CUSTOM_COMPONENT_CODE, + name="code", + advanced=False, + ) + ], + ) + description: str = "Python Class to be executed." + base_classes: list[str] = [] + + def to_dict(self): + return super().to_dict() diff --git a/src/backend/langflow/utils/constants.py b/src/backend/langflow/utils/constants.py index 44103c2b7..70ad06ee3 100644 --- a/src/backend/langflow/utils/constants.py +++ b/src/backend/langflow/utils/constants.py @@ -17,18 +17,30 @@ CHAT_OPENAI_MODELS = [ ] ANTHROPIC_MODELS = [ - "claude-v1", # largest model, ideal for a wide range of more complex tasks. - "claude-v1-100k", # An enhanced version of claude-v1 with a 100,000 token (roughly 75,000 word) context window. - "claude-instant-v1", # A smaller model with far lower latency, sampling at roughly 40 words/sec! - "claude-instant-v1-100k", # Like claude-instant-v1 with a 100,000 token context window but retains its performance. + # largest model, ideal for a wide range of more complex tasks. + "claude-v1", + # An enhanced version of claude-v1 with a 100,000 token (roughly 75,000 word) context window. + "claude-v1-100k", + # A smaller model with far lower latency, sampling at roughly 40 words/sec! + "claude-instant-v1", + # Like claude-instant-v1 with a 100,000 token context window but retains its performance. + "claude-instant-v1-100k", + # Specific sub-versions of the above models: - "claude-v1.3", # Vs claude-v1.2: better instruction-following, code, and non-English dialogue and writing. - "claude-v1.3-100k", # An enhanced version of claude-v1.3 with a 100,000 token (roughly 75,000 word) context window. - "claude-v1.2", # Vs claude-v1.1: small adv in general helpfulness, instruction following, coding, and other tasks. - "claude-v1.0", # An earlier version of claude-v1. - "claude-instant-v1.1", # Latest version of claude-instant-v1. Better than claude-instant-v1.0 at most tasks. - "claude-instant-v1.1-100k", # Version of claude-instant-v1.1 with a 100K token context window. - "claude-instant-v1.0", # An earlier version of claude-instant-v1. + # Vs claude-v1.2: better instruction-following, code, and non-English dialogue and writing. + "claude-v1.3", + # An enhanced version of claude-v1.3 with a 100,000 token (roughly 75,000 word) context window. + "claude-v1.3-100k", + # Vs claude-v1.1: small adv in general helpfulness, instruction following, coding, and other tasks. + "claude-v1.2", + # An earlier version of claude-v1. + "claude-v1.0", + # Latest version of claude-instant-v1. Better than claude-instant-v1.0 at most tasks. + "claude-instant-v1.1", + # Version of claude-instant-v1.1 with a 100K token context window. + "claude-instant-v1.1-100k", + # An earlier version of claude-instant-v1. + "claude-instant-v1.0", ] DEFAULT_PYTHON_FUNCTION = """ @@ -36,4 +48,12 @@ def python_function(text: str) -> str: \"\"\"This is a default python function that returns the input text\"\"\" return text """ + +DEFAULT_CUSTOM_COMPONENT_CODE = """ +def custom_component(text: str) -> str: + \"\"\"This is a default custom component function that returns the input text\"\"\" + \"\"\"TODO: Add a Class template\"\"\" + return text +""" + DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]