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
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
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:
|
def get_signature(self, name: str) -> Dict | None:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,10 @@
|
||||||
from typing import Callable, Optional
|
|
||||||
from langchain import LLMChain
|
from langchain import LLMChain
|
||||||
from langchain.agents import AgentExecutor, ZeroShotAgent
|
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.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.agent_toolkits.json.toolkit import JsonToolkit
|
||||||
|
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||||
from langchain.schema import BaseLanguageModel
|
from langchain.schema import BaseLanguageModel
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class JsonAgent(BaseModel):
|
class JsonAgent(BaseModel):
|
||||||
|
|
@ -9,11 +9,14 @@ from langflow.template.template import Template, Field, FrontendNode
|
||||||
|
|
||||||
class LangChainTypeCreator(BaseModel, ABC):
|
class LangChainTypeCreator(BaseModel, ABC):
|
||||||
type_name: str
|
type_name: str
|
||||||
|
type_dict: Optional[Dict] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def type_to_loader_dict(self) -> Dict:
|
def type_to_loader_dict(self) -> Dict:
|
||||||
pass
|
if self.type_dict is None:
|
||||||
|
raise NotImplementedError
|
||||||
|
return self.type_dict
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_signature(self, name: str) -> Optional[Dict[Any, Any]]:
|
def get_signature(self, name: str) -> Optional[Dict[Any, Any]]:
|
||||||
|
|
@ -27,7 +30,10 @@ class LangChainTypeCreator(BaseModel, ABC):
|
||||||
result: Dict = {self.type_name: {}}
|
result: Dict = {self.type_name: {}}
|
||||||
|
|
||||||
for name in self.to_list():
|
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
|
return result
|
||||||
|
|
||||||
|
|
@ -45,6 +51,9 @@ class LangChainTypeCreator(BaseModel, ABC):
|
||||||
show=value.get("show", True),
|
show=value.get("show", True),
|
||||||
multiline=value.get("multiline", False),
|
multiline=value.get("multiline", False),
|
||||||
value=value.get("value", None),
|
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()
|
for key, value in signature["template"].items()
|
||||||
if key != "_type"
|
if key != "_type"
|
||||||
|
|
@ -52,7 +61,7 @@ class LangChainTypeCreator(BaseModel, ABC):
|
||||||
template = Template(type_name=name, fields=fields)
|
template = Template(type_name=name, fields=fields)
|
||||||
return FrontendNode(
|
return FrontendNode(
|
||||||
template=template,
|
template=template,
|
||||||
description=signature["description"],
|
description=signature.get("description", ""),
|
||||||
base_classes=signature["base_classes"],
|
base_classes=signature["base_classes"],
|
||||||
name=name,
|
name=name,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ class ChainCreator(LangChainTypeCreator):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
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:
|
def get_signature(self, name: str) -> Dict | None:
|
||||||
try:
|
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.llms.base import llm_creator
|
||||||
from langflow.interface.memories.base import memory_creator
|
from langflow.interface.memories.base import memory_creator
|
||||||
from langflow.interface.prompts.base import prompt_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
|
from langflow.interface.tools.base import tool_creator
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -14,6 +15,7 @@ def get_type_dict():
|
||||||
"tools": tool_creator.to_list(),
|
"tools": tool_creator.to_list(),
|
||||||
"chains": chain_creator.to_list(),
|
"chains": chain_creator.to_list(),
|
||||||
"memory": memory_creator.to_list(),
|
"memory": memory_creator.to_list(),
|
||||||
|
"toolkits": toolkits_creator.to_list(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ class LLMCreator(LangChainTypeCreator):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
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:
|
def get_signature(self, name: str) -> Dict | None:
|
||||||
"""Get the signature of an llm."""
|
"""Get the signature of an llm."""
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ class MemoryCreator(LangChainTypeCreator):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
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:
|
def get_signature(self, name: str) -> Dict | None:
|
||||||
"""Get the signature of a memory."""
|
"""Get the signature of a memory."""
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ class PromptCreator(LangChainTypeCreator):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
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:
|
def get_signature(self, name: str) -> Dict | None:
|
||||||
try:
|
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.custom import customs
|
||||||
from langflow.interface.tools.constants import ALL_TOOLS_NAMES, CUSTOM_TOOLS
|
from langflow.interface.tools.constants import (
|
||||||
import langflow.interface.tools.util
|
ALL_TOOLS_NAMES,
|
||||||
|
CUSTOM_TOOLS,
|
||||||
|
OTHER_TOOLS,
|
||||||
|
)
|
||||||
from langflow.template.template import Field, Template
|
from langflow.template.template import Field, Template
|
||||||
from langflow.utils import util
|
from langflow.utils import util
|
||||||
from langflow.settings import settings
|
from langflow.settings import settings
|
||||||
|
|
@ -12,7 +15,11 @@ from langchain.agents.load_tools import (
|
||||||
_EXTRA_OPTIONAL_TOOLS,
|
_EXTRA_OPTIONAL_TOOLS,
|
||||||
_LLM_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):
|
class ToolCreator(LangChainTypeCreator):
|
||||||
|
|
@ -32,9 +39,7 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
base_classes = ["Tool"]
|
base_classes = ["Tool"]
|
||||||
all_tools = {}
|
all_tools = {}
|
||||||
for tool in self.type_to_loader_dict.keys():
|
for tool in self.type_to_loader_dict.keys():
|
||||||
if tool_params := langflow.interface.tools.util.get_tool_params(
|
if tool_params := get_tool_params(get_tool_by_name(tool)):
|
||||||
langflow.interface.tools.util.get_tool_by_name(tool)
|
|
||||||
):
|
|
||||||
tool_name = tool_params.get("name") or str(tool)
|
tool_name = tool_params.get("name") or str(tool)
|
||||||
all_tools[tool_name] = {"type": tool, "params": tool_params}
|
all_tools[tool_name] = {"type": tool, "params": tool_params}
|
||||||
|
|
||||||
|
|
@ -67,6 +72,13 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
value="",
|
value="",
|
||||||
multiline=True,
|
multiline=True,
|
||||||
),
|
),
|
||||||
|
"dict_": Field(
|
||||||
|
field_type="file",
|
||||||
|
required=True,
|
||||||
|
is_list=False,
|
||||||
|
show=True,
|
||||||
|
value="",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_type: str = all_tools[name]["type"] # type: ignore
|
tool_type: str = all_tools[name]["type"] # type: ignore
|
||||||
|
|
@ -89,6 +101,8 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
base_classes = ["function"]
|
base_classes = ["function"]
|
||||||
if node := customs.get_custom_nodes("tools").get(tool_type):
|
if node := customs.get_custom_nodes("tools").get(tool_type):
|
||||||
return node
|
return node
|
||||||
|
elif tool_type in OTHER_TOOLS:
|
||||||
|
params = all_tools[name]["params"] # type: ignore
|
||||||
|
|
||||||
else:
|
else:
|
||||||
params = []
|
params = []
|
||||||
|
|
@ -108,9 +122,7 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
|
|
||||||
template = Template(fields=fields, type_name=tool_type)
|
template = Template(fields=fields, type_name=tool_type)
|
||||||
|
|
||||||
tool_params = langflow.interface.tools.util.get_tool_params(
|
tool_params = get_tool_params(get_tool_by_name(tool_type))
|
||||||
langflow.interface.tools.util.get_tool_by_name(tool_type)
|
|
||||||
)
|
|
||||||
if tool_params is None:
|
if tool_params is None:
|
||||||
tool_params = {}
|
tool_params = {}
|
||||||
return {
|
return {
|
||||||
|
|
@ -125,9 +137,11 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
tools = []
|
tools = []
|
||||||
|
|
||||||
for tool in ALL_TOOLS_NAMES:
|
for tool in ALL_TOOLS_NAMES:
|
||||||
tool_params = langflow.interface.tools.util.get_tool_params(
|
tool_params = get_tool_params(get_tool_by_name(tool))
|
||||||
langflow.interface.tools.util.get_tool_by_name(tool)
|
|
||||||
)
|
if tool_params and not tool_params.get("name"):
|
||||||
|
tool_params["name"] = tool
|
||||||
|
|
||||||
if tool_params and (
|
if tool_params and (
|
||||||
tool_params.get("name") in settings.tools
|
tool_params.get("name") in settings.tools
|
||||||
or (tool_params.get("name") and settings.dev)
|
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.load_tools import get_all_tool_names
|
||||||
from langchain.agents import Tool
|
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
|
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.memories.base import memory_creator
|
||||||
from langflow.interface.prompts.base import prompt_creator
|
from langflow.interface.prompts.base import prompt_creator
|
||||||
from langflow.interface.chains.base import chain_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
|
from langflow.interface.tools.base import tool_creator
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ def build_langchain_types_dict():
|
||||||
llm_creator,
|
llm_creator,
|
||||||
memory_creator,
|
memory_creator,
|
||||||
tool_creator,
|
tool_creator,
|
||||||
|
toolkits_creator,
|
||||||
]
|
]
|
||||||
|
|
||||||
all_types = {}
|
all_types = {}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Any
|
from typing import Any, Union
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,6 +10,9 @@ class Field(BaseModel):
|
||||||
show: bool = True
|
show: bool = True
|
||||||
multiline: bool = False
|
multiline: bool = False
|
||||||
value: Any = None
|
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
|
# _name will be used to store the name of the field
|
||||||
# in the template
|
# in the template
|
||||||
name: str = ""
|
name: str = ""
|
||||||
|
|
@ -18,10 +21,16 @@ class Field(BaseModel):
|
||||||
result = self.dict()
|
result = self.dict()
|
||||||
# Remove key if it is None
|
# Remove key if it is None
|
||||||
for key in list(result.keys()):
|
for key in list(result.keys()):
|
||||||
if result[key] is None:
|
if result[key] is None or result[key] == []:
|
||||||
del result[key]
|
del result[key]
|
||||||
result["type"] = result.pop("field_type")
|
result["type"] = result.pop("field_type")
|
||||||
result["list"] = result.pop("is_list")
|
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
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,8 @@ def format_dict(d, name: Optional[str] = None):
|
||||||
# Change type from str to Tool
|
# Change type from str to Tool
|
||||||
value["type"] = "Tool" if key in ["allowed_tools"] else _type
|
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
|
# Show or not field
|
||||||
value["show"] = bool(
|
value["show"] = bool(
|
||||||
(value["required"] and key not in ["input_variables"])
|
(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():
|
if "dict" in value["type"].lower():
|
||||||
value["type"] = "code"
|
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
|
# Replace default value with actual value
|
||||||
if "default" in value:
|
if "default" in value:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Test this:
|
# Test this:
|
||||||
from langflow.interface.custom_types import PythonFunction
|
from langflow.interface.custom.types import PythonFunction
|
||||||
from langflow.utils import constants
|
from langflow.utils import constants
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue