fix: Improve vertex filtering and update is_vertex_runnable logic (#2612)
* feat: improve is_vertex_runnable method in RunnableVerticesManager This commit improves the `is_vertex_runnable` method in the `RunnableVerticesManager` class. It adds an additional parameter `activated_vertices` to the method signature and updates the logic to check if a vertex is runnable based on the presence of activated vertices. This enhancement improves the accuracy of determining whether a vertex is runnable or not. * fix: add predecessors to vertices_to_run * style: fix lint issues * feat: optimize vertex filtering in useFlowStore This commit optimizes the vertex filtering logic in the `useFlowStore` function in `flowStore.ts`. It introduces a more efficient way to filter out vertices that are already being built, resulting in improved performance and accuracy. * refactor: add is_active method to Vertex class This commit adds the `is_active` method to the `Vertex` class in the `base.py` file. The `is_active` method checks if the state of the vertex is set to `ACTIVE` and returns a boolean value accordingly. This enhancement improves the readability and maintainability of the codebase. * refactor: improve is_vertex_runnable method in RunnableVerticesManager * refactor: improve find_runnable_predecessors_for_successors method in Graph class * refactor: move test_create_function
This commit is contained in:
parent
46966d164d
commit
d28fe8eedc
5 changed files with 51 additions and 36 deletions
|
|
@ -157,12 +157,14 @@ class Graph:
|
||||||
edges_set.update(_vertex.edges)
|
edges_set.update(_vertex.edges)
|
||||||
if _vertex.state == VertexStates.INACTIVE:
|
if _vertex.state == VertexStates.INACTIVE:
|
||||||
_vertex.set_state("ACTIVE")
|
_vertex.set_state("ACTIVE")
|
||||||
|
|
||||||
|
vertices_ids.add(_vertex.id)
|
||||||
edges = list(edges_set)
|
edges = list(edges_set)
|
||||||
predecessor_map, _ = self.build_adjacency_maps(edges)
|
predecessor_map, _ = self.build_adjacency_maps(edges)
|
||||||
new_predecessor_map.update(predecessor_map)
|
new_predecessor_map.update(predecessor_map)
|
||||||
|
|
||||||
# vertices_ids.update(new_predecessor_map.keys())
|
vertices_ids.update(new_predecessor_map.keys())
|
||||||
# vertices_ids.update(v_id for value_list in new_predecessor_map.values() for v_id in value_list)
|
vertices_ids.update(v_id for value_list in new_predecessor_map.values() for v_id in value_list)
|
||||||
|
|
||||||
self.activated_vertices = list(vertices_ids)
|
self.activated_vertices = list(vertices_ids)
|
||||||
self.vertices_to_run.update(vertices_ids)
|
self.vertices_to_run.update(vertices_ids)
|
||||||
|
|
@ -1459,7 +1461,7 @@ class Graph:
|
||||||
|
|
||||||
def is_vertex_runnable(self, vertex_id: str) -> bool:
|
def is_vertex_runnable(self, vertex_id: str) -> bool:
|
||||||
"""Returns whether a vertex is runnable."""
|
"""Returns whether a vertex is runnable."""
|
||||||
return self.run_manager.is_vertex_runnable(vertex_id, self.inactivated_vertices)
|
return self.run_manager.is_vertex_runnable(self.get_vertex(vertex_id))
|
||||||
|
|
||||||
def build_run_map(self):
|
def build_run_map(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1479,7 +1481,7 @@ class Graph:
|
||||||
This checks the direct predecessors of each successor to identify any that are
|
This checks the direct predecessors of each successor to identify any that are
|
||||||
immediately runnable, expanding the search to ensure progress can be made.
|
immediately runnable, expanding the search to ensure progress can be made.
|
||||||
"""
|
"""
|
||||||
return self.run_manager.find_runnable_predecessors_for_successors(vertex_id, self.inactivated_vertices)
|
return self.run_manager.find_runnable_predecessors_for_successors(self.get_vertex(vertex_id))
|
||||||
|
|
||||||
def remove_from_predecessors(self, vertex_id: str):
|
def remove_from_predecessors(self, vertex_id: str):
|
||||||
self.run_manager.remove_from_predecessors(vertex_id)
|
self.run_manager.remove_from_predecessors(vertex_id)
|
||||||
|
|
|
||||||
|
|
@ -46,33 +46,36 @@ class RunnableVerticesManager:
|
||||||
self.vertices_to_run.update(vertices_to_run)
|
self.vertices_to_run.update(vertices_to_run)
|
||||||
self.build_run_map(self.run_predecessors, self.vertices_to_run)
|
self.build_run_map(self.run_predecessors, self.vertices_to_run)
|
||||||
|
|
||||||
def is_vertex_runnable(self, vertex_id: str, inactivated_vertices: set[str]) -> bool:
|
def is_vertex_runnable(self, vertex: "Vertex") -> bool:
|
||||||
"""Determines if a vertex is runnable."""
|
"""Determines if a vertex is runnable."""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
vertex_id not in self.vertices_being_run
|
vertex.is_active() and self.are_all_predecessors_fulfilled(vertex.id) and vertex.id in self.vertices_to_run
|
||||||
and not self.run_predecessors.get(vertex_id)
|
|
||||||
and vertex_id not in inactivated_vertices
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def find_runnable_predecessors_for_successors(self, vertex_id: str, inactivated_vertices: set[str]) -> List[str]:
|
def are_all_predecessors_fulfilled(self, vertex_id: str) -> bool:
|
||||||
|
return not any(self.run_predecessors.get(vertex_id, []))
|
||||||
|
|
||||||
|
def find_runnable_predecessors_for_successors(self, vertex: "Vertex") -> List[str]:
|
||||||
"""Finds runnable predecessors for the successors of a given vertex."""
|
"""Finds runnable predecessors for the successors of a given vertex."""
|
||||||
runnable_vertices = []
|
runnable_vertices = []
|
||||||
visited = set()
|
visited = set()
|
||||||
|
get_vertex = vertex.graph.get_vertex
|
||||||
|
|
||||||
def find_runnable_predecessors(predecessor_id: str):
|
def find_runnable_predecessors(predecessor: "Vertex"):
|
||||||
|
predecessor_id = predecessor.id
|
||||||
if predecessor_id in visited:
|
if predecessor_id in visited:
|
||||||
return
|
return
|
||||||
visited.add(predecessor_id)
|
visited.add(predecessor_id)
|
||||||
if self.is_vertex_runnable(predecessor_id, inactivated_vertices):
|
if self.is_vertex_runnable(predecessor):
|
||||||
runnable_vertices.append(predecessor_id)
|
runnable_vertices.append(predecessor_id)
|
||||||
else:
|
else:
|
||||||
for pred_pred_id in self.run_predecessors.get(predecessor_id, []):
|
for pred_pred_id in self.run_predecessors.get(predecessor_id, []):
|
||||||
find_runnable_predecessors(pred_pred_id)
|
find_runnable_predecessors(get_vertex(pred_pred_id))
|
||||||
|
|
||||||
for successor_id in self.run_map.get(vertex_id, []):
|
for successor_id in self.run_map.get(vertex.id, []):
|
||||||
for predecessor_id in self.run_predecessors.get(successor_id, []):
|
for predecessor_id in self.run_predecessors.get(successor_id, []):
|
||||||
find_runnable_predecessors(predecessor_id)
|
find_runnable_predecessors(get_vertex(predecessor_id))
|
||||||
|
|
||||||
return runnable_vertices
|
return runnable_vertices
|
||||||
|
|
||||||
|
|
@ -97,7 +100,6 @@ class RunnableVerticesManager:
|
||||||
if is_runnable:
|
if is_runnable:
|
||||||
self.vertices_to_run.add(vertex_id)
|
self.vertices_to_run.add(vertex_id)
|
||||||
else:
|
else:
|
||||||
self.vertices_to_run.discard(vertex_id)
|
|
||||||
self.vertices_being_run.discard(vertex_id)
|
self.vertices_being_run.discard(vertex_id)
|
||||||
|
|
||||||
async def get_next_runnable_vertices(
|
async def get_next_runnable_vertices(
|
||||||
|
|
@ -124,14 +126,10 @@ class RunnableVerticesManager:
|
||||||
"""
|
"""
|
||||||
async with lock:
|
async with lock:
|
||||||
self.remove_vertex_from_runnables(vertex.id)
|
self.remove_vertex_from_runnables(vertex.id)
|
||||||
direct_successors_ready = [
|
direct_successors_ready = [v for v in vertex.successors_ids if self.is_vertex_runnable(graph.get_vertex(v))]
|
||||||
v for v in vertex.successors_ids if self.is_vertex_runnable(v, graph.inactivated_vertices)
|
|
||||||
]
|
|
||||||
if not direct_successors_ready:
|
if not direct_successors_ready:
|
||||||
# No direct successors ready, look for runnable predecessors of successors
|
# No direct successors ready, look for runnable predecessors of successors
|
||||||
next_runnable_vertices = self.find_runnable_predecessors_for_successors(
|
next_runnable_vertices = self.find_runnable_predecessors_for_successors(vertex)
|
||||||
vertex.id, graph.inactivated_vertices
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
next_runnable_vertices = direct_successors_ready
|
next_runnable_vertices = direct_successors_ready
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,9 @@ class Vertex:
|
||||||
elif self.state == VertexStates.ACTIVE and self.id in self.graph.inactivated_vertices:
|
elif self.state == VertexStates.ACTIVE and self.id in self.graph.inactivated_vertices:
|
||||||
self.graph.inactivated_vertices.remove(self.id)
|
self.graph.inactivated_vertices.remove(self.id)
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
return self.state == VertexStates.ACTIVE
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def avg_build_time(self):
|
def avg_build_time(self):
|
||||||
return sum(self.build_times) / len(self.build_times) if self.build_times else 0
|
return sum(self.build_times) / len(self.build_times) if self.build_times else 0
|
||||||
|
|
|
||||||
|
|
@ -510,11 +510,23 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
const top_level_vertices = vertexBuildData.top_level_vertices.filter(
|
const top_level_vertices = vertexBuildData.top_level_vertices.filter(
|
||||||
(vertex) => !vertexBuildData.inactivated_vertices?.includes(vertex),
|
(vertex) => !vertexBuildData.inactivated_vertices?.includes(vertex),
|
||||||
);
|
);
|
||||||
const nextVertices: VertexLayerElementType[] = zip(
|
let nextVertices: VertexLayerElementType[] = zip(
|
||||||
next_vertices_ids,
|
next_vertices_ids,
|
||||||
top_level_vertices,
|
top_level_vertices,
|
||||||
).map(([id, reference]) => ({ id: id!, reference }));
|
).map(([id, reference]) => ({ id: id!, reference }));
|
||||||
|
|
||||||
|
// Now we filter nextVertices to remove any vertices that are in verticesLayers
|
||||||
|
// because they are already being built
|
||||||
|
// each layer is a list of vertexlayerelementtypes
|
||||||
|
let lastLayer =
|
||||||
|
get().verticesBuild!.verticesLayers[
|
||||||
|
get().verticesBuild!.verticesLayers.length - 1
|
||||||
|
];
|
||||||
|
nextVertices = nextVertices.filter(
|
||||||
|
(vertex) =>
|
||||||
|
!lastLayer.some((layer) => layer.id === vertex.id) &&
|
||||||
|
!lastLayer.some((layer) => layer.reference === vertex.reference),
|
||||||
|
);
|
||||||
const newLayers = [
|
const newLayers = [
|
||||||
...get().verticesBuild!.verticesLayers,
|
...get().verticesBuild!.verticesLayers,
|
||||||
nextVertices,
|
nextVertices,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,20 @@ from requests.exceptions import MissingSchema
|
||||||
from langflow.utils.validate import create_function, execute_function, extract_function_name, validate_code
|
from langflow.utils.validate import create_function, execute_function, extract_function_name, validate_code
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_function():
|
||||||
|
code = """
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def my_function(x: str) -> Path:
|
||||||
|
return Path(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
function_name = extract_function_name(code)
|
||||||
|
function = create_function(code, function_name)
|
||||||
|
result = function("test")
|
||||||
|
assert result == Path("test")
|
||||||
|
|
||||||
|
|
||||||
def test_validate_code():
|
def test_validate_code():
|
||||||
# Test case with a valid import and function
|
# Test case with a valid import and function
|
||||||
code1 = """
|
code1 = """
|
||||||
|
|
@ -88,17 +102,3 @@ def my_function(x):
|
||||||
with mock.patch("requests.get", side_effect=MissingSchema):
|
with mock.patch("requests.get", side_effect=MissingSchema):
|
||||||
with pytest.raises(MissingSchema):
|
with pytest.raises(MissingSchema):
|
||||||
execute_function(code, "my_function", "invalid_url")
|
execute_function(code, "my_function", "invalid_url")
|
||||||
|
|
||||||
|
|
||||||
def test_create_function():
|
|
||||||
code = """
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def my_function(x: str) -> Path:
|
|
||||||
return Path(x)
|
|
||||||
"""
|
|
||||||
|
|
||||||
function_name = extract_function_name(code)
|
|
||||||
function = create_function(code, function_name)
|
|
||||||
result = function("test")
|
|
||||||
assert result == Path("test")
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue