From a833441f5ab5c5463c57fc59c08a723ecf75f0a5 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Wed, 29 Mar 2023 20:26:38 -0300 Subject: [PATCH] feat: adding broken modules for testing --- .../langflow/interface/custom_lists.py | 23 +++++- src/backend/langflow/interface/listing.py | 26 ++++++- src/backend/langflow/interface/signature.py | 33 +++++++++ src/backend/langflow/interface/types.py | 14 ++++ src/backend/langflow/utils/util.py | 70 +++++++++++++++++-- 5 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/backend/langflow/interface/custom_lists.py b/src/backend/langflow/interface/custom_lists.py index 3e8a800f9..2cf57cc39 100644 --- a/src/backend/langflow/interface/custom_lists.py +++ b/src/backend/langflow/interface/custom_lists.py @@ -1,8 +1,11 @@ ## LLM from typing import Any -from langchain import llms +from langchain import llms, requests from langchain.llms.openai import OpenAIChat +from langchain.agents import agent_toolkits +from langflow.interface.importing.utils import import_class + llm_type_to_cls_dict = llms.type_to_cls_dict llm_type_to_cls_dict["openai-chat"] = OpenAIChat @@ -41,3 +44,21 @@ memory_type_to_cls_dict: dict[str, Any] = { # chain_type_to_cls_dict = type_to_loader_dict # chain_type_to_cls_dict["conversation_chain"] = ConversationChain + +toolkit_type_to_loader_dict: dict[str, Any] = { + toolkit_name: import_class(f"langchain.agents.agent_toolkits.{toolkit_name}") + # if toolkit_name is lower case it is a loader + for toolkit_name in agent_toolkits.__all__ + if toolkit_name.islower() +} + +toolkit_type_to_cls_dict: dict[str, Any] = { + toolkit_name: import_class(f"langchain.agents.agent_toolkits.{toolkit_name}") + # if toolkit_name is not lower case it is a class + for toolkit_name in agent_toolkits.__all__ + if not toolkit_name.islower() +} + +wrapper_type_to_cls_dict: dict[str, Any] = { + wrapper.__name__: wrapper for wrapper in [requests.RequestsWrapper] +} diff --git a/src/backend/langflow/interface/listing.py b/src/backend/langflow/interface/listing.py index 53d34e2a5..fcd44c6b0 100644 --- a/src/backend/langflow/interface/listing.py +++ b/src/backend/langflow/interface/listing.py @@ -1,5 +1,6 @@ from langchain import agents, chains, prompts - +from langchain.agents import agent_toolkits +from langchain import requests from langflow.custom import customs from langflow.interface.custom_lists import ( llm_type_to_cls_dict, @@ -10,11 +11,14 @@ from langflow.utils import util from langchain.agents.load_tools import get_all_tool_names from langchain.agents import Tool from langflow.interface.custom_types import PythonFunction +from langchain.tools.json.tool import JsonSpec - +OTHER_TOOLS = {"JsonSpec": JsonSpec} CUSTOM_TOOLS = {"Tool": Tool, "PythonFunction": PythonFunction} TOOLS_DICT = util.get_tools_dict() -ALL_TOOLS_NAMES = set(get_all_tool_names() + list(CUSTOM_TOOLS.keys())) +ALL_TOOLS_NAMES = set( + get_all_tool_names() + list(CUSTOM_TOOLS.keys()) + list(OTHER_TOOLS.keys()) +) def get_type_dict(): @@ -25,6 +29,8 @@ def get_type_dict(): "llms": list_llms, "tools": list_tools, "memories": list_memories, + "toolkits": list_toolkis, + "wrappers": list_wrappers, } @@ -33,6 +39,11 @@ def list_type(object_type: str): return get_type_dict().get(object_type, lambda: None)() +def list_wrappers(): + """List all wrapper types""" + return [requests.RequestsWrapper.__name__] + + def list_agents(): """List all agent types""" return [ @@ -42,6 +53,11 @@ def list_agents(): ] +def list_toolkis(): + """List all toolkit types""" + return agent_toolkits.__all__ + + def list_prompts(): """List all prompt types""" custom_prompts = customs.get_custom_nodes("prompts") @@ -60,6 +76,10 @@ def list_tools(): for tool in ALL_TOOLS_NAMES: tool_params = util.get_tool_params(util.get_tool_by_name(tool)) + + if "name" not in tool_params: + tool_params["name"] = tool + if tool_params and ( tool_params.get("name") in settings.tools or (tool_params.get("name") and settings.dev) diff --git a/src/backend/langflow/interface/signature.py b/src/backend/langflow/interface/signature.py index 390f64671..1426af4e1 100644 --- a/src/backend/langflow/interface/signature.py +++ b/src/backend/langflow/interface/signature.py @@ -12,7 +12,11 @@ from langflow.custom import customs from langflow.interface.custom_lists import ( llm_type_to_cls_dict, memory_type_to_cls_dict, + toolkit_type_to_cls_dict, + toolkit_type_to_loader_dict, + wrapper_type_to_cls_dict, ) + from langflow.interface.listing import CUSTOM_TOOLS, ALL_TOOLS_NAMES from langflow.template.template import Field, Template from langflow.utils import util @@ -21,15 +25,44 @@ from langflow.utils import util def get_signature(name: str, object_type: str): """Get the signature of an object.""" return { + "toolkits": get_toolkit_signature, "chains": get_chain_signature, "agents": get_agent_signature, "prompts": get_prompt_signature, "llms": get_llm_signature, # "memories": get_memory_signature, "tools": get_tool_signature, + "wrappers": get_wrapper_signature, }.get(object_type, lambda name: f"Invalid type: {name}")(name) +def get_toolkit_signature(name: str): + """Get the signature of a toolkit.""" + try: + if name.islower(): + pass + # return util.build_template_from_function( + # name, toolkit_type_to_loader_dict, add_function=True + # ) + else: + return util.build_template_from_class( + name, toolkit_type_to_cls_dict, add_function=True + ) + except ValueError as exc: + raise ValueError("Toolkit not found") from exc + + +def get_wrapper_signature(name: str): + """Get the signature of a wrapper.""" + try: + return util.build_template_from_class( + name, + wrapper_type_to_cls_dict, + ) + except ValueError as exc: + raise ValueError("Wrapper not found") from exc + + def get_chain_signature(name: str): """Get the chain type by signature.""" try: diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index 595addb50..89f9877ca 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -33,4 +33,18 @@ def build_langchain_types_dict(): for memory in list_type("memories") }, "tools": {tool: get_signature(tool, "tools") for tool in list_type("tools")}, + "toolkits": get_toolkits(), + "wrappers": { + wrapper: get_signature(wrapper, "wrappers") + for wrapper in list_type("wrappers") + }, } + + +def get_toolkits(): + """Get a list of all toolkits""" + result = {} + for toolkit in list_type("toolkits"): + if sig := get_signature(toolkit, "toolkits"): + result[toolkit] = sig + return result diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 664630890..652291992 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -3,7 +3,6 @@ import importlib import inspect import re from typing import Dict, Optional, Union - from langchain.agents.load_tools import ( _BASE_TOOLS, _EXTRA_LLM_TOOLS, @@ -18,6 +17,49 @@ from langchain.agents.tools import Tool from langflow.utils import constants +def build_template_from_parameters( + name: str, type_to_loader_dict: Dict, add_function: bool = False +): + # Retrieve the function that matches the provided name + func = None + for _, v in type_to_loader_dict.items(): + if v.__name__ == name: + func = v + break + + if func is None: + raise ValueError(f"{name} not found") + + # Process parameters + parameters = func.__annotations__ + variables = {} + for param_name, param_type in parameters.items(): + if param_name in ["return", "kwargs"]: + continue + + variables[param_name] = { + "type": param_type.__name__, + "default": parameters[param_name].__repr_args__()[0][1], + # Op + "placeholder": "", + } + + # Get the base classes of the return type + return_type = parameters.get("return") + base_classes = get_base_classes(return_type) if return_type else [] + if add_function: + base_classes.append("function") + + # Get the function's docstring + docs = inspect.getdoc(func) or "" + + return { + "template": format_dict(variables, name), + "description": docs["Description"], + "base_classes": base_classes, + } + + def build_template_from_function( name: str, type_to_loader_dict: Dict, add_function: bool = False ): @@ -37,7 +79,7 @@ def build_template_from_function( variables = {"_type": _type} for class_field_items, value in _class.__fields__.items(): - if class_field_items in ["callback_manager", "requests_wrapper"]: + if class_field_items in ["callback_manager"]: continue variables[class_field_items] = {} for name_, value_ in value.__repr_args__(): @@ -150,7 +192,7 @@ def get_default_factory(module: str, function: str): def get_tools_dict(): """Get the tools dictionary.""" - from langflow.interface.listing import CUSTOM_TOOLS + from langflow.interface.listing import CUSTOM_TOOLS, OTHER_TOOLS tools = { **_BASE_TOOLS, @@ -158,6 +200,7 @@ def get_tools_dict(): **{k: v[0] for k, v in _EXTRA_LLM_TOOLS.items()}, **{k: v[0] for k, v in _EXTRA_OPTIONAL_TOOLS.items()}, **CUSTOM_TOOLS, + **OTHER_TOOLS, } return tools @@ -170,15 +213,15 @@ def get_tool_by_name(name: str): return tools[name] -def get_tool_params(tool, **kwargs) -> Union[Dict, None]: +def get_tool_params(tool, **kwargs) -> Dict: # Parse the function code into an abstract syntax tree # Define if it is a function or a class if inspect.isfunction(tool): - return get_func_tool_params(tool, **kwargs) + return get_func_tool_params(tool, **kwargs) or {} elif inspect.isclass(tool): # Get the parameters necessary to # instantiate the class - return get_class_tool_params(tool, **kwargs) + return get_class_tool_params(tool, **kwargs) or {} else: raise ValueError("Tool must be a function or class.") @@ -373,7 +416,20 @@ def format_dict(d, name: Optional[str] = None): ) # Add multline - value["multiline"] = key in ["suffix", "prefix", "template", "examples", "code"] + value["multiline"] = key in [ + "suffix", + "prefix", + "template", + "examples", + "code", + "headers", + ] + + # Replace dict type with str + if "dict" in value["type"].lower(): + value["type"] = "str" + + value["file"] = key in ["dict_"] # Replace default value with actual value if "default" in value: