Refactor graph class and add layer refinement algorithm
This commit is contained in:
parent
4bca4fd894
commit
b7427d08d3
1 changed files with 68 additions and 14 deletions
|
|
@ -6,7 +6,12 @@ from langflow.graph.edge.base import ContractEdge
|
||||||
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
||||||
from langflow.graph.graph.utils import process_flow
|
from langflow.graph.graph.utils import process_flow
|
||||||
from langflow.graph.vertex.base import Vertex
|
from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.graph.vertex.types import ChatVertex, FileToolVertex, LLMVertex, ToolkitVertex
|
from langflow.graph.vertex.types import (
|
||||||
|
ChatVertex,
|
||||||
|
FileToolVertex,
|
||||||
|
LLMVertex,
|
||||||
|
ToolkitVertex,
|
||||||
|
)
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
@ -127,7 +132,9 @@ class Graph:
|
||||||
return
|
return
|
||||||
for vertex in self.vertices:
|
for vertex in self.vertices:
|
||||||
if not self._validate_vertex(vertex):
|
if not self._validate_vertex(vertex):
|
||||||
raise ValueError(f"{vertex.vertex_type} is not connected to any other components")
|
raise ValueError(
|
||||||
|
f"{vertex.vertex_type} is not connected to any other components"
|
||||||
|
)
|
||||||
|
|
||||||
def _validate_vertex(self, vertex: Vertex) -> bool:
|
def _validate_vertex(self, vertex: Vertex) -> bool:
|
||||||
"""Validates a vertex."""
|
"""Validates a vertex."""
|
||||||
|
|
@ -140,7 +147,11 @@ class Graph:
|
||||||
|
|
||||||
def get_vertex_edges(self, vertex_id: str) -> List[ContractEdge]:
|
def get_vertex_edges(self, vertex_id: str) -> List[ContractEdge]:
|
||||||
"""Returns a list of edges for a given vertex."""
|
"""Returns a list of edges for a given vertex."""
|
||||||
return [edge for edge in self.edges if edge.source_id == vertex_id or edge.target_id == vertex_id]
|
return [
|
||||||
|
edge
|
||||||
|
for edge in self.edges
|
||||||
|
if edge.source_id == vertex_id or edge.target_id == vertex_id
|
||||||
|
]
|
||||||
|
|
||||||
def get_vertices_with_target(self, vertex_id: str) -> List[Vertex]:
|
def get_vertices_with_target(self, vertex_id: str) -> List[Vertex]:
|
||||||
"""Returns the vertices connected to a vertex."""
|
"""Returns the vertices connected to a vertex."""
|
||||||
|
|
@ -178,7 +189,9 @@ class Graph:
|
||||||
def dfs(vertex):
|
def dfs(vertex):
|
||||||
if state[vertex] == 1:
|
if state[vertex] == 1:
|
||||||
# We have a cycle
|
# We have a cycle
|
||||||
raise ValueError("Graph contains a cycle, cannot perform topological sort")
|
raise ValueError(
|
||||||
|
"Graph contains a cycle, cannot perform topological sort"
|
||||||
|
)
|
||||||
if state[vertex] == 0:
|
if state[vertex] == 0:
|
||||||
state[vertex] = 1
|
state[vertex] = 1
|
||||||
for edge in vertex.edges:
|
for edge in vertex.edges:
|
||||||
|
|
@ -237,7 +250,9 @@ class Graph:
|
||||||
edges.append(ContractEdge(source, target, edge))
|
edges.append(ContractEdge(source, target, edge))
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> Type[Vertex]:
|
def _get_vertex_class(
|
||||||
|
self, node_type: str, node_base_type: str, node_id: str
|
||||||
|
) -> Type[Vertex]:
|
||||||
"""Returns the node class based on the node type."""
|
"""Returns the node class based on the node type."""
|
||||||
# First we check for the node_base_type
|
# First we check for the node_base_type
|
||||||
node_name = node_id.split("-")[0]
|
node_name = node_id.split("-")[0]
|
||||||
|
|
@ -267,14 +282,18 @@ class Graph:
|
||||||
vertex_type: str = vertex_data["type"] # type: ignore
|
vertex_type: str = vertex_data["type"] # type: ignore
|
||||||
vertex_base_type: str = vertex_data["node"]["template"]["_type"] # type: ignore
|
vertex_base_type: str = vertex_data["node"]["template"]["_type"] # type: ignore
|
||||||
|
|
||||||
VertexClass = self._get_vertex_class(vertex_type, vertex_base_type, vertex_data["id"])
|
VertexClass = self._get_vertex_class(
|
||||||
|
vertex_type, vertex_base_type, vertex_data["id"]
|
||||||
|
)
|
||||||
vertex_instance = VertexClass(vertex, graph=self)
|
vertex_instance = VertexClass(vertex, graph=self)
|
||||||
vertex_instance.set_top_level(self.top_level_vertices)
|
vertex_instance.set_top_level(self.top_level_vertices)
|
||||||
vertices.append(vertex_instance)
|
vertices.append(vertex_instance)
|
||||||
|
|
||||||
return vertices
|
return vertices
|
||||||
|
|
||||||
def get_children_by_vertex_type(self, vertex: Vertex, vertex_type: str) -> List[Vertex]:
|
def get_children_by_vertex_type(
|
||||||
|
self, vertex: Vertex, vertex_type: str
|
||||||
|
) -> List[Vertex]:
|
||||||
"""Returns the children of a vertex based on the vertex type."""
|
"""Returns the children of a vertex based on the vertex type."""
|
||||||
children = []
|
children = []
|
||||||
vertex_types = [vertex.data["type"]]
|
vertex_types = [vertex.data["type"]]
|
||||||
|
|
@ -286,7 +305,9 @@ class Graph:
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
vertex_ids = [vertex.id for vertex in self.vertices]
|
vertex_ids = [vertex.id for vertex in self.vertices]
|
||||||
edges_repr = "\n".join([f"{edge.source_id} --> {edge.target_id}" for edge in self.edges])
|
edges_repr = "\n".join(
|
||||||
|
[f"{edge.source_id} --> {edge.target_id}" for edge in self.edges]
|
||||||
|
)
|
||||||
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
||||||
|
|
||||||
def layered_topological_sort(self):
|
def layered_topological_sort(self):
|
||||||
|
|
@ -299,7 +320,9 @@ class Graph:
|
||||||
in_degree[edge.target_id] += 1
|
in_degree[edge.target_id] += 1
|
||||||
|
|
||||||
# Queue for vertices with no incoming edges
|
# Queue for vertices with no incoming edges
|
||||||
queue = deque(vertex.id for vertex in self.vertices if in_degree[vertex.id] == 0)
|
queue = deque(
|
||||||
|
vertex.id for vertex in self.vertices if in_degree[vertex.id] == 0
|
||||||
|
)
|
||||||
layers = []
|
layers = []
|
||||||
|
|
||||||
current_layer = 0
|
current_layer = 0
|
||||||
|
|
@ -314,9 +337,40 @@ class Graph:
|
||||||
if in_degree[neighbor] == 0:
|
if in_degree[neighbor] == 0:
|
||||||
queue.append(neighbor)
|
queue.append(neighbor)
|
||||||
current_layer += 1 # Next layer
|
current_layer += 1 # Next layer
|
||||||
|
new_layers = self.refine_layers(graph, layers)
|
||||||
|
return new_layers
|
||||||
|
|
||||||
return layers
|
def refine_layers(self, graph, initial_layers):
|
||||||
return layers
|
# Map each vertex to its current layer
|
||||||
return layers
|
vertex_to_layer = {}
|
||||||
return layers
|
for layer_index, layer in enumerate(initial_layers):
|
||||||
return layers
|
for vertex in layer:
|
||||||
|
vertex_to_layer[vertex] = layer_index
|
||||||
|
|
||||||
|
# Build the adjacency list for reverse lookup (dependencies)
|
||||||
|
|
||||||
|
refined_layers = [[] for _ in initial_layers] # Start with empty layers
|
||||||
|
new_layer_index_map = defaultdict(
|
||||||
|
int
|
||||||
|
) # Map each vertex to its highest dependency layer
|
||||||
|
|
||||||
|
for vertex_id, deps in graph.items():
|
||||||
|
for dep in deps:
|
||||||
|
new_layer_index_map[vertex_id] = (
|
||||||
|
max(new_layer_index_map[vertex_id], vertex_to_layer[dep]) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for layer_index, layer in enumerate(initial_layers):
|
||||||
|
for vertex_id in layer:
|
||||||
|
# Place the vertex in the highest possible layer where its dependencies are met
|
||||||
|
new_layer_index = new_layer_index_map[vertex_id]
|
||||||
|
if new_layer_index > layer_index:
|
||||||
|
refined_layers[new_layer_index].append(vertex_id)
|
||||||
|
vertex_to_layer[vertex_id] = new_layer_index
|
||||||
|
else:
|
||||||
|
refined_layers[layer_index].append(vertex_id)
|
||||||
|
|
||||||
|
# Remove empty layers if any
|
||||||
|
refined_layers = [layer for layer in refined_layers if layer]
|
||||||
|
|
||||||
|
return refined_layers
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue