feat: implement file type
This commit is contained in:
parent
18b3fa6c34
commit
2db74fc30e
18 changed files with 149 additions and 61 deletions
|
|
@ -10,7 +10,9 @@ class AgentCreator(LangChainTypeCreator):
|
|||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return loading.AGENT_TO_CLASS
|
||||
if self.type_dict is None:
|
||||
self.type_dict = loading.AGENT_TO_CLASS
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,44 +1,10 @@
|
|||
from typing import Callable, Optional
|
||||
from langchain import LLMChain
|
||||
from langchain.agents import AgentExecutor, ZeroShotAgent
|
||||
from langflow.utils import validate
|
||||
from pydantic import BaseModel, validator
|
||||
from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
|
||||
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit
|
||||
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
from langchain.schema import BaseLanguageModel
|
||||
|
||||
|
||||
class Function(BaseModel):
|
||||
code: str
|
||||
function: Optional[Callable] = None
|
||||
imports: Optional[str] = None
|
||||
|
||||
# Eval code and store the function
|
||||
def __init__(self, **data):
|
||||
super().__init__(**data)
|
||||
|
||||
# Validate the function
|
||||
@validator("code")
|
||||
def validate_func(cls, v):
|
||||
try:
|
||||
validate.eval_function(v)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
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):
|
||||
"""Python function"""
|
||||
|
||||
code: str
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class JsonAgent(BaseModel):
|
||||
|
|
@ -9,11 +9,14 @@ from langflow.template.template import Template, Field, FrontendNode
|
|||
|
||||
class LangChainTypeCreator(BaseModel, ABC):
|
||||
type_name: str
|
||||
type_dict: Optional[Dict] = None
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
pass
|
||||
if self.type_dict is None:
|
||||
raise NotImplementedError
|
||||
return self.type_dict
|
||||
|
||||
@abstractmethod
|
||||
def get_signature(self, name: str) -> Optional[Dict[Any, Any]]:
|
||||
|
|
@ -27,7 +30,10 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
result: Dict = {self.type_name: {}}
|
||||
|
||||
for name in self.to_list():
|
||||
result[self.type_name][name] = self.frontend_node(name).to_dict()
|
||||
# frontend_node.to_dict() returns a dict with the following structure:
|
||||
# {name: {template: {fields}, description: str}}
|
||||
# so we should update the result dict
|
||||
result[self.type_name].update(self.frontend_node(name).to_dict())
|
||||
|
||||
return result
|
||||
|
||||
|
|
@ -45,6 +51,9 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
show=value.get("show", True),
|
||||
multiline=value.get("multiline", False),
|
||||
value=value.get("value", None),
|
||||
suffixes=value.get("suffixes", []),
|
||||
file_types=value.get("fileTypes", []),
|
||||
content=value.get("content", None),
|
||||
)
|
||||
for key, value in signature["template"].items()
|
||||
if key != "_type"
|
||||
|
|
@ -52,7 +61,7 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
template = Template(type_name=name, fields=fields)
|
||||
return FrontendNode(
|
||||
template=template,
|
||||
description=signature["description"],
|
||||
description=signature.get("description", ""),
|
||||
base_classes=signature["base_classes"],
|
||||
name=name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ class ChainCreator(LangChainTypeCreator):
|
|||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return chains_loading.type_to_loader_dict
|
||||
if self.type_dict is None:
|
||||
self.type_dict = chains_loading.type_to_loader_dict
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
try:
|
||||
|
|
|
|||
0
src/backend/langflow/interface/custom/__init__.py
Normal file
0
src/backend/langflow/interface/custom/__init__.py
Normal file
37
src/backend/langflow/interface/custom/types.py
Normal file
37
src/backend/langflow/interface/custom/types.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from langflow.utils import validate
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
|
||||
from typing import Callable, Optional
|
||||
|
||||
|
||||
class Function(BaseModel):
|
||||
code: str
|
||||
function: Optional[Callable] = None
|
||||
imports: Optional[str] = None
|
||||
|
||||
# Eval code and store the function
|
||||
def __init__(self, **data):
|
||||
super().__init__(**data)
|
||||
|
||||
# Validate the function
|
||||
@validator("code")
|
||||
def validate_func(cls, v):
|
||||
try:
|
||||
validate.eval_function(v)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
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):
|
||||
"""Python function"""
|
||||
|
||||
code: str
|
||||
|
|
@ -3,6 +3,7 @@ from langflow.interface.chains.base import chain_creator
|
|||
from langflow.interface.llms.base import llm_creator
|
||||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.prompts.base import prompt_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.tools.base import tool_creator
|
||||
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ def get_type_dict():
|
|||
"tools": tool_creator.to_list(),
|
||||
"chains": chain_creator.to_list(),
|
||||
"memory": memory_creator.to_list(),
|
||||
"toolkits": toolkits_creator.to_list(),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ class LLMCreator(LangChainTypeCreator):
|
|||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return llm_type_to_cls_dict
|
||||
if self.type_dict is None:
|
||||
self.type_dict = llm_type_to_cls_dict
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
"""Get the signature of an llm."""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ class MemoryCreator(LangChainTypeCreator):
|
|||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return memory_type_to_cls_dict
|
||||
if self.type_dict is None:
|
||||
self.type_dict = memory_type_to_cls_dict
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
"""Get the signature of a memory."""
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ class PromptCreator(LangChainTypeCreator):
|
|||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return loading.type_to_loader_dict
|
||||
if self.type_dict is None:
|
||||
self.type_dict = loading.type_to_loader_dict
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
try:
|
||||
|
|
|
|||
0
src/backend/langflow/interface/toolkits/__init__.py
Normal file
0
src/backend/langflow/interface/toolkits/__init__.py
Normal file
34
src/backend/langflow/interface/toolkits/base.py
Normal file
34
src/backend/langflow/interface/toolkits/base.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.utils.util import build_template_from_class
|
||||
from typing import Dict, List
|
||||
from langchain.agents import agent_toolkits
|
||||
from langflow.interface.importing.utils import import_class
|
||||
|
||||
|
||||
class ToolkitCreator(LangChainTypeCreator):
|
||||
type_name: str = "toolkits"
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
if self.type_dict is None:
|
||||
self.type_dict = {
|
||||
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()
|
||||
}
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Dict | None:
|
||||
try:
|
||||
return build_template_from_class(name, self.type_to_loader_dict)
|
||||
except ValueError as exc:
|
||||
raise ValueError("Prompt not found") from exc
|
||||
|
||||
def to_list(self) -> List[str]:
|
||||
return list(self.type_to_loader_dict.keys())
|
||||
|
||||
|
||||
toolkits_creator = ToolkitCreator()
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
from langflow.custom import customs
|
||||
from langflow.interface.tools.constants import ALL_TOOLS_NAMES, CUSTOM_TOOLS
|
||||
import langflow.interface.tools.util
|
||||
from langflow.interface.tools.constants import (
|
||||
ALL_TOOLS_NAMES,
|
||||
CUSTOM_TOOLS,
|
||||
OTHER_TOOLS,
|
||||
)
|
||||
from langflow.template.template import Field, Template
|
||||
from langflow.utils import util
|
||||
from langflow.settings import settings
|
||||
|
|
@ -12,7 +15,11 @@ from langchain.agents.load_tools import (
|
|||
_EXTRA_OPTIONAL_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
)
|
||||
from langflow.interface.tools.util import get_tools_dict
|
||||
from langflow.interface.tools.util import (
|
||||
get_tool_by_name,
|
||||
get_tools_dict,
|
||||
get_tool_params,
|
||||
)
|
||||
|
||||
|
||||
class ToolCreator(LangChainTypeCreator):
|
||||
|
|
@ -32,9 +39,7 @@ class ToolCreator(LangChainTypeCreator):
|
|||
base_classes = ["Tool"]
|
||||
all_tools = {}
|
||||
for tool in self.type_to_loader_dict.keys():
|
||||
if tool_params := langflow.interface.tools.util.get_tool_params(
|
||||
langflow.interface.tools.util.get_tool_by_name(tool)
|
||||
):
|
||||
if tool_params := get_tool_params(get_tool_by_name(tool)):
|
||||
tool_name = tool_params.get("name") or str(tool)
|
||||
all_tools[tool_name] = {"type": tool, "params": tool_params}
|
||||
|
||||
|
|
@ -67,6 +72,13 @@ class ToolCreator(LangChainTypeCreator):
|
|||
value="",
|
||||
multiline=True,
|
||||
),
|
||||
"dict_": Field(
|
||||
field_type="file",
|
||||
required=True,
|
||||
is_list=False,
|
||||
show=True,
|
||||
value="",
|
||||
),
|
||||
}
|
||||
|
||||
tool_type: str = all_tools[name]["type"] # type: ignore
|
||||
|
|
@ -89,6 +101,8 @@ class ToolCreator(LangChainTypeCreator):
|
|||
base_classes = ["function"]
|
||||
if node := customs.get_custom_nodes("tools").get(tool_type):
|
||||
return node
|
||||
elif tool_type in OTHER_TOOLS:
|
||||
params = all_tools[name]["params"] # type: ignore
|
||||
|
||||
else:
|
||||
params = []
|
||||
|
|
@ -108,9 +122,7 @@ class ToolCreator(LangChainTypeCreator):
|
|||
|
||||
template = Template(fields=fields, type_name=tool_type)
|
||||
|
||||
tool_params = langflow.interface.tools.util.get_tool_params(
|
||||
langflow.interface.tools.util.get_tool_by_name(tool_type)
|
||||
)
|
||||
tool_params = get_tool_params(get_tool_by_name(tool_type))
|
||||
if tool_params is None:
|
||||
tool_params = {}
|
||||
return {
|
||||
|
|
@ -125,9 +137,11 @@ class ToolCreator(LangChainTypeCreator):
|
|||
tools = []
|
||||
|
||||
for tool in ALL_TOOLS_NAMES:
|
||||
tool_params = langflow.interface.tools.util.get_tool_params(
|
||||
langflow.interface.tools.util.get_tool_by_name(tool)
|
||||
)
|
||||
tool_params = get_tool_params(get_tool_by_name(tool))
|
||||
|
||||
if tool_params and not tool_params.get("name"):
|
||||
tool_params["name"] = tool
|
||||
|
||||
if tool_params and (
|
||||
tool_params.get("name") in settings.tools
|
||||
or (tool_params.get("name") and settings.dev)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from langchain.agents.load_tools import get_all_tool_names
|
||||
from langchain.agents import Tool
|
||||
from langflow.interface.custom_types import PythonFunction
|
||||
from langflow.interface.custom.types import PythonFunction
|
||||
from langchain.tools.json.tool import JsonSpec
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from langflow.interface.llms.base import llm_creator
|
|||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.prompts.base import prompt_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.tools.base import tool_creator
|
||||
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ def build_langchain_types_dict():
|
|||
llm_creator,
|
||||
memory_creator,
|
||||
tool_creator,
|
||||
toolkits_creator,
|
||||
]
|
||||
|
||||
all_types = {}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any
|
||||
from typing import Any, Union
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
|
|
@ -10,6 +10,9 @@ class Field(BaseModel):
|
|||
show: bool = True
|
||||
multiline: bool = False
|
||||
value: Any = None
|
||||
suffixes: list[str] = []
|
||||
file_types: list[str] = []
|
||||
content: Union[str, None] = None
|
||||
# _name will be used to store the name of the field
|
||||
# in the template
|
||||
name: str = ""
|
||||
|
|
@ -18,10 +21,16 @@ class Field(BaseModel):
|
|||
result = self.dict()
|
||||
# Remove key if it is None
|
||||
for key in list(result.keys()):
|
||||
if result[key] is None:
|
||||
if result[key] is None or result[key] == []:
|
||||
del result[key]
|
||||
result["type"] = result.pop("field_type")
|
||||
result["list"] = result.pop("is_list")
|
||||
|
||||
if result.get("file_types"):
|
||||
result["fileTypes"] = result.pop("file_types")
|
||||
|
||||
if self.field_type == "file":
|
||||
result["content"] = self.content
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,8 @@ def format_dict(d, name: Optional[str] = None):
|
|||
# Change type from str to Tool
|
||||
value["type"] = "Tool" if key in ["allowed_tools"] else _type
|
||||
|
||||
value["type"] = "int" if key in ["max_value_length"] else value["type"]
|
||||
|
||||
# Show or not field
|
||||
value["show"] = bool(
|
||||
(value["required"] and key not in ["input_variables"])
|
||||
|
|
@ -307,7 +309,10 @@ def format_dict(d, name: Optional[str] = None):
|
|||
if "dict" in value["type"].lower():
|
||||
value["type"] = "code"
|
||||
|
||||
value["file"] = key in ["dict_"]
|
||||
if key == "dict_":
|
||||
value["type"] = "file"
|
||||
value["suffixes"] = [".json", ".yaml", ".yml"]
|
||||
value["fileTypes"] = ["json", "yaml", "yml"]
|
||||
|
||||
# Replace default value with actual value
|
||||
if "default" in value:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Test this:
|
||||
from langflow.interface.custom_types import PythonFunction
|
||||
from langflow.interface.custom.types import PythonFunction
|
||||
from langflow.utils import constants
|
||||
import pytest
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue