merge fix
This commit is contained in:
commit
bd7cb06e91
19 changed files with 198 additions and 126 deletions
2
Makefile
2
Makefile
|
|
@ -59,7 +59,7 @@ lcserve_push:
|
||||||
make build_frontend
|
make build_frontend
|
||||||
@version=$$(poetry version --short); \
|
@version=$$(poetry version --short); \
|
||||||
lc-serve push --app langflow.lcserve:app --app-dir . \
|
lc-serve push --app langflow.lcserve:app --app-dir . \
|
||||||
--image-name langflow --image-tag $${version} --verbose
|
--image-name langflow --image-tag $${version} --verbose --public
|
||||||
|
|
||||||
lcserve_deploy:
|
lcserve_deploy:
|
||||||
@:$(if $(uses),,$(error `uses` is not set. Please run `make uses=... lcserve_deploy`))
|
@:$(if $(uses),,$(error `uses` is not set. Please run `make uses=... lcserve_deploy`))
|
||||||
|
|
|
||||||
10
poetry.lock
generated
10
poetry.lock
generated
|
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiofiles"
|
name = "aiofiles"
|
||||||
|
|
@ -1096,7 +1096,9 @@ files = [
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b2707096d6df4321044fcde2c9f04da632d11a8be60957fd09d49a42fae71a29"},
|
{file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b2707096d6df4321044fcde2c9f04da632d11a8be60957fd09d49a42fae71a29"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b27df1b70ae74d2c88efb5ffca8490954fdc678099509a9c4404ca30acc53426"},
|
{file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b27df1b70ae74d2c88efb5ffca8490954fdc678099509a9c4404ca30acc53426"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75a97c800271b52dd0f37696d074c50576dcb4b2750b6115932a98696a268070"},
|
{file = "duckdb-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75a97c800271b52dd0f37696d074c50576dcb4b2750b6115932a98696a268070"},
|
||||||
|
{file = "duckdb-0.8.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:804cac261a5e016506a6d67838a65d19b06a237f7949f1704f0e800eb708286a"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6b9abca7fa6713e1d031c18485343b4de99742c7e1b85c10718aa2f31a4e2c6"},
|
{file = "duckdb-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6b9abca7fa6713e1d031c18485343b4de99742c7e1b85c10718aa2f31a4e2c6"},
|
||||||
|
{file = "duckdb-0.8.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:51aa6d606d49072abcfeb3be209eb559ac94c1b5e70f58ac3adbb94aca9cd69f"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7c8dc769aaf2be0a1c57995ca657e5b92c1c56fc8437edb720ca6cab571adf14"},
|
{file = "duckdb-0.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7c8dc769aaf2be0a1c57995ca657e5b92c1c56fc8437edb720ca6cab571adf14"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-win32.whl", hash = "sha256:c4207d18b42387c4a035846d8878eb967070198be8ac26fd77797ce320d1a400"},
|
{file = "duckdb-0.8.0-cp311-cp311-win32.whl", hash = "sha256:c4207d18b42387c4a035846d8878eb967070198be8ac26fd77797ce320d1a400"},
|
||||||
{file = "duckdb-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:0c392257547c20794c3072fcbca99a49ef0a49974005d755e93893e2b4875267"},
|
{file = "duckdb-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:0c392257547c20794c3072fcbca99a49ef0a49974005d755e93893e2b4875267"},
|
||||||
|
|
@ -2374,13 +2376,13 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-serve"
|
name = "langchain-serve"
|
||||||
version = "0.0.38"
|
version = "0.0.40"
|
||||||
description = "Langchain Serve - serve your langchain apps on Jina AI Cloud."
|
description = "Langchain Serve - serve your langchain apps on Jina AI Cloud."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "langchain-serve-0.0.38.tar.gz", hash = "sha256:649b8e26eebe6b33960c081b388fb7118acbfdc00f97dd935a580ab88aca53d6"},
|
{file = "langchain-serve-0.0.40.tar.gz", hash = "sha256:c60b173fcf0b682fbb70d34e8f485ce168e2229f55cb5c4ffbc26a5206af1c06"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -6178,4 +6180,4 @@ deploy = ["langchain-serve"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<3.12"
|
python-versions = ">=3.9,<3.12"
|
||||||
content-hash = "9ce165d2decf2190d7ce69be608872b3ed9abe705a276045623706d01665754b"
|
content-hash = "2bf357ad30f79c68751b34c991b4a73767ceb628657f4133228d4eb487d8a6fb"
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ psycopg2-binary = "^2.9.6"
|
||||||
pyarrow = "^11.0.0"
|
pyarrow = "^11.0.0"
|
||||||
tiktoken = "^0.3.3"
|
tiktoken = "^0.3.3"
|
||||||
wikipedia = "^1.4.0"
|
wikipedia = "^1.4.0"
|
||||||
langchain-serve = { version = "^0.0.38", optional = true }
|
langchain-serve = { version = ">0.0.39", optional = true }
|
||||||
qdrant-client = "^1.2.0"
|
qdrant-client = "^1.2.0"
|
||||||
websockets = "^11.0.3"
|
websockets = "^11.0.3"
|
||||||
weaviate-client = "^3.19.2"
|
weaviate-client = "^3.19.2"
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from langflow.api.base import (
|
||||||
PromptValidationResponse,
|
PromptValidationResponse,
|
||||||
validate_prompt,
|
validate_prompt,
|
||||||
)
|
)
|
||||||
from langflow.graph.nodes import VectorStoreNode
|
from langflow.graph.node.types import VectorStoreNode
|
||||||
from langflow.interface.run import build_graph
|
from langflow.interface.run import build_graph
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
from langflow.utils.validate import validate_code
|
from langflow.utils.validate import validate_code
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,35 @@
|
||||||
from langflow.graph.base import Edge, Node
|
from langflow.graph.edge.base import Edge
|
||||||
from langflow.graph.graph import Graph
|
from langflow.graph.graph.base import Graph
|
||||||
|
from langflow.graph.node.base import Node
|
||||||
|
from langflow.graph.node.types import (
|
||||||
|
AgentNode,
|
||||||
|
ChainNode,
|
||||||
|
DocumentLoaderNode,
|
||||||
|
EmbeddingNode,
|
||||||
|
LLMNode,
|
||||||
|
MemoryNode,
|
||||||
|
PromptNode,
|
||||||
|
TextSplitterNode,
|
||||||
|
ToolNode,
|
||||||
|
ToolkitNode,
|
||||||
|
VectorStoreNode,
|
||||||
|
WrapperNode,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = ["Graph", "Node", "Edge"]
|
__all__ = [
|
||||||
|
"Graph",
|
||||||
|
"Node",
|
||||||
|
"Edge",
|
||||||
|
"AgentNode",
|
||||||
|
"ChainNode",
|
||||||
|
"DocumentLoaderNode",
|
||||||
|
"EmbeddingNode",
|
||||||
|
"LLMNode",
|
||||||
|
"MemoryNode",
|
||||||
|
"PromptNode",
|
||||||
|
"TextSplitterNode",
|
||||||
|
"ToolNode",
|
||||||
|
"ToolkitNode",
|
||||||
|
"VectorStoreNode",
|
||||||
|
"WrapperNode",
|
||||||
|
]
|
||||||
|
|
|
||||||
0
src/backend/langflow/graph/edge/__init__.py
Normal file
0
src/backend/langflow/graph/edge/__init__.py
Normal file
52
src/backend/langflow/graph/edge/base.py
Normal file
52
src/backend/langflow/graph/edge/base.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
from langflow.utils.logger import logger
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from langflow.graph.node.base import Node
|
||||||
|
|
||||||
|
|
||||||
|
class Edge:
|
||||||
|
def __init__(self, source: "Node", target: "Node"):
|
||||||
|
self.source: "Node" = source
|
||||||
|
self.target: "Node" = target
|
||||||
|
self.validate_edge()
|
||||||
|
|
||||||
|
def validate_edge(self) -> None:
|
||||||
|
# Validate that the outputs of the source node are valid inputs
|
||||||
|
# for the target node
|
||||||
|
self.source_types = self.source.output
|
||||||
|
self.target_reqs = self.target.required_inputs + self.target.optional_inputs
|
||||||
|
# Both lists contain strings and sometimes a string contains the value we are
|
||||||
|
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
||||||
|
# so we need to check if any of the strings in source_types is in target_reqs
|
||||||
|
self.valid = any(
|
||||||
|
output in target_req
|
||||||
|
for output in self.source_types
|
||||||
|
for target_req in self.target_reqs
|
||||||
|
)
|
||||||
|
# Get what type of input the target node is expecting
|
||||||
|
|
||||||
|
self.matched_type = next(
|
||||||
|
(
|
||||||
|
output
|
||||||
|
for output in self.source_types
|
||||||
|
for target_req in self.target_reqs
|
||||||
|
if output in target_req
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
no_matched_type = self.matched_type is None
|
||||||
|
if no_matched_type:
|
||||||
|
logger.debug(self.source_types)
|
||||||
|
logger.debug(self.target_reqs)
|
||||||
|
if no_matched_type:
|
||||||
|
raise ValueError(
|
||||||
|
f"Edge between {self.source.node_type} and {self.target.node_type} "
|
||||||
|
f"has no matched type"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}"
|
||||||
|
f", matched_type={self.matched_type})"
|
||||||
|
)
|
||||||
0
src/backend/langflow/graph/graph/__init__.py
Normal file
0
src/backend/langflow/graph/graph/__init__.py
Normal file
|
|
@ -1,38 +1,20 @@
|
||||||
from typing import Dict, List, Type, Union
|
from typing import Dict, List, Type, Union
|
||||||
|
|
||||||
from langflow.graph.base import Edge, Node
|
from langflow.graph.edge.base import Edge
|
||||||
from langflow.graph.nodes import (
|
from langflow.graph.graph.constants import NODE_TYPE_MAP
|
||||||
AgentNode,
|
from langflow.graph.node.base import Node
|
||||||
ChainNode,
|
from langflow.graph.node.types import (
|
||||||
DocumentLoaderNode,
|
|
||||||
EmbeddingNode,
|
|
||||||
FileToolNode,
|
FileToolNode,
|
||||||
LLMNode,
|
LLMNode,
|
||||||
MemoryNode,
|
|
||||||
PromptNode,
|
|
||||||
TextSplitterNode,
|
|
||||||
ToolkitNode,
|
ToolkitNode,
|
||||||
ToolNode,
|
|
||||||
VectorStoreNode,
|
|
||||||
WrapperNode,
|
|
||||||
)
|
)
|
||||||
from langflow.interface.agents.base import agent_creator
|
|
||||||
from langflow.interface.chains.base import chain_creator
|
|
||||||
from langflow.interface.document_loaders.base import documentloader_creator
|
|
||||||
from langflow.interface.embeddings.base import embedding_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.text_splitters.base import textsplitter_creator
|
|
||||||
from langflow.interface.toolkits.base import toolkits_creator
|
|
||||||
from langflow.interface.tools.base import tool_creator
|
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.interface.vector_store.base import vectorstore_creator
|
|
||||||
from langflow.interface.wrappers.base import wrapper_creator
|
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
|
"""A class representing a graph of nodes and edges."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
nodes: List[Dict[str, Union[str, Dict[str, Union[str, List[str]]]]]],
|
nodes: List[Dict[str, Union[str, Dict[str, Union[str, List[str]]]]]],
|
||||||
|
|
@ -43,6 +25,7 @@ class Graph:
|
||||||
self._build_graph()
|
self._build_graph()
|
||||||
|
|
||||||
def _build_graph(self) -> None:
|
def _build_graph(self) -> None:
|
||||||
|
"""Builds the graph from the nodes and edges."""
|
||||||
self.nodes = self._build_nodes()
|
self.nodes = self._build_nodes()
|
||||||
self.edges = self._build_edges()
|
self.edges = self._build_edges()
|
||||||
for edge in self.edges:
|
for edge in self.edges:
|
||||||
|
|
@ -51,17 +34,25 @@ class Graph:
|
||||||
|
|
||||||
# This is a hack to make sure that the LLM node is sent to
|
# This is a hack to make sure that the LLM node is sent to
|
||||||
# the toolkit node
|
# the toolkit node
|
||||||
|
self._build_node_params()
|
||||||
|
# remove invalid nodes
|
||||||
|
self._remove_invalid_nodes()
|
||||||
|
|
||||||
|
def _build_node_params(self) -> None:
|
||||||
|
"""Identifies and handles the LLM node within the graph."""
|
||||||
llm_node = None
|
llm_node = None
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
node._build_params()
|
node._build_params()
|
||||||
|
|
||||||
if isinstance(node, LLMNode):
|
if isinstance(node, LLMNode):
|
||||||
llm_node = node
|
llm_node = node
|
||||||
|
|
||||||
for node in self.nodes:
|
if llm_node:
|
||||||
if isinstance(node, ToolkitNode):
|
for node in self.nodes:
|
||||||
node.params["llm"] = llm_node
|
if isinstance(node, ToolkitNode):
|
||||||
# remove invalid nodes
|
node.params["llm"] = llm_node
|
||||||
|
|
||||||
|
def _remove_invalid_nodes(self) -> None:
|
||||||
|
"""Removes invalid nodes from the graph."""
|
||||||
self.nodes = [
|
self.nodes = [
|
||||||
node
|
node
|
||||||
for node in self.nodes
|
for node in self.nodes
|
||||||
|
|
@ -70,19 +61,23 @@ class Graph:
|
||||||
]
|
]
|
||||||
|
|
||||||
def _validate_node(self, node: Node) -> bool:
|
def _validate_node(self, node: Node) -> bool:
|
||||||
|
"""Validates a node."""
|
||||||
# All nodes that do not have edges are invalid
|
# All nodes that do not have edges are invalid
|
||||||
return len(node.edges) > 0
|
return len(node.edges) > 0
|
||||||
|
|
||||||
def get_node(self, node_id: str) -> Union[None, Node]:
|
def get_node(self, node_id: str) -> Union[None, Node]:
|
||||||
|
"""Returns a node by id."""
|
||||||
return next((node for node in self.nodes if node.id == node_id), None)
|
return next((node for node in self.nodes if node.id == node_id), None)
|
||||||
|
|
||||||
def get_nodes_with_target(self, node: Node) -> List[Node]:
|
def get_nodes_with_target(self, node: Node) -> List[Node]:
|
||||||
|
"""Returns the nodes connected to a node."""
|
||||||
connected_nodes: List[Node] = [
|
connected_nodes: List[Node] = [
|
||||||
edge.source for edge in self.edges if edge.target == node
|
edge.source for edge in self.edges if edge.target == node
|
||||||
]
|
]
|
||||||
return connected_nodes
|
return connected_nodes
|
||||||
|
|
||||||
def build(self) -> List[Node]:
|
def build(self) -> List[Node]:
|
||||||
|
"""Builds the graph."""
|
||||||
# Get root node
|
# Get root node
|
||||||
root_node = payload.get_root_node(self)
|
root_node = payload.get_root_node(self)
|
||||||
if root_node is None:
|
if root_node is None:
|
||||||
|
|
@ -90,6 +85,7 @@ class Graph:
|
||||||
return root_node.build()
|
return root_node.build()
|
||||||
|
|
||||||
def get_node_neighbors(self, node: Node) -> Dict[Node, int]:
|
def get_node_neighbors(self, node: Node) -> Dict[Node, int]:
|
||||||
|
"""Returns the neighbors of a node."""
|
||||||
neighbors: Dict[Node, int] = {}
|
neighbors: Dict[Node, int] = {}
|
||||||
for edge in self.edges:
|
for edge in self.edges:
|
||||||
if edge.source == node:
|
if edge.source == node:
|
||||||
|
|
@ -105,6 +101,7 @@ class Graph:
|
||||||
return neighbors
|
return neighbors
|
||||||
|
|
||||||
def _build_edges(self) -> List[Edge]:
|
def _build_edges(self) -> List[Edge]:
|
||||||
|
"""Builds the edges of the graph."""
|
||||||
# Edge takes two nodes as arguments, so we need to build the nodes first
|
# Edge takes two nodes as arguments, so we need to build the nodes first
|
||||||
# and then build the edges
|
# and then build the edges
|
||||||
# if we can't find a node, we raise an error
|
# if we can't find a node, we raise an error
|
||||||
|
|
@ -121,30 +118,15 @@ class Graph:
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
def _get_node_class(self, node_type: str, node_lc_type: str) -> Type[Node]:
|
def _get_node_class(self, node_type: str, node_lc_type: str) -> Type[Node]:
|
||||||
node_type_map: Dict[str, Type[Node]] = {
|
"""Returns the node class based on the node type."""
|
||||||
**{t: PromptNode for t in prompt_creator.to_list()},
|
|
||||||
**{t: AgentNode for t in agent_creator.to_list()},
|
|
||||||
**{t: ChainNode for t in chain_creator.to_list()},
|
|
||||||
**{t: ToolNode for t in tool_creator.to_list()},
|
|
||||||
**{t: ToolkitNode for t in toolkits_creator.to_list()},
|
|
||||||
**{t: WrapperNode for t in wrapper_creator.to_list()},
|
|
||||||
**{t: LLMNode for t in llm_creator.to_list()},
|
|
||||||
**{t: MemoryNode for t in memory_creator.to_list()},
|
|
||||||
**{t: EmbeddingNode for t in embedding_creator.to_list()},
|
|
||||||
**{t: VectorStoreNode for t in vectorstore_creator.to_list()},
|
|
||||||
**{t: DocumentLoaderNode for t in documentloader_creator.to_list()},
|
|
||||||
**{t: TextSplitterNode for t in textsplitter_creator.to_list()},
|
|
||||||
}
|
|
||||||
|
|
||||||
if node_type in FILE_TOOLS:
|
if node_type in FILE_TOOLS:
|
||||||
return FileToolNode
|
return FileToolNode
|
||||||
if node_type in node_type_map:
|
if node_type in NODE_TYPE_MAP:
|
||||||
return node_type_map[node_type]
|
return NODE_TYPE_MAP[node_type]
|
||||||
if node_lc_type in node_type_map:
|
return NODE_TYPE_MAP[node_lc_type] if node_lc_type in NODE_TYPE_MAP else Node
|
||||||
return node_type_map[node_lc_type]
|
|
||||||
return Node
|
|
||||||
|
|
||||||
def _build_nodes(self) -> List[Node]:
|
def _build_nodes(self) -> List[Node]:
|
||||||
|
"""Builds the nodes of the graph."""
|
||||||
nodes: List[Node] = []
|
nodes: List[Node] = []
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
node_data = node["data"]
|
node_data = node["data"]
|
||||||
|
|
@ -157,6 +139,7 @@ class Graph:
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def get_children_by_node_type(self, node: Node, node_type: str) -> List[Node]:
|
def get_children_by_node_type(self, node: Node, node_type: str) -> List[Node]:
|
||||||
|
"""Returns the children of a node based on the node type."""
|
||||||
children = []
|
children = []
|
||||||
node_types = [node.data["type"]]
|
node_types = [node.data["type"]]
|
||||||
if "node" in node.data:
|
if "node" in node.data:
|
||||||
49
src/backend/langflow/graph/graph/constants.py
Normal file
49
src/backend/langflow/graph/graph/constants.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
from langflow.graph.node.base import Node
|
||||||
|
from langflow.graph.node.types import (
|
||||||
|
AgentNode,
|
||||||
|
ChainNode,
|
||||||
|
DocumentLoaderNode,
|
||||||
|
EmbeddingNode,
|
||||||
|
LLMNode,
|
||||||
|
MemoryNode,
|
||||||
|
PromptNode,
|
||||||
|
TextSplitterNode,
|
||||||
|
ToolNode,
|
||||||
|
ToolkitNode,
|
||||||
|
VectorStoreNode,
|
||||||
|
WrapperNode,
|
||||||
|
)
|
||||||
|
from langflow.interface.agents.base import agent_creator
|
||||||
|
from langflow.interface.chains.base import chain_creator
|
||||||
|
from langflow.interface.document_loaders.base import documentloader_creator
|
||||||
|
from langflow.interface.embeddings.base import embedding_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.text_splitters.base import textsplitter_creator
|
||||||
|
from langflow.interface.toolkits.base import toolkits_creator
|
||||||
|
from langflow.interface.tools.base import tool_creator
|
||||||
|
from langflow.interface.vector_store.base import vectorstore_creator
|
||||||
|
from langflow.interface.wrappers.base import wrapper_creator
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Dict, Type
|
||||||
|
|
||||||
|
|
||||||
|
DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]
|
||||||
|
|
||||||
|
|
||||||
|
NODE_TYPE_MAP: Dict[str, Type[Node]] = {
|
||||||
|
**{t: PromptNode for t in prompt_creator.to_list()},
|
||||||
|
**{t: AgentNode for t in agent_creator.to_list()},
|
||||||
|
**{t: ChainNode for t in chain_creator.to_list()},
|
||||||
|
**{t: ToolNode for t in tool_creator.to_list()},
|
||||||
|
**{t: ToolkitNode for t in toolkits_creator.to_list()},
|
||||||
|
**{t: WrapperNode for t in wrapper_creator.to_list()},
|
||||||
|
**{t: LLMNode for t in llm_creator.to_list()},
|
||||||
|
**{t: MemoryNode for t in memory_creator.to_list()},
|
||||||
|
**{t: EmbeddingNode for t in embedding_creator.to_list()},
|
||||||
|
**{t: VectorStoreNode for t in vectorstore_creator.to_list()},
|
||||||
|
**{t: DocumentLoaderNode for t in documentloader_creator.to_list()},
|
||||||
|
**{t: TextSplitterNode for t in textsplitter_creator.to_list()},
|
||||||
|
}
|
||||||
0
src/backend/langflow/graph/node/__init__.py
Normal file
0
src/backend/langflow/graph/node/__init__.py
Normal file
|
|
@ -1,27 +1,27 @@
|
||||||
# Description: Graph class for building a graph of nodes and edges
|
|
||||||
# Insights:
|
|
||||||
# - Defer prompts building to the last moment or when they have all the tools
|
|
||||||
# - Build each inner agent first, then build the outer agent
|
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import inspect
|
|
||||||
import types
|
|
||||||
import warnings
|
|
||||||
from typing import Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from langflow.cache import base as cache_utils
|
from langflow.cache import base as cache_utils
|
||||||
from langflow.graph.constants import DIRECT_TYPES
|
from langflow.graph.node.constants import DIRECT_TYPES
|
||||||
from langflow.interface import loading
|
from langflow.interface import loading
|
||||||
from langflow.interface.listing import ALL_TYPES_DICT
|
from langflow.interface.listing import ALL_TYPES_DICT
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
from langflow.utils.util import sync_to_async
|
from langflow.utils.util import sync_to_async
|
||||||
|
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import inspect
|
||||||
|
import types
|
||||||
|
import warnings
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from langflow.graph.edge.base import Edge
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
def __init__(self, data: Dict, base_type: Optional[str] = None) -> None:
|
def __init__(self, data: Dict, base_type: Optional[str] = None) -> None:
|
||||||
self.id: str = data["id"]
|
self.id: str = data["id"]
|
||||||
self._data = data
|
self._data = data
|
||||||
self.edges: List[Edge] = []
|
self.edges: List["Edge"] = []
|
||||||
self.base_type: Optional[str] = base_type
|
self.base_type: Optional[str] = base_type
|
||||||
self._parse_data()
|
self._parse_data()
|
||||||
self._built_object = None
|
self._built_object = None
|
||||||
|
|
@ -227,50 +227,3 @@ class Node:
|
||||||
|
|
||||||
def _built_object_repr(self):
|
def _built_object_repr(self):
|
||||||
return repr(self._built_object)
|
return repr(self._built_object)
|
||||||
|
|
||||||
|
|
||||||
class Edge:
|
|
||||||
def __init__(self, source: "Node", target: "Node"):
|
|
||||||
self.source: "Node" = source
|
|
||||||
self.target: "Node" = target
|
|
||||||
self.validate_edge()
|
|
||||||
|
|
||||||
def validate_edge(self) -> None:
|
|
||||||
# Validate that the outputs of the source node are valid inputs
|
|
||||||
# for the target node
|
|
||||||
self.source_types = self.source.output
|
|
||||||
self.target_reqs = self.target.required_inputs + self.target.optional_inputs
|
|
||||||
# Both lists contain strings and sometimes a string contains the value we are
|
|
||||||
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
|
||||||
# so we need to check if any of the strings in source_types is in target_reqs
|
|
||||||
self.valid = any(
|
|
||||||
output in target_req
|
|
||||||
for output in self.source_types
|
|
||||||
for target_req in self.target_reqs
|
|
||||||
)
|
|
||||||
# Get what type of input the target node is expecting
|
|
||||||
|
|
||||||
self.matched_type = next(
|
|
||||||
(
|
|
||||||
output
|
|
||||||
for output in self.source_types
|
|
||||||
for target_req in self.target_reqs
|
|
||||||
if output in target_req
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
no_matched_type = self.matched_type is None
|
|
||||||
if no_matched_type:
|
|
||||||
logger.debug(self.source_types)
|
|
||||||
logger.debug(self.target_reqs)
|
|
||||||
if no_matched_type:
|
|
||||||
raise ValueError(
|
|
||||||
f"Edge between {self.source.node_type} and {self.target.node_type} "
|
|
||||||
f"has no matched type"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}"
|
|
||||||
f", matched_type={self.matched_type})"
|
|
||||||
)
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from langflow.graph.base import Node
|
from langflow.graph.node.base import Node
|
||||||
from langflow.graph.utils import extract_input_variables_from_prompt
|
from langflow.graph.utils import extract_input_variables_from_prompt
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,6 +12,7 @@ from langchain.agents.load_tools import (
|
||||||
_LLM_TOOLS,
|
_LLM_TOOLS,
|
||||||
)
|
)
|
||||||
from langchain.agents.loading import load_agent_from_config
|
from langchain.agents.loading import load_agent_from_config
|
||||||
|
from langflow.graph import Graph
|
||||||
from langchain.agents.tools import Tool
|
from langchain.agents.tools import Tool
|
||||||
from langchain.base_language import BaseLanguageModel
|
from langchain.base_language import BaseLanguageModel
|
||||||
from langchain.callbacks.base import BaseCallbackManager
|
from langchain.callbacks.base import BaseCallbackManager
|
||||||
|
|
@ -164,7 +165,6 @@ def instantiate_utility(node_type, class_object, params):
|
||||||
def load_flow_from_json(path: str, build=True):
|
def load_flow_from_json(path: str, build=True):
|
||||||
"""Load flow from json file"""
|
"""Load flow from json file"""
|
||||||
# This is done to avoid circular imports
|
# This is done to avoid circular imports
|
||||||
from langflow.graph import Graph
|
|
||||||
|
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
flow_graph = json.load(f)
|
flow_graph = json.load(f)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langchain.schema import AgentAction
|
||||||
|
|
||||||
from langflow.api.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler # type: ignore
|
from langflow.api.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler # type: ignore
|
||||||
from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict
|
from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict
|
||||||
from langflow.graph.graph import Graph
|
from langflow.graph import Graph
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
|
from langflow.graph.graph.base import Graph
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
@ -46,7 +47,6 @@ def client():
|
||||||
|
|
||||||
def get_graph(_type="basic"):
|
def get_graph(_type="basic"):
|
||||||
"""Get a graph from a json file"""
|
"""Get a graph from a json file"""
|
||||||
from langflow.graph.graph import Graph
|
|
||||||
|
|
||||||
if _type == "basic":
|
if _type == "basic":
|
||||||
path = pytest.BASIC_EXAMPLE_PATH
|
path = pytest.BASIC_EXAMPLE_PATH
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
from typing import Type, Union
|
from typing import Type, Union
|
||||||
|
from langflow.graph.edge.base import Edge
|
||||||
|
from langflow.graph.node.base import Node
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
from langchain.llms.fake import FakeListLLM
|
from langchain.llms.fake import FakeListLLM
|
||||||
from langflow.graph import Edge, Graph, Node
|
from langflow.graph import Graph
|
||||||
from langflow.graph.nodes import (
|
from langflow.graph.node.types import (
|
||||||
AgentNode,
|
AgentNode,
|
||||||
ChainNode,
|
ChainNode,
|
||||||
FileToolNode,
|
FileToolNode,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue