Merge branch 'better_io' into state_theories
This commit is contained in:
commit
e8d047239e
46 changed files with 593 additions and 327 deletions
|
|
@ -22,10 +22,10 @@ from langflow.services.auth.utils import get_current_active_user
|
||||||
from langflow.services.chat.service import ChatService
|
from langflow.services.chat.service import ChatService
|
||||||
from langflow.services.deps import get_chat_service, get_session, get_session_service
|
from langflow.services.deps import get_chat_service, get_session, get_session_service
|
||||||
from langflow.services.monitor.utils import log_vertex_build
|
from langflow.services.monitor.utils import log_vertex_build
|
||||||
from langflow.services.session.service import SessionService
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.graph.vertex.types import ChatVertex
|
from langflow.graph.vertex.types import ChatVertex
|
||||||
|
from langflow.services.session.service import SessionService
|
||||||
|
|
||||||
router = APIRouter(tags=["Chat"])
|
router = APIRouter(tags=["Chat"])
|
||||||
|
|
||||||
|
|
@ -49,7 +49,8 @@ async def try_running_celery_task(vertex, user_id):
|
||||||
@router.get("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
@router.get("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||||
async def get_vertices(
|
async def get_vertices(
|
||||||
flow_id: str,
|
flow_id: str,
|
||||||
component_id: Optional[str] = None,
|
stop_component_id: Optional[str] = None,
|
||||||
|
start_component_id: Optional[str] = None,
|
||||||
chat_service: "ChatService" = Depends(get_chat_service),
|
chat_service: "ChatService" = Depends(get_chat_service),
|
||||||
session=Depends(get_session),
|
session=Depends(get_session),
|
||||||
):
|
):
|
||||||
|
|
@ -60,9 +61,9 @@ async def get_vertices(
|
||||||
if cache := chat_service.get_cache(flow_id):
|
if cache := chat_service.get_cache(flow_id):
|
||||||
graph = cache.get("result")
|
graph = cache.get("result")
|
||||||
graph = build_and_cache_graph(flow_id, session, chat_service, graph)
|
graph = build_and_cache_graph(flow_id, session, chat_service, graph)
|
||||||
if component_id:
|
if stop_component_id or start_component_id:
|
||||||
try:
|
try:
|
||||||
vertices = graph.sort_vertices(component_id)
|
vertices = graph.sort_vertices(stop_component_id, start_component_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(exc)
|
logger.error(exc)
|
||||||
vertices = graph.sort_vertices()
|
vertices = graph.sort_vertices()
|
||||||
|
|
@ -94,6 +95,7 @@ async def build_vertex(
|
||||||
"""Build a vertex instead of the entire graph."""
|
"""Build a vertex instead of the entire graph."""
|
||||||
{"inputs": {"input_value": "some value"}}
|
{"inputs": {"input_value": "some value"}}
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
|
next_vertices_ids = []
|
||||||
try:
|
try:
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
cache = chat_service.get_cache(flow_id)
|
cache = chat_service.get_cache(flow_id)
|
||||||
|
|
@ -123,6 +125,10 @@ async def build_vertex(
|
||||||
artifacts = vertex.artifacts
|
artifacts = vertex.artifacts
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"No result found for vertex {vertex_id}")
|
raise ValueError(f"No result found for vertex {vertex_id}")
|
||||||
|
next_vertices_ids = vertex.successors_ids
|
||||||
|
next_vertices_ids = [
|
||||||
|
v for v in next_vertices_ids if graph.should_run_vertex(v)
|
||||||
|
]
|
||||||
|
|
||||||
result_data_response = ResultDataResponse(**result_dict.model_dump())
|
result_data_response = ResultDataResponse(**result_dict.model_dump())
|
||||||
|
|
||||||
|
|
@ -160,9 +166,16 @@ async def build_vertex(
|
||||||
graph.reset_activated_vertices()
|
graph.reset_activated_vertices()
|
||||||
chat_service.set_cache(flow_id, graph)
|
chat_service.set_cache(flow_id, graph)
|
||||||
|
|
||||||
|
# graph.stop_vertex tells us if the user asked
|
||||||
|
# to stop the build of the graph at a certain vertex
|
||||||
|
# if it is in next_vertices_ids, we need to remove other
|
||||||
|
# vertices from next_vertices_ids
|
||||||
|
if graph.stop_vertex and graph.stop_vertex in next_vertices_ids:
|
||||||
|
next_vertices_ids = [graph.stop_vertex]
|
||||||
|
|
||||||
build_response = VertexBuildResponse(
|
build_response = VertexBuildResponse(
|
||||||
inactivated_vertices=inactivated_vertices,
|
inactivated_vertices=inactivated_vertices,
|
||||||
activated_layers=activated_layers,
|
next_vertices_ids=next_vertices_ids,
|
||||||
valid=valid,
|
valid=valid,
|
||||||
params=params,
|
params=params,
|
||||||
id=vertex.id,
|
id=vertex.id,
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ class ApiKeyCreateRequest(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class VerticesOrderResponse(BaseModel):
|
class VerticesOrderResponse(BaseModel):
|
||||||
ids: List[List[str]]
|
ids: List[str]
|
||||||
run_id: UUID
|
run_id: UUID
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -230,7 +230,7 @@ class ResultDataResponse(BaseModel):
|
||||||
class VertexBuildResponse(BaseModel):
|
class VertexBuildResponse(BaseModel):
|
||||||
id: Optional[str] = None
|
id: Optional[str] = None
|
||||||
inactivated_vertices: Optional[List[str]] = None
|
inactivated_vertices: Optional[List[str]] = None
|
||||||
activated_layers: Optional[List[List[str]]] = None
|
next_vertices_ids: Optional[List[str]] = None
|
||||||
valid: bool
|
valid: bool
|
||||||
params: Optional[str]
|
params: Optional[str]
|
||||||
"""JSON string of the params."""
|
"""JSON string of the params."""
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,12 @@ class ConversationChainComponent(CustomComponent):
|
||||||
chain = ConversationChain(llm=llm)
|
chain = ConversationChain(llm=llm)
|
||||||
else:
|
else:
|
||||||
chain = ConversationChain(llm=llm, memory=memory)
|
chain = ConversationChain(llm=llm, memory=memory)
|
||||||
result = chain.invoke({chain.input_key: input_value})
|
result = chain.invoke(inputs)
|
||||||
# result is an AIMessage which is a subclass of BaseMessage
|
|
||||||
# We need to check if it is a string or a BaseMessage
|
|
||||||
result_str: Text = ""
|
|
||||||
if hasattr(result, "content") and isinstance(result.content, str):
|
if hasattr(result, "content") and isinstance(result.content, str):
|
||||||
result_str = result.content
|
result = result.content
|
||||||
elif isinstance(result, str):
|
elif isinstance(result, str):
|
||||||
result_str = result
|
result = result
|
||||||
else:
|
else:
|
||||||
# is dict
|
result = result.get("response")
|
||||||
result_str = Text(result.get("response"))
|
self.status = result
|
||||||
self.status = result_str
|
return result
|
||||||
return result_str
|
|
||||||
|
|
|
||||||
48
src/backend/langflow/components/routing/ShouldRunNext.py
Normal file
48
src/backend/langflow/components/routing/ShouldRunNext.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Implement ShouldRunNext component
|
||||||
|
from langchain_core.prompts import PromptTemplate
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.field_typing import BaseLanguageModel, Prompt
|
||||||
|
|
||||||
|
|
||||||
|
class ShouldRunNext(CustomComponent):
|
||||||
|
display_name = "Should Run Next"
|
||||||
|
description = "Decides whether to run the next component."
|
||||||
|
|
||||||
|
def build_config(self):
|
||||||
|
return {
|
||||||
|
"prompt": {
|
||||||
|
"display_name": "Prompt",
|
||||||
|
"info": "The prompt to use for the decision. It should generate a boolean response (True or False).",
|
||||||
|
},
|
||||||
|
"llm": {
|
||||||
|
"display_name": "LLM",
|
||||||
|
"info": "The language model to use for the decision.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(self, template: Prompt, llm: BaseLanguageModel, **kwargs) -> bool:
|
||||||
|
# This is a simple component that always returns True
|
||||||
|
prompt_template = PromptTemplate.from_template(template)
|
||||||
|
|
||||||
|
attributes_to_check = ["text", "page_content"]
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
for attribute in attributes_to_check:
|
||||||
|
if hasattr(value, attribute):
|
||||||
|
kwargs[key] = getattr(value, attribute)
|
||||||
|
|
||||||
|
chain = prompt_template | llm
|
||||||
|
result = chain.invoke(kwargs)
|
||||||
|
if hasattr(result, "content") and isinstance(result.content, str):
|
||||||
|
result = result.content
|
||||||
|
elif isinstance(result, str):
|
||||||
|
result = result
|
||||||
|
else:
|
||||||
|
result = result.get("response")
|
||||||
|
|
||||||
|
if result.lower() not in ["true", "false"]:
|
||||||
|
raise ValueError("The prompt should generate a boolean response (True or False).")
|
||||||
|
# The string should be the words true or false
|
||||||
|
# if not raise an error
|
||||||
|
bool_result = result.lower() == "true"
|
||||||
|
return bool_result
|
||||||
0
src/backend/langflow/components/routing/__init__.py
Normal file
0
src/backend/langflow/components/routing/__init__.py
Normal file
|
|
@ -1,5 +1,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
|
from itertools import chain
|
||||||
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Type, Union
|
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Type, Union
|
||||||
|
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
|
|
@ -59,6 +60,11 @@ class Graph:
|
||||||
self._edges = self._graph_data["edges"]
|
self._edges = self._graph_data["edges"]
|
||||||
self.inactivated_vertices: set = set()
|
self.inactivated_vertices: set = set()
|
||||||
self.activated_layers: List[List[str]] = []
|
self.activated_layers: List[List[str]] = []
|
||||||
|
self.vertices_layers = []
|
||||||
|
self.vertices_to_run = set()
|
||||||
|
self.stop_vertex = None
|
||||||
|
|
||||||
|
self.inactive_vertices: set = set()
|
||||||
self.edges: List[ContractEdge] = []
|
self.edges: List[ContractEdge] = []
|
||||||
self.vertices: List[Vertex] = []
|
self.vertices: List[Vertex] = []
|
||||||
self._build_graph()
|
self._build_graph()
|
||||||
|
|
@ -191,6 +197,14 @@ class Graph:
|
||||||
outputs.extend(run_outputs)
|
outputs.extend(run_outputs)
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
|
# vertices_layers is a list of lists ordered by the order the vertices
|
||||||
|
# should be built.
|
||||||
|
# We need to create a new method that will take the vertices_layers
|
||||||
|
# and return the next vertex to be built.
|
||||||
|
def next_vertex_to_build(self):
|
||||||
|
"""Returns the next vertex to be built."""
|
||||||
|
yield from chain.from_iterable(self.vertices_layers)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
return {
|
return {
|
||||||
|
|
@ -336,9 +350,14 @@ class Graph:
|
||||||
# Add new vertices
|
# Add new vertices
|
||||||
for vertex_id in new_vertex_ids:
|
for vertex_id in new_vertex_ids:
|
||||||
new_vertex = other.get_vertex(vertex_id)
|
new_vertex = other.get_vertex(vertex_id)
|
||||||
new_vertex.graph = self
|
|
||||||
self._add_vertex(new_vertex)
|
self._add_vertex(new_vertex)
|
||||||
|
|
||||||
|
# Now update the edges
|
||||||
|
for vertex_id in new_vertex_ids:
|
||||||
|
new_vertex = other.get_vertex(vertex_id)
|
||||||
|
self._update_edges(new_vertex)
|
||||||
|
new_vertex.graph = self
|
||||||
|
|
||||||
# Update existing vertices that have changed
|
# Update existing vertices that have changed
|
||||||
for vertex_id in existing_vertex_ids.intersection(other_vertex_ids):
|
for vertex_id in existing_vertex_ids.intersection(other_vertex_ids):
|
||||||
self_vertex = self.get_vertex(vertex_id)
|
self_vertex = self.get_vertex(vertex_id)
|
||||||
|
|
@ -385,12 +404,24 @@ class Graph:
|
||||||
_vertex._build_params()
|
_vertex._build_params()
|
||||||
|
|
||||||
def _add_vertex(self, vertex: Vertex) -> None:
|
def _add_vertex(self, vertex: Vertex) -> None:
|
||||||
"""Adds a new vertex to the graph."""
|
"""Adds a vertex to the graph."""
|
||||||
self.vertices.append(vertex)
|
self.vertices.append(vertex)
|
||||||
self.vertex_map[vertex.id] = vertex
|
self.vertex_map[vertex.id] = vertex
|
||||||
|
|
||||||
|
def add_vertex(self, vertex: Vertex) -> None:
|
||||||
|
"""Adds a new vertex to the graph."""
|
||||||
|
self._add_vertex(vertex)
|
||||||
|
self._update_edges(vertex)
|
||||||
|
|
||||||
|
def _update_edges(self, vertex: Vertex) -> None:
|
||||||
|
"""Updates the edges of a vertex."""
|
||||||
# Vertex has edges, so we need to update the edges
|
# Vertex has edges, so we need to update the edges
|
||||||
for edge in vertex.edges:
|
for edge in vertex.edges:
|
||||||
if edge.source_id in self.vertex_map and edge.target_id in self.vertex_map:
|
if (
|
||||||
|
edge not in self.edges
|
||||||
|
and edge.source_id in self.vertex_map
|
||||||
|
and edge.target_id in self.vertex_map
|
||||||
|
):
|
||||||
self.edges.append(edge)
|
self.edges.append(edge)
|
||||||
|
|
||||||
def _build_graph(self) -> None:
|
def _build_graph(self) -> None:
|
||||||
|
|
@ -722,7 +753,7 @@ class Graph:
|
||||||
)
|
)
|
||||||
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
||||||
|
|
||||||
def sort_up_to_vertex(self, vertex_id: str) -> List[Vertex]:
|
def sort_up_to_vertex(self, vertex_id: str, is_start: bool = False) -> List[Vertex]:
|
||||||
"""Cuts the graph up to a given vertex and sorts the resulting subgraph."""
|
"""Cuts the graph up to a given vertex and sorts the resulting subgraph."""
|
||||||
# Initial setup
|
# Initial setup
|
||||||
visited = set() # To keep track of visited vertices
|
visited = set() # To keep track of visited vertices
|
||||||
|
|
@ -756,11 +787,19 @@ class Graph:
|
||||||
if current_id == vertex_id:
|
if current_id == vertex_id:
|
||||||
# We should add to visited all the vertices that are successors of the current vertex
|
# We should add to visited all the vertices that are successors of the current vertex
|
||||||
# and their successors and so on
|
# and their successors and so on
|
||||||
|
# if the vertex is a start, it means we are starting from the beginning
|
||||||
|
# and getting successors
|
||||||
for successor in current_vertex.successors:
|
for successor in current_vertex.successors:
|
||||||
excluded.add(successor.id)
|
if is_start:
|
||||||
|
stack.append(successor.id)
|
||||||
|
else:
|
||||||
|
excluded.add(successor.id)
|
||||||
all_successors = get_successors(successor)
|
all_successors = get_successors(successor)
|
||||||
for successor in all_successors:
|
for successor in all_successors:
|
||||||
excluded.add(successor.id)
|
if is_start:
|
||||||
|
stack.append(successor.id)
|
||||||
|
else:
|
||||||
|
excluded.add(successor.id)
|
||||||
|
|
||||||
# Filter the original graph's vertices and edges to keep only those in `visited`
|
# Filter the original graph's vertices and edges to keep only those in `visited`
|
||||||
vertices_to_keep = [self.get_vertex(vid) for vid in visited]
|
vertices_to_keep = [self.get_vertex(vid) for vid in visited]
|
||||||
|
|
@ -871,12 +910,19 @@ class Graph:
|
||||||
|
|
||||||
return vertices_layers
|
return vertices_layers
|
||||||
|
|
||||||
def sort_vertices(self, component_id: Optional[str] = None) -> List[List[str]]:
|
def sort_vertices(
|
||||||
|
self,
|
||||||
|
stop_component_id: Optional[str] = None,
|
||||||
|
start_component_id: Optional[str] = None,
|
||||||
|
) -> List[str]:
|
||||||
"""Sorts the vertices in the graph."""
|
"""Sorts the vertices in the graph."""
|
||||||
self.mark_all_vertices("ACTIVE")
|
self.mark_all_vertices("ACTIVE")
|
||||||
if component_id:
|
if stop_component_id:
|
||||||
vertices = self.sort_up_to_vertex(component_id)
|
self.stop_vertex = stop_component_id
|
||||||
vertices_layers = self.layered_topological_sort(vertices)
|
vertices = self.sort_up_to_vertex(stop_component_id)
|
||||||
|
elif start_component_id:
|
||||||
|
vertices = self.sort_up_to_vertex(start_component_id, is_start=True)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
vertices = self.vertices
|
vertices = self.vertices
|
||||||
# without component_id we are probably running in the chat
|
# without component_id we are probably running in the chat
|
||||||
|
|
@ -886,10 +932,23 @@ class Graph:
|
||||||
vertices, filter_graphs=True
|
vertices, filter_graphs=True
|
||||||
)
|
)
|
||||||
vertices_layers = self.sort_by_avg_build_time(vertices_layers)
|
vertices_layers = self.sort_by_avg_build_time(vertices_layers)
|
||||||
vertices_layers = self.sort_chat_inputs_first(vertices_layers)
|
# vertices_layers = self.sort_chat_inputs_first(vertices_layers)
|
||||||
self.increment_run_count()
|
self.increment_run_count()
|
||||||
self._sorted_vertices_layers = vertices_layers
|
first_layer = vertices_layers[0]
|
||||||
return vertices_layers
|
# save the only the rest
|
||||||
|
self.vertices_layers = vertices_layers[1:]
|
||||||
|
self.vertices_to_run = {
|
||||||
|
vertex for vertex in chain.from_iterable(vertices_layers)
|
||||||
|
}
|
||||||
|
# Return just the first layer
|
||||||
|
return first_layer
|
||||||
|
|
||||||
|
def should_run_vertex(self, vertex_id: str) -> bool:
|
||||||
|
"""Returns whether a component should be run."""
|
||||||
|
should_run = vertex_id in self.vertices_to_run
|
||||||
|
if should_run:
|
||||||
|
self.vertices_to_run.remove(vertex_id)
|
||||||
|
return should_run
|
||||||
|
|
||||||
def sort_interface_components_first(
|
def sort_interface_components_first(
|
||||||
self, vertices_layers: List[List[str]]
|
self, vertices_layers: List[List[str]]
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,10 @@ class VertexTypesDict(LazyLoadDictBase):
|
||||||
**{t: types.DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
**{t: types.DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
||||||
**{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
|
**{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
|
||||||
**{t: types.OutputParserVertex for t in output_parser_creator.to_list()},
|
**{t: types.OutputParserVertex for t in output_parser_creator.to_list()},
|
||||||
**{t: types.CustomComponentVertex for t in custom_component_creator.to_list()},
|
**{
|
||||||
|
t: types.CustomComponentVertex
|
||||||
|
for t in custom_component_creator.to_list()
|
||||||
|
},
|
||||||
**{t: types.RetrieverVertex for t in retriever_creator.to_list()},
|
**{t: types.RetrieverVertex for t in retriever_creator.to_list()},
|
||||||
**{t: types.ChatVertex for t in CHAT_COMPONENTS},
|
**{t: types.ChatVertex for t in CHAT_COMPONENTS},
|
||||||
**{t: types.RoutingVertex for t in ROUTING_COMPONENTS},
|
**{t: types.RoutingVertex for t in ROUTING_COMPONENTS},
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,26 @@ import ast
|
||||||
import inspect
|
import inspect
|
||||||
import types
|
import types
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import (TYPE_CHECKING, Any, AsyncIterator, Callable, Coroutine,
|
from typing import (
|
||||||
Dict, Iterator, List, Optional)
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AsyncIterator,
|
||||||
|
Callable,
|
||||||
|
Coroutine,
|
||||||
|
Dict,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
)
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.graph.schema import (INPUT_COMPONENTS, OUTPUT_COMPONENTS,
|
from langflow.graph.schema import (
|
||||||
InterfaceComponentTypes, ResultData)
|
INPUT_COMPONENTS,
|
||||||
|
OUTPUT_COMPONENTS,
|
||||||
|
InterfaceComponentTypes,
|
||||||
|
ResultData,
|
||||||
|
)
|
||||||
from langflow.graph.utils import UnbuiltObject, UnbuiltResult
|
from langflow.graph.utils import UnbuiltObject, UnbuiltResult
|
||||||
from langflow.graph.vertex.utils import generate_result
|
from langflow.graph.vertex.utils import generate_result
|
||||||
from langflow.interface.initialize import loading
|
from langflow.interface.initialize import loading
|
||||||
|
|
@ -152,6 +165,10 @@ class Vertex:
|
||||||
def successors(self) -> List["Vertex"]:
|
def successors(self) -> List["Vertex"]:
|
||||||
return self.graph.get_successors(self)
|
return self.graph.get_successors(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def successors_ids(self) -> List[str]:
|
||||||
|
return self.graph.successor_map.get(self.id, [])
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return {
|
return {
|
||||||
"_data": self._data,
|
"_data": self._data,
|
||||||
|
|
@ -594,9 +611,6 @@ class Vertex:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead."
|
f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead."
|
||||||
)
|
)
|
||||||
raise ValueError(
|
|
||||||
f"{self.display_name}: You are trying to stream to a non-streamable component."
|
|
||||||
)
|
|
||||||
|
|
||||||
def _reset(self, params_update: Optional[Dict[str, Any]] = None):
|
def _reset(self, params_update: Optional[Dict[str, Any]] = None):
|
||||||
self._built = False
|
self._built = False
|
||||||
|
|
@ -606,6 +620,9 @@ class Vertex:
|
||||||
self.steps_ran = []
|
self.steps_ran = []
|
||||||
self._build_params()
|
self._build_params()
|
||||||
|
|
||||||
|
def _is_chat_input(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def build_inactive(self):
|
def build_inactive(self):
|
||||||
# Just set the results to None
|
# Just set the results to None
|
||||||
self._built = True
|
self._built = True
|
||||||
|
|
@ -632,7 +649,7 @@ class Vertex:
|
||||||
return await self.get_requester_result(requester)
|
return await self.get_requester_result(requester)
|
||||||
self._reset()
|
self._reset()
|
||||||
|
|
||||||
if self.is_input and inputs is not None:
|
if self._is_chat_input() and inputs is not None:
|
||||||
self.update_raw_params(inputs)
|
self.update_raw_params(inputs)
|
||||||
|
|
||||||
# Run steps
|
# Run steps
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import yaml
|
||||||
from langchain_core.messages import AIMessage
|
from langchain_core.messages import AIMessage
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.graph.schema import INPUT_FIELD_NAME
|
from langflow.graph.schema import INPUT_FIELD_NAME, InterfaceComponentTypes
|
||||||
from langflow.graph.utils import UnbuiltObject, flatten_list, serialize_field
|
from langflow.graph.utils import UnbuiltObject, flatten_list, serialize_field
|
||||||
from langflow.graph.vertex.base import Vertex
|
from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||||
|
|
@ -455,18 +455,30 @@ class ChatVertex(Vertex):
|
||||||
async for _ in self.stream():
|
async for _ in self.stream():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _is_chat_input(self):
|
||||||
|
return self.vertex_type == InterfaceComponentTypes.ChatInput and self.is_input
|
||||||
|
|
||||||
|
|
||||||
class RoutingVertex(Vertex):
|
class RoutingVertex(Vertex):
|
||||||
def __init__(self, data: Dict, graph):
|
def __init__(self, data: Dict, graph):
|
||||||
super().__init__(data, graph=graph, base_type="custom_components")
|
super().__init__(data, graph=graph, base_type="custom_components")
|
||||||
self.use_result = True
|
self.use_result = True
|
||||||
self.steps = [self._build, self._run]
|
self.steps = [self._build]
|
||||||
|
|
||||||
def _built_object_repr(self):
|
def _built_object_repr(self):
|
||||||
if self.artifacts and "repr" in self.artifacts:
|
if self.artifacts and "repr" in self.artifacts:
|
||||||
return self.artifacts["repr"] or super()._built_object_repr()
|
return self.artifacts["repr"] or super()._built_object_repr()
|
||||||
return super()._built_object_repr()
|
return super()._built_object_repr()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def successors_ids(self):
|
||||||
|
if isinstance(self._built_object, bool):
|
||||||
|
ids = super().successors_ids
|
||||||
|
if self._built_object:
|
||||||
|
return ids
|
||||||
|
return []
|
||||||
|
raise ValueError("RoutingVertex should return a boolean value.")
|
||||||
|
|
||||||
def _run(self, *args, **kwargs):
|
def _run(self, *args, **kwargs):
|
||||||
if self._built_object:
|
if self._built_object:
|
||||||
condition = self._built_object.get("condition")
|
condition = self._built_object.get("condition")
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||||
import { Button } from "../../../../components/ui/button";
|
import { Button } from "../../../../components/ui/button";
|
||||||
import {
|
import {
|
||||||
|
INPUT_HANDLER_HOVER,
|
||||||
LANGFLOW_SUPPORTED_TYPES,
|
LANGFLOW_SUPPORTED_TYPES,
|
||||||
|
OUTPUT_HANDLER_HOVER,
|
||||||
TOOLTIP_EMPTY,
|
TOOLTIP_EMPTY,
|
||||||
inputHandleHover,
|
|
||||||
outputHandleHover,
|
|
||||||
} from "../../../../constants/constants";
|
} from "../../../../constants/constants";
|
||||||
import { postCustomComponentUpdate } from "../../../../controllers/API";
|
import { postCustomComponentUpdate } from "../../../../controllers/API";
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
|
|
@ -182,7 +182,7 @@ export default function ParameterComponent({
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<span>{left ? inputHandleHover : outputHandleHover}</span>
|
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
|
||||||
)}
|
)}
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
|
|
@ -304,7 +304,10 @@ export default function ParameterComponent({
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={
|
className={
|
||||||
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
|
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
|
||||||
(name === "code" ? " hidden " : "")
|
((name === "code" && type === "code") ||
|
||||||
|
(name.includes("code") && proxy)
|
||||||
|
? " hidden "
|
||||||
|
: "")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import Loading from "../../components/ui/loading";
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
import { Textarea } from "../../components/ui/textarea";
|
||||||
import Xmark from "../../components/ui/xmark";
|
import Xmark from "../../components/ui/xmark";
|
||||||
import {
|
import {
|
||||||
|
STATUS_BUILD,
|
||||||
|
STATUS_BUILDING,
|
||||||
priorityFields,
|
priorityFields,
|
||||||
statusBuild,
|
|
||||||
statusBuilding,
|
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { BuildStatus } from "../../constants/enums";
|
import { BuildStatus } from "../../constants/enums";
|
||||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||||
|
|
@ -49,7 +49,12 @@ export default function GenericNode({
|
||||||
const [nodeDescription, setNodeDescription] = useState(
|
const [nodeDescription, setNodeDescription] = useState(
|
||||||
data.node?.description!
|
data.node?.description!
|
||||||
);
|
);
|
||||||
const buildStatus = useFlowStore((state) => state.flowBuildStatus[data.id]);
|
const buildStatus = useFlowStore(
|
||||||
|
(state) => state.flowBuildStatus[data.id]?.status
|
||||||
|
);
|
||||||
|
const lastRunTime = useFlowStore(
|
||||||
|
(state) => state.flowBuildStatus[data.id]?.timestamp
|
||||||
|
);
|
||||||
const [validationStatus, setValidationStatus] =
|
const [validationStatus, setValidationStatus] =
|
||||||
useState<validationStatusType | null>(null);
|
useState<validationStatusType | null>(null);
|
||||||
const [handles, setHandles] = useState<number>(0);
|
const [handles, setHandles] = useState<number>(0);
|
||||||
|
|
@ -332,8 +337,8 @@ export default function GenericNode({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="group flex items-center gap-2.5">
|
<div className="group flex items-start gap-1.5">
|
||||||
<ShadTooltip content={data.id}>
|
<ShadTooltip content={data.node?.display_name}>
|
||||||
<div
|
<div
|
||||||
onDoubleClick={(event) => {
|
onDoubleClick={(event) => {
|
||||||
if (nameEditable) {
|
if (nameEditable) {
|
||||||
|
|
@ -359,8 +364,8 @@ export default function GenericNode({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Pencil"
|
name="PencilLine"
|
||||||
className="hidden h-4 w-4 animate-pulse text-status-blue group-hover:block"
|
className="hidden h-3 w-3 text-status-blue group-hover:block"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -390,13 +395,34 @@ export default function GenericNode({
|
||||||
})}
|
})}
|
||||||
data={data}
|
data={data}
|
||||||
color={
|
color={
|
||||||
nodeColors[
|
data.node?.template[templateField].input_types &&
|
||||||
types[data.node?.template[templateField].type!]
|
data.node?.template[templateField].input_types!
|
||||||
] ??
|
.length > 0
|
||||||
nodeColors[
|
? nodeColors[
|
||||||
data.node?.template[templateField].type!
|
data.node?.template[templateField]
|
||||||
] ??
|
.input_types![
|
||||||
nodeColors.unknown
|
data.node?.template[templateField]
|
||||||
|
.input_types!.length - 1
|
||||||
|
]
|
||||||
|
] ??
|
||||||
|
nodeColors[
|
||||||
|
types[
|
||||||
|
data.node?.template[templateField]
|
||||||
|
.input_types![
|
||||||
|
data.node?.template[templateField]
|
||||||
|
.input_types!.length - 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
: nodeColors[
|
||||||
|
data.node?.template[templateField].type!
|
||||||
|
] ??
|
||||||
|
nodeColors[
|
||||||
|
types[
|
||||||
|
data.node?.template[templateField].type!
|
||||||
|
]
|
||||||
|
] ??
|
||||||
|
nodeColors.unknown
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
|
|
@ -458,23 +484,14 @@ export default function GenericNode({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showNode && (
|
{showNode && (
|
||||||
<Button
|
<Button variant="secondary" className={"group h-9 px-1.5"}>
|
||||||
variant="secondary"
|
|
||||||
className={"group h-9 px-1.5"}
|
|
||||||
onClick={() => {
|
|
||||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
|
||||||
return;
|
|
||||||
setValidationStatus(null);
|
|
||||||
buildFlow({ nodeId: data.id });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
buildStatus === BuildStatus.BUILDING ? (
|
buildStatus === BuildStatus.BUILDING ? (
|
||||||
<span> {statusBuilding} </span>
|
<span> {STATUS_BUILDING} </span>
|
||||||
) : !validationStatus ? (
|
) : !validationStatus ? (
|
||||||
<span className="flex">{statusBuild}</span>
|
<span className="flex">{STATUS_BUILD}</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="max-h-96 overflow-auto">
|
<div className="max-h-96 overflow-auto">
|
||||||
{typeof validationStatus.params === "string"
|
{typeof validationStatus.params === "string"
|
||||||
|
|
@ -489,7 +506,15 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
>
|
>
|
||||||
<div className="generic-node-status-position flex items-center justify-center">
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||||
|
return;
|
||||||
|
setValidationStatus(null);
|
||||||
|
buildFlow({ stopNodeId: data.id });
|
||||||
|
}}
|
||||||
|
className="generic-node-status-position flex items-center justify-center"
|
||||||
|
>
|
||||||
{renderIconStatus(buildStatus, validationStatus)}
|
{renderIconStatus(buildStatus, validationStatus)}
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
@ -607,13 +632,18 @@ export default function GenericNode({
|
||||||
data.node?.template[templateField].input_types!
|
data.node?.template[templateField].input_types!
|
||||||
.length > 0
|
.length > 0
|
||||||
? nodeColors[
|
? nodeColors[
|
||||||
data.node?.template[templateField]
|
data.node?.template[templateField].input_types![
|
||||||
.input_types![0]
|
data.node?.template[templateField]
|
||||||
|
.input_types!.length - 1
|
||||||
|
]
|
||||||
] ??
|
] ??
|
||||||
nodeColors[
|
nodeColors[
|
||||||
types[
|
types[
|
||||||
data.node?.template[templateField]
|
data.node?.template[templateField]
|
||||||
.input_types![0]
|
.input_types![
|
||||||
|
data.node?.template[templateField]
|
||||||
|
.input_types!.length - 1
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
: nodeColors[
|
: nodeColors[
|
||||||
|
|
@ -695,6 +725,15 @@ export default function GenericNode({
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<div>
|
||||||
|
{lastRunTime && (
|
||||||
|
<div className="flex justify-center text-muted-foreground">
|
||||||
|
{lastRunTime.split("\n").map((line, index) => (
|
||||||
|
<div key={index}>{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "../../components/ui/popover";
|
} from "../../components/ui/popover";
|
||||||
import { zeroNotifications } from "../../constants/constants";
|
import { ZERO_NOTIFICATIONS } from "../../constants/constants";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import { AlertDropdownType } from "../../types/alerts";
|
import { AlertDropdownType } from "../../types/alerts";
|
||||||
import SingleAlert from "./components/singleAlertComponent";
|
import SingleAlert from "./components/singleAlertComponent";
|
||||||
|
|
@ -70,7 +70,7 @@ export default function AlertDropdown({
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
||||||
{zeroNotifications}
|
{ZERO_NOTIFICATIONS}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default function IOOutputView({
|
||||||
case "TextOutput":
|
case "TextOutput":
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full h-full custom-scroll"
|
className="w-full custom-scroll"
|
||||||
placeholder={"Empty"}
|
placeholder={"Empty"}
|
||||||
// update to real value on flowPool
|
// update to real value on flowPool
|
||||||
value={
|
value={
|
||||||
|
|
@ -31,7 +31,7 @@ export default function IOOutputView({
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full h-full custom-scroll"
|
className="w-full custom-scroll"
|
||||||
placeholder={"Enter text..."}
|
placeholder={"Enter text..."}
|
||||||
value={node.data.node!.template["input_value"]}
|
value={node.data.node!.template["input_value"]}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
CHAT_FORM_DIALOG_SUBTITLE,
|
CHAT_FORM_DIALOG_SUBTITLE,
|
||||||
outputsModalTitle,
|
OUTPUTS_MODAL_TITLE,
|
||||||
textInputModalTitle,
|
TEXT_INPUT_MODAL_TITLE,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import BaseModal from "../../modals/baseModal";
|
import BaseModal from "../../modals/baseModal";
|
||||||
import useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
|
import { NodeType } from "../../types/flow";
|
||||||
import { updateVerticesOrder } from "../../utils/buildUtils";
|
import { updateVerticesOrder } from "../../utils/buildUtils";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
import AccordionComponent from "../AccordionComponent";
|
import AccordionComponent from "../AccordionComponent";
|
||||||
|
|
@ -51,6 +52,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
const [chatValue, setChatValue] = useState("");
|
const [chatValue, setChatValue] = useState("");
|
||||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||||
|
const setNode = useFlowStore((state) => state.setNode);
|
||||||
|
|
||||||
async function updateVertices() {
|
async function updateVertices() {
|
||||||
return updateVerticesOrder(currentFlow!.id, null);
|
return updateVerticesOrder(currentFlow!.id, null);
|
||||||
|
|
@ -68,12 +70,22 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
setLockChat(true);
|
setLockChat(true);
|
||||||
setChatValue("");
|
setChatValue("");
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
await buildFlow({ input_value: chatValue }).catch((err) => {
|
await buildFlow({
|
||||||
|
input_value: chatValue,
|
||||||
|
startNodeId: chatInput?.id,
|
||||||
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setLockChat(false);
|
setLockChat(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLockChat(false);
|
setLockChat(false);
|
||||||
|
if (chatInput) {
|
||||||
|
setNode(chatInput.id, (node: NodeType) => {
|
||||||
|
const newNode = { ...node };
|
||||||
|
newNode.data.node!.template["input_value"].value = chatValue;
|
||||||
|
return newNode;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -135,7 +147,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
>
|
>
|
||||||
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
||||||
<IconComponent className="h-4 w-4" name={"Type"} />
|
<IconComponent className="h-4 w-4" name={"Type"} />
|
||||||
{textInputModalTitle}
|
{TEXT_INPUT_MODAL_TITLE}
|
||||||
</div>
|
</div>
|
||||||
{nodes
|
{nodes
|
||||||
.filter((node) =>
|
.filter((node) =>
|
||||||
|
|
@ -196,7 +208,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
>
|
>
|
||||||
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
||||||
<IconComponent className="h-4 w-4" name={"FileType2"} />
|
<IconComponent className="h-4 w-4" name={"FileType2"} />
|
||||||
{outputsModalTitle}
|
{OUTPUTS_MODAL_TITLE}
|
||||||
</div>
|
</div>
|
||||||
{nodes
|
{nodes
|
||||||
.filter((node) =>
|
.filter((node) =>
|
||||||
|
|
@ -220,7 +232,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Badge variant="gray" size="md">
|
<Badge variant="gray" size="md">
|
||||||
{output.displayName}
|
{node.data.node.display_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
@ -263,7 +275,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{haveChat ? (
|
{haveChat ? (
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full min-w-96 flex-grow">
|
||||||
{selectedViewField && (
|
{selectedViewField && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ export default function CodeAreaComponent({
|
||||||
setNodeClass,
|
setNodeClass,
|
||||||
id = "",
|
id = "",
|
||||||
readonly = false,
|
readonly = false,
|
||||||
openModal,
|
open,
|
||||||
|
setOpen,
|
||||||
}: CodeAreaComponentType) {
|
}: CodeAreaComponentType) {
|
||||||
const [myValue, setMyValue] = useState(
|
const [myValue, setMyValue] = useState(
|
||||||
typeof value == "string" ? value : JSON.stringify(value)
|
typeof value == "string" ? value : JSON.stringify(value)
|
||||||
|
|
@ -33,7 +34,8 @@ export default function CodeAreaComponent({
|
||||||
return (
|
return (
|
||||||
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
||||||
<CodeAreaModal
|
<CodeAreaModal
|
||||||
openModal={openModal}
|
open={open}
|
||||||
|
setOpen={setOpen}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
dynamic={dynamic}
|
dynamic={dynamic}
|
||||||
value={myValue}
|
value={myValue}
|
||||||
|
|
|
||||||
|
|
@ -193,9 +193,9 @@ export default function CodeTabsComponent({
|
||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
className="mt-0 h-full w-full overflow-auto custom-scroll"
|
language={tab.language}
|
||||||
language={tab.mode}
|
|
||||||
style={oneDark}
|
style={oneDark}
|
||||||
|
className="mt-0 h-full overflow-auto custom-scroll rounded-sm text-left"
|
||||||
>
|
>
|
||||||
{tab.code}
|
{tab.code}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Node } from "reactflow";
|
import { Node } from "reactflow";
|
||||||
import { savedHover } from "../../../../constants/constants";
|
import { SAVED_HOVER } from "../../../../constants/constants";
|
||||||
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
|
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
|
|
@ -128,7 +128,7 @@ export const MenuBar = ({
|
||||||
</div>
|
</div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
savedHover +
|
SAVED_HOVER +
|
||||||
new Date(currentFlow.updated_at ?? "").toLocaleString("en-US", {
|
new Date(currentFlow.updated_at ?? "").toLocaleString("en-US", {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { useEffect, useState } from "react";
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
import IconComponent from "../../../components/genericIconComponent";
|
||||||
import { Textarea } from "../../../components/ui/textarea";
|
import { Textarea } from "../../../components/ui/textarea";
|
||||||
import {
|
import {
|
||||||
chatInputPlaceholder,
|
CHAT_INPUT_PLACEHOLDER,
|
||||||
chatInputPlaceholderSend,
|
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||||
} from "../../../constants/constants";
|
} from "../../../constants/constants";
|
||||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||||
import { chatInputType } from "../../../types/components";
|
import { chatInputType } from "../../../types/components";
|
||||||
|
|
@ -84,7 +84,7 @@ export default function ChatInput({
|
||||||
"form-modal-lockchat"
|
"form-modal-lockchat"
|
||||||
)}
|
)}
|
||||||
placeholder={
|
placeholder={
|
||||||
noInput ? chatInputPlaceholder : chatInputPlaceholderSend
|
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className="form-modal-send-icon-position">
|
<div className="form-modal-send-icon-position">
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ export default function ChatMessage({
|
||||||
chat.isSend ? "" : " "
|
chat.isSend ? "" : " "
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={classNames("form-modal-chatbot-icon ")}>
|
<div className={classNames("form-modal-chatbot-icon")}>
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="form-modal-chat-image">
|
<div className="form-modal-chat-image">
|
||||||
<div className="form-modal-chat-bot-icon ">
|
<div className="form-modal-chat-bot-icon ">
|
||||||
|
|
@ -134,7 +134,7 @@ export default function ChatMessage({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="form-modal-chat-text-position">
|
<div className="form-modal-chat-text-position flex-grow min-w-96">
|
||||||
<div className="form-modal-chat-text">
|
<div className="form-modal-chat-text">
|
||||||
{hidden && chat.thought && chat.thought !== "" && (
|
{hidden && chat.thought && chat.thought !== "" && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -155,9 +155,9 @@ export default function ChatMessage({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
||||||
<div className="w-full">
|
<div className="w-full flex flex-col">
|
||||||
<div className="w-full dark:text-white">
|
<div className="w-full flex flex-col dark:text-white">
|
||||||
<div className="w-full">
|
<div className="w-full flex flex-col">
|
||||||
{useMemo(
|
{useMemo(
|
||||||
() =>
|
() =>
|
||||||
chatMessage === "" && lockChat ? (
|
chatMessage === "" && lockChat ? (
|
||||||
|
|
@ -169,7 +169,7 @@ export default function ChatMessage({
|
||||||
<Markdown
|
<Markdown
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
rehypePlugins={[rehypeMathjax]}
|
rehypePlugins={[rehypeMathjax]}
|
||||||
className="markdown prose min-w-full text-primary word-break-break-word
|
className="markdown flex flex-col prose text-primary word-break-break-word
|
||||||
dark:prose-invert"
|
dark:prose-invert"
|
||||||
components={{
|
components={{
|
||||||
pre({ node, ...props }) {
|
pre({ node, ...props }) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { useEffect, useRef, useState } from "react";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
|
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
chatFirstInitialText,
|
CHAT_FIRST_INITIAL_TEXT,
|
||||||
chatSecondInitialText,
|
CHAT_SECOND_INITIAL_TEXT,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { deleteFlowPool } from "../../controllers/API";
|
import { deleteFlowPool } from "../../controllers/API";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
|
|
@ -182,14 +182,14 @@ export default function NewChatView({
|
||||||
<br />
|
<br />
|
||||||
<div className="langflow-chat-desc">
|
<div className="langflow-chat-desc">
|
||||||
<span className="langflow-chat-desc-span">
|
<span className="langflow-chat-desc-span">
|
||||||
{chatFirstInitialText}{" "}
|
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
||||||
<span>
|
<span>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="MessageSquare"
|
name="MessageSquare"
|
||||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||||
/>
|
/>
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
{chatSecondInitialText}
|
{CHAT_SECOND_INITIAL_TEXT}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { editTextModalTitle } from "../../constants/constants";
|
import { EDIT_TEXT_MODAL_TITLE } from "../../constants/constants";
|
||||||
import { TypeModal } from "../../constants/enums";
|
import { TypeModal } from "../../constants/enums";
|
||||||
import GenericModal from "../../modals/genericModal";
|
import GenericModal from "../../modals/genericModal";
|
||||||
import { TextAreaComponentType } from "../../types/components";
|
import { TextAreaComponentType } from "../../types/components";
|
||||||
|
|
@ -38,7 +38,7 @@ export default function TextAreaComponent({
|
||||||
<GenericModal
|
<GenericModal
|
||||||
type={TypeModal.TEXT}
|
type={TypeModal.TEXT}
|
||||||
buttonText="Finish Editing"
|
buttonText="Finish Editing"
|
||||||
modalTitle={editTextModalTitle}
|
modalTitle={EDIT_TEXT_MODAL_TITLE}
|
||||||
value={value}
|
value={value}
|
||||||
setValue={(value: string) => {
|
setValue={(value: string) => {
|
||||||
onChange(value);
|
onChange(value);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
"flex flex-col fixed left-[50%] top-[50%] z-50 w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -56,3 +56,6 @@ export const USER_ADD_SUCCESS_ALERT = "Success! New user added!";
|
||||||
export const DEL_KEY_SUCCESS_ALERT = "Success! Key deleted!";
|
export const DEL_KEY_SUCCESS_ALERT = "Success! Key deleted!";
|
||||||
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
||||||
export const SAVE_SUCCESS_ALERT = "Changes saved successfully!";
|
export const SAVE_SUCCESS_ALERT = "Changes saved successfully!";
|
||||||
|
|
||||||
|
// Generic Node
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,38 +691,39 @@ export const priorityFields = new Set(["code", "template"]);
|
||||||
export const INPUT_TYPES = new Set(["ChatInput", "TextInput"]);
|
export const INPUT_TYPES = new Set(["ChatInput", "TextInput"]);
|
||||||
export const OUTPUT_TYPES = new Set(["ChatOutput", "TextOutput"]);
|
export const OUTPUT_TYPES = new Set(["ChatOutput", "TextOutput"]);
|
||||||
|
|
||||||
export const chatFirstInitialText =
|
export const CHAT_FIRST_INITIAL_TEXT =
|
||||||
"Start a conversation and click the agent's thoughts";
|
"Start a conversation and click the agent's thoughts";
|
||||||
|
|
||||||
export const chatSecondInitialText = "to inspect the chaining process.";
|
export const CHAT_SECOND_INITIAL_TEXT = "to inspect the chaining process.";
|
||||||
|
|
||||||
export const zeroNotifications = "No new notifications";
|
export const ZERO_NOTIFICATIONS = "No new notifications";
|
||||||
|
|
||||||
export const successBuild = "Built sucessfully ✨";
|
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
||||||
|
|
||||||
export const alertSaveWApi =
|
export const ALERT_SAVE_WITH_API =
|
||||||
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
||||||
|
|
||||||
export const saveWApiCheckbox = "Save with my API keys";
|
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||||
export const editTextModalTitle = "Edit Text";
|
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||||
export const editTextPlaceholder = "Type message here.";
|
export const EDIT_TEXT_PLACEHOLDER = "Type message here.";
|
||||||
export const inputHandleHover = "Avaliable input components:";
|
export const INPUT_HANDLER_HOVER = "Avaliable input components:";
|
||||||
export const outputHandleHover = "Avaliable output components:";
|
export const OUTPUT_HANDLER_HOVER = "Avaliable output components:";
|
||||||
export const textInputModalTitle = "Text Inputs";
|
export const TEXT_INPUT_MODAL_TITLE = "Text Inputs";
|
||||||
export const outputsModalTitle = "Text Outputs";
|
export const OUTPUTS_MODAL_TITLE = "Text Outputs";
|
||||||
export const langflowChatTitle = "Langflow Chat";
|
export const LANGFLOW_CHAT_TITLE = "Langflow Chat";
|
||||||
export const chatInputPlaceholder =
|
export const CHAT_INPUT_PLACEHOLDER =
|
||||||
"No chat input variables found. Click to run your flow.";
|
"No chat input variables found. Click to run your flow.";
|
||||||
export const chatInputPlaceholderSend = "Send a message...";
|
export const CHAT_INPUT_PLACEHOLDER_SEND = "Send a message...";
|
||||||
export const editCodeTitle = "Edit Code";
|
export const EDIT_CODE_TITLE = "Edit Code";
|
||||||
export const myCollectionDesc =
|
export const MY_COLLECTION_DESC =
|
||||||
"Manage your personal projects. Download and upload entire collections.";
|
"Manage your personal projects. Download and upload entire collections.";
|
||||||
export const storeDesc = "Explore community-shared flows and components.";
|
export const STORE_DESC = "Explore community-shared flows and components.";
|
||||||
export const storeTitle = "Langflow Store";
|
export const STORE_TITLE = "Langflow Store";
|
||||||
export const noApi = "You don't have an API key. ";
|
export const NO_API_KEY = "You don't have an API key. ";
|
||||||
export const insertApi = "Insert your Langflow API key.";
|
export const INSERT_API_KEY = "Insert your Langflow API key.";
|
||||||
export const invalidApi = "Your API key is not valid. ";
|
export const INVALID_API_KEY = "Your API key is not valid. ";
|
||||||
export const createApi = `Don’t have an API key? Sign up at`;
|
export const CREATE_API_KEY = `Don’t have an API key? Sign up at`;
|
||||||
export const statusBuild = "Build to validate status.";
|
export const STATUS_BUILD = "Build to validate status.";
|
||||||
export const statusBuilding = "Building...";
|
export const STATUS_BUILDING = "Building...";
|
||||||
export const savedHover = "Last saved at ";
|
export const SAVED_HOVER = "Last saved at ";
|
||||||
|
export const RUN_TIMESTAMP_PREFIX = "Last Run: ";
|
||||||
|
|
|
||||||
|
|
@ -856,13 +856,16 @@ export async function requestLogout() {
|
||||||
|
|
||||||
export async function getVerticesOrder(
|
export async function getVerticesOrder(
|
||||||
flowId: string,
|
flowId: string,
|
||||||
nodeId?: string | null
|
startNodeId?: string | null,
|
||||||
|
stopNodeId?: string | null
|
||||||
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
|
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
|
||||||
// nodeId is optional and is a query parameter
|
// nodeId is optional and is a query parameter
|
||||||
// if nodeId is not provided, the API will return all vertices
|
// if nodeId is not provided, the API will return all vertices
|
||||||
const config = {};
|
const config = {};
|
||||||
if (nodeId) {
|
if (stopNodeId) {
|
||||||
config["params"] = { component_id: nodeId };
|
config["params"] = { stop_component_id: stopNodeId };
|
||||||
|
} else if (startNodeId) {
|
||||||
|
config["params"] = { start_component_id: startNodeId };
|
||||||
}
|
}
|
||||||
return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config);
|
return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config);
|
||||||
}
|
}
|
||||||
|
|
@ -872,6 +875,7 @@ export async function postBuildVertex(
|
||||||
vertexId: string,
|
vertexId: string,
|
||||||
input_value: string
|
input_value: string
|
||||||
): Promise<AxiosResponse<VertexBuildTypeAPI>> {
|
): Promise<AxiosResponse<VertexBuildTypeAPI>> {
|
||||||
|
// input_value is optional and is a query parameter
|
||||||
return await api.post(
|
return await api.post(
|
||||||
`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`,
|
`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`,
|
||||||
input_value ? { inputs: { input_value: input_value } } : undefined
|
input_value ? { inputs: { input_value: input_value } } : undefined
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,8 @@ const EditNodeModal = forwardRef(
|
||||||
)
|
)
|
||||||
) ?? false;
|
) ?? false;
|
||||||
return (
|
return (
|
||||||
<TableRow key={index} className="h-10">
|
<TableRow key={index} className={"h-10 " + ((templateParam==="code" && myData.node?.template[templateParam].type==="code") || (templateParam.includes("code") && myData.node?.template[templateParam].proxy) ? " hidden " : "")
|
||||||
|
}>
|
||||||
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import {
|
||||||
API_SUCCESS_ALERT,
|
API_SUCCESS_ALERT,
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
createApi,
|
CREATE_API_KEY,
|
||||||
insertApi,
|
INSERT_API_KEY,
|
||||||
invalidApi,
|
INVALID_API_KEY,
|
||||||
noApi,
|
NO_API_KEY,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { addApiKeyStore } from "../../controllers/API";
|
import { addApiKeyStore } from "../../controllers/API";
|
||||||
|
|
@ -68,8 +68,11 @@ export default function StoreApiKeyModal({
|
||||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||||
<BaseModal.Header
|
<BaseModal.Header
|
||||||
description={
|
description={
|
||||||
(hasApiKey && !validApiKey ? invalidApi : !hasApiKey ? noApi : "") +
|
(hasApiKey && !validApiKey
|
||||||
insertApi
|
? INVALID_API_KEY
|
||||||
|
: !hasApiKey
|
||||||
|
? NO_API_KEY
|
||||||
|
: "") + INSERT_API_KEY
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="pr-2">API Key</span>
|
<span className="pr-2">API Key</span>
|
||||||
|
|
@ -104,7 +107,7 @@ export default function StoreApiKeyModal({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end justify-between">
|
<div className="flex items-end justify-between">
|
||||||
<span className="pr-1 text-xs text-muted-foreground">
|
<span className="pr-1 text-xs text-muted-foreground">
|
||||||
{createApi}{" "}
|
{CREATE_API_KEY}{" "}
|
||||||
<a
|
<a
|
||||||
className="text-high-indigo underline"
|
className="text-high-indigo underline"
|
||||||
href="https://langflow.store/"
|
href="https://langflow.store/"
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ function BaseModal({
|
||||||
minWidth = "min-w-[60vw]";
|
minWidth = "min-w-[60vw]";
|
||||||
break;
|
break;
|
||||||
case "large":
|
case "large":
|
||||||
minWidth = "min-w-[80vw]";
|
minWidth = "min-w-[85vw]";
|
||||||
height = "h-[80vh]";
|
height = "h-[80vh]";
|
||||||
break;
|
break;
|
||||||
case "large-thin":
|
case "large-thin":
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import {
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
CODE_PROMPT_DIALOG_SUBTITLE,
|
CODE_PROMPT_DIALOG_SUBTITLE,
|
||||||
editCodeTitle,
|
EDIT_CODE_TITLE,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
|
|
@ -35,9 +35,14 @@ export default function CodeAreaModal({
|
||||||
children,
|
children,
|
||||||
dynamic,
|
dynamic,
|
||||||
readonly = false,
|
readonly = false,
|
||||||
openModal,
|
open: myOpen,
|
||||||
|
setOpen: mySetOpen,
|
||||||
}: codeAreaModalPropsType): JSX.Element {
|
}: codeAreaModalPropsType): JSX.Element {
|
||||||
const [code, setCode] = useState(value);
|
const [code, setCode] = useState(value);
|
||||||
|
const [open, setOpen] =
|
||||||
|
mySetOpen !== undefined && myOpen !== undefined
|
||||||
|
? [myOpen, mySetOpen]
|
||||||
|
: useState(false);
|
||||||
const dark = useDarkStore((state) => state.dark);
|
const dark = useDarkStore((state) => state.dark);
|
||||||
const unselectAll = useFlowStore((state) => state.unselectAll);
|
const unselectAll = useFlowStore((state) => state.unselectAll);
|
||||||
|
|
||||||
|
|
@ -56,10 +61,6 @@ export default function CodeAreaModal({
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (openModal) setOpen(true);
|
|
||||||
}, [openModal]);
|
|
||||||
|
|
||||||
function processNonDynamicField() {
|
function processNonDynamicField() {
|
||||||
postValidateCode(code)
|
postValidateCode(code)
|
||||||
.then((apiReturn) => {
|
.then((apiReturn) => {
|
||||||
|
|
@ -143,8 +144,6 @@ export default function CodeAreaModal({
|
||||||
};
|
};
|
||||||
}, [error, setHeight]);
|
}, [error, setHeight]);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCode(value);
|
setCode(value);
|
||||||
}, [value, open]);
|
}, [value, open]);
|
||||||
|
|
@ -153,7 +152,7 @@ export default function CodeAreaModal({
|
||||||
<BaseModal open={open} setOpen={setOpen}>
|
<BaseModal open={open} setOpen={setOpen}>
|
||||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||||
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
|
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
|
||||||
<span className="pr-2"> {editCodeTitle} </span>
|
<span className="pr-2"> {EDIT_CODE_TITLE} </span>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="prompts"
|
name="prompts"
|
||||||
className="h-6 w-6 pl-1 text-primary "
|
className="h-6 w-6 pl-1 text-primary "
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import { Button } from "../../components/ui/button";
|
||||||
import { Checkbox } from "../../components/ui/checkbox";
|
import { Checkbox } from "../../components/ui/checkbox";
|
||||||
import { API_WARNING_NOTICE_ALERT } from "../../constants/alerts_constants";
|
import { API_WARNING_NOTICE_ALERT } from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
|
ALERT_SAVE_WITH_API,
|
||||||
EXPORT_DIALOG_SUBTITLE,
|
EXPORT_DIALOG_SUBTITLE,
|
||||||
alertSaveWApi,
|
SAVE_WITH_API_CHECKBOX,
|
||||||
saveWApiCheckbox,
|
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
import { useDarkStore } from "../../stores/darkStore";
|
||||||
|
|
@ -56,10 +56,12 @@ const ExportModal = forwardRef(
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="terms" className="export-modal-save-api text-sm ">
|
<label htmlFor="terms" className="export-modal-save-api text-sm ">
|
||||||
{saveWApiCheckbox}
|
{SAVE_WITH_API_CHECKBOX}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<span className=" text-xs text-destructive ">{alertSaveWApi}</span>
|
<span className=" text-xs text-destructive ">
|
||||||
|
{ALERT_SAVE_WITH_API}
|
||||||
|
</span>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
|
||||||
<BaseModal.Footer>
|
<BaseModal.Footer>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { useEffect } from "react";
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
import IconComponent from "../../../components/genericIconComponent";
|
||||||
import { Textarea } from "../../../components/ui/textarea";
|
import { Textarea } from "../../../components/ui/textarea";
|
||||||
import {
|
import {
|
||||||
chatInputPlaceholder,
|
CHAT_INPUT_PLACEHOLDER,
|
||||||
chatInputPlaceholderSend,
|
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||||
} from "../../../constants/constants";
|
} from "../../../constants/constants";
|
||||||
import { chatInputType } from "../../../types/components";
|
import { chatInputType } from "../../../types/components";
|
||||||
import { classNames } from "../../../utils/utils";
|
import { classNames } from "../../../utils/utils";
|
||||||
|
|
@ -55,7 +55,7 @@ export default function ChatInput({
|
||||||
? "Thinking..."
|
? "Thinking..."
|
||||||
: typeof chatValue === "object" &&
|
: typeof chatValue === "object" &&
|
||||||
Object.keys(chatValue)?.length === 0
|
Object.keys(chatValue)?.length === 0
|
||||||
? chatInputPlaceholder
|
? CHAT_INPUT_PLACEHOLDER
|
||||||
: chatValue
|
: chatValue
|
||||||
}
|
}
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
|
|
@ -70,7 +70,9 @@ export default function ChatInput({
|
||||||
|
|
||||||
"form-modal-lockchat"
|
"form-modal-lockchat"
|
||||||
)}
|
)}
|
||||||
placeholder={noInput ? chatInputPlaceholder : chatInputPlaceholderSend}
|
placeholder={
|
||||||
|
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="form-modal-send-icon-position">
|
<div className="form-modal-send-icon-position">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ import {
|
||||||
MSG_ERROR_ALERT,
|
MSG_ERROR_ALERT,
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
|
CHAT_FIRST_INITIAL_TEXT,
|
||||||
CHAT_FORM_DIALOG_SUBTITLE,
|
CHAT_FORM_DIALOG_SUBTITLE,
|
||||||
chatFirstInitialText,
|
CHAT_SECOND_INITIAL_TEXT,
|
||||||
chatSecondInitialText,
|
LANGFLOW_CHAT_TITLE,
|
||||||
langflowChatTitle,
|
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { getBuildStatus } from "../../controllers/API";
|
import { getBuildStatus } from "../../controllers/API";
|
||||||
|
|
@ -594,20 +594,20 @@ export default function FormModal({
|
||||||
<span>
|
<span>
|
||||||
👋{" "}
|
👋{" "}
|
||||||
<span className="langflow-chat-span">
|
<span className="langflow-chat-span">
|
||||||
{langflowChatTitle}
|
{LANGFLOW_CHAT_TITLE}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<div className="langflow-chat-desc">
|
<div className="langflow-chat-desc">
|
||||||
<span className="langflow-chat-desc-span">
|
<span className="langflow-chat-desc-span">
|
||||||
{chatFirstInitialText}{" "}
|
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
||||||
<span>
|
<span>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="MessageSquare"
|
name="MessageSquare"
|
||||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||||
/>
|
/>
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
{chatSecondInitialText}
|
{CHAT_SECOND_INITIAL_TEXT}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ import {
|
||||||
TEMP_NOTICE_ALERT,
|
TEMP_NOTICE_ALERT,
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
|
EDIT_TEXT_PLACEHOLDER,
|
||||||
INVALID_CHARACTERS,
|
INVALID_CHARACTERS,
|
||||||
MAX_WORDS_HIGHLIGHT,
|
MAX_WORDS_HIGHLIGHT,
|
||||||
PROMPT_DIALOG_SUBTITLE,
|
PROMPT_DIALOG_SUBTITLE,
|
||||||
TEXT_DIALOG_SUBTITLE,
|
TEXT_DIALOG_SUBTITLE,
|
||||||
editTextPlaceholder,
|
|
||||||
regexHighlight,
|
regexHighlight,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import { TypeModal } from "../../constants/enums";
|
import { TypeModal } from "../../constants/enums";
|
||||||
|
|
@ -227,7 +227,7 @@ export default function GenericModal({
|
||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
checkVariables(event.target.value);
|
checkVariables(event.target.value);
|
||||||
}}
|
}}
|
||||||
placeholder={editTextPlaceholder}
|
placeholder={EDIT_TEXT_PLACEHOLDER}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
handleKeyDown(e, inputValue, "");
|
handleKeyDown(e, inputValue, "");
|
||||||
}}
|
}}
|
||||||
|
|
@ -249,7 +249,7 @@ export default function GenericModal({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
}}
|
}}
|
||||||
placeholder={editTextPlaceholder}
|
placeholder={EDIT_TEXT_PLACEHOLDER}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
handleKeyDown(e, value, "");
|
handleKeyDown(e, value, "");
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -219,23 +219,6 @@ export default function NodeToolbarComponent({
|
||||||
}}
|
}}
|
||||||
data-testid="code-button-modal"
|
data-testid="code-button-modal"
|
||||||
>
|
>
|
||||||
<div className="hidden">
|
|
||||||
<CodeAreaComponent
|
|
||||||
openModal={openModal}
|
|
||||||
readonly={
|
|
||||||
data.node?.flow && data.node.template[name].dynamic
|
|
||||||
? true
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
dynamic={data.node?.template[name].dynamic ?? false}
|
|
||||||
setNodeClass={handleNodeClass}
|
|
||||||
nodeClass={data.node}
|
|
||||||
disabled={false}
|
|
||||||
value={data.node?.template[name].value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id={"code-input-node-toolbar-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<IconComponent name="TerminalSquare" className="h-4 w-4" />
|
<IconComponent name="TerminalSquare" className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
@ -492,6 +475,26 @@ export default function NodeToolbarComponent({
|
||||||
is_component={true}
|
is_component={true}
|
||||||
component={flowComponent!}
|
component={flowComponent!}
|
||||||
/>
|
/>
|
||||||
|
{hasCode && (
|
||||||
|
<div className="hidden">
|
||||||
|
<CodeAreaComponent
|
||||||
|
open={openModal}
|
||||||
|
setOpen={setOpenModal}
|
||||||
|
readonly={
|
||||||
|
data.node?.flow && data.node.template[name].dynamic
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
dynamic={data.node?.template[name].dynamic ?? false}
|
||||||
|
setNodeClass={handleNodeClass}
|
||||||
|
nodeClass={data.node}
|
||||||
|
disabled={false}
|
||||||
|
value={data.node?.template[name].value ?? ""}
|
||||||
|
onChange={handleOnNewValue}
|
||||||
|
id={"code-input-node-toolbar-" + name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function ComponentsComponent({
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(20);
|
||||||
const [pageIndex, setPageIndex] = useState(1);
|
const [pageIndex, setPageIndex] = useState(1);
|
||||||
const [loadingScreen, setLoadingScreen] = useState(true);
|
const [loadingScreen, setLoadingScreen] = useState(true);
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ export default function ComponentsComponent({
|
||||||
|
|
||||||
function resetFilter() {
|
function resetFilter() {
|
||||||
setPageIndex(1);
|
setPageIndex(1);
|
||||||
setPageSize(10);
|
setPageSize(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import SidebarNav from "../../components/sidebarComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
|
MY_COLLECTION_DESC,
|
||||||
USER_PROJECTS_HEADER,
|
USER_PROJECTS_HEADER,
|
||||||
myCollectionDesc,
|
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
|
|
@ -75,7 +75,7 @@ export default function HomePage(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
title={USER_PROJECTS_HEADER}
|
title={USER_PROJECTS_HEADER}
|
||||||
description={myCollectionDesc}
|
description={MY_COLLECTION_DESC}
|
||||||
button={
|
button={
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import {
|
||||||
INVALID_API_ERROR_ALERT,
|
INVALID_API_ERROR_ALERT,
|
||||||
NOAPI_ERROR_ALERT,
|
NOAPI_ERROR_ALERT,
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import { storeDesc, storeTitle } from "../../constants/constants";
|
import { STORE_DESC, STORE_TITLE } from "../../constants/constants";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
||||||
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
||||||
|
|
@ -174,8 +174,8 @@ export default function StorePage(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
betaIcon
|
betaIcon
|
||||||
title={storeTitle}
|
title={STORE_TITLE}
|
||||||
description={storeDesc}
|
description={STORE_DESC}
|
||||||
button={
|
button={
|
||||||
<>
|
<>
|
||||||
{StoreApiKeyModal && (
|
{StoreApiKeyModal && (
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
FLOW_BUILD_SUCCESS_ALERT,
|
FLOW_BUILD_SUCCESS_ALERT,
|
||||||
MISSED_ERROR_ALERT,
|
MISSED_ERROR_ALERT,
|
||||||
} from "../constants/alerts_constants";
|
} from "../constants/alerts_constants";
|
||||||
|
import { RUN_TIMESTAMP_PREFIX } from "../constants/constants";
|
||||||
import { BuildStatus } from "../constants/enums";
|
import { BuildStatus } from "../constants/enums";
|
||||||
import { getFlowPool } from "../controllers/API";
|
import { getFlowPool } from "../controllers/API";
|
||||||
import { VertexBuildTypeAPI } from "../types/api";
|
import { VertexBuildTypeAPI } from "../types/api";
|
||||||
|
|
@ -416,10 +417,12 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
buildFlow: async ({
|
buildFlow: async ({
|
||||||
nodeId,
|
startNodeId,
|
||||||
|
stopNodeId,
|
||||||
input_value,
|
input_value,
|
||||||
}: {
|
}: {
|
||||||
nodeId?: string;
|
startNodeId?: string;
|
||||||
|
stopNodeId?: string;
|
||||||
input_value?: string;
|
input_value?: string;
|
||||||
}) => {
|
}) => {
|
||||||
get().setIsBuilding(true);
|
get().setIsBuilding(true);
|
||||||
|
|
@ -444,7 +447,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
function handleBuildUpdate(
|
function handleBuildUpdate(
|
||||||
vertexBuildData: VertexBuildTypeAPI,
|
vertexBuildData: VertexBuildTypeAPI,
|
||||||
status: BuildStatus,
|
status: BuildStatus,
|
||||||
buildId: string
|
runId: string
|
||||||
) {
|
) {
|
||||||
if (vertexBuildData && vertexBuildData.inactivated_vertices) {
|
if (vertexBuildData && vertexBuildData.inactivated_vertices) {
|
||||||
get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices);
|
get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices);
|
||||||
|
|
@ -452,20 +455,47 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
if (vertexBuildData && vertexBuildData.activated_layers) {
|
if (vertexBuildData && vertexBuildData.activated_layers) {
|
||||||
get().addToVerticesBuild(vertexBuildData.activated_layers.flat());
|
get().addToVerticesBuild(vertexBuildData.activated_layers.flat());
|
||||||
}
|
}
|
||||||
|
if (vertexBuildData.next_vertices_ids) {
|
||||||
|
// next_vertices_ids is a list of vertices that are going to be built next
|
||||||
|
// verticesLayers is a list of list of vertices ids, where each list is a layer of vertices
|
||||||
|
// we want to add a new layer (next_vertices_ids) to the list of layers (verticesLayers)
|
||||||
|
// and the values of next_vertices_ids to the list of vertices ids (verticesIds)
|
||||||
|
const newLayers = [
|
||||||
|
...get().verticesBuild!.verticesLayers,
|
||||||
|
vertexBuildData.next_vertices_ids,
|
||||||
|
];
|
||||||
|
const newIds = [
|
||||||
|
...get().verticesBuild!.verticesIds,
|
||||||
|
...vertexBuildData.next_vertices_ids,
|
||||||
|
];
|
||||||
|
get().updateVerticesBuild({
|
||||||
|
verticesIds: newIds,
|
||||||
|
verticesLayers: newLayers,
|
||||||
|
runId: runId,
|
||||||
|
});
|
||||||
|
get().updateBuildStatus(
|
||||||
|
vertexBuildData.next_vertices_ids,
|
||||||
|
BuildStatus.TO_BUILD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get().addDataToFlowPool(
|
get().addDataToFlowPool(
|
||||||
{ ...vertexBuildData, buildId },
|
{ ...vertexBuildData, buildId: runId },
|
||||||
vertexBuildData.id
|
vertexBuildData.id
|
||||||
);
|
);
|
||||||
|
|
||||||
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
|
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
|
||||||
}
|
}
|
||||||
await buildVertices({
|
await buildVertices({
|
||||||
input_value,
|
input_value,
|
||||||
flowId: currentFlow!.id,
|
flowId: currentFlow!.id,
|
||||||
nodeId,
|
startNodeId,
|
||||||
|
stopNodeId,
|
||||||
onGetOrderSuccess: () => {
|
onGetOrderSuccess: () => {
|
||||||
setNoticeData({ title: "Running components" });
|
setNoticeData({ title: "Running components" });
|
||||||
},
|
},
|
||||||
onBuildComplete: () => {
|
onBuildComplete: () => {
|
||||||
|
const nodeId = startNodeId || stopNodeId;
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
setSuccessData({
|
setSuccessData({
|
||||||
title: `${
|
title: `${
|
||||||
|
|
@ -482,6 +512,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
onBuildError: (title, list, idList) => {
|
onBuildError: (title, list, idList) => {
|
||||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILT);
|
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILT);
|
||||||
setErrorData({ list, title });
|
setErrorData({ list, title });
|
||||||
|
get().setIsBuilding(false);
|
||||||
},
|
},
|
||||||
onBuildStart: (idList) => {
|
onBuildStart: (idList) => {
|
||||||
console.log("onBuildStart", idList);
|
console.log("onBuildStart", idList);
|
||||||
|
|
@ -489,6 +520,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
},
|
},
|
||||||
validateNodes: validateSubgraph,
|
validateNodes: validateSubgraph,
|
||||||
});
|
});
|
||||||
|
get().setIsBuilding(false);
|
||||||
get().revertBuiltStatusFromBuilding();
|
get().revertBuiltStatusFromBuilding();
|
||||||
},
|
},
|
||||||
getFlow: () => {
|
getFlow: () => {
|
||||||
|
|
@ -505,6 +537,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
runId: string;
|
runId: string;
|
||||||
} | null
|
} | null
|
||||||
) => {
|
) => {
|
||||||
|
console.log("updateVerticesBuild", vertices);
|
||||||
set({ verticesBuild: vertices });
|
set({ verticesBuild: vertices });
|
||||||
},
|
},
|
||||||
verticesBuild: null,
|
verticesBuild: null,
|
||||||
|
|
@ -531,17 +564,27 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {
|
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {
|
||||||
|
console.log("updateBuildStatus", nodeIdList, status);
|
||||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||||
nodeIdList.forEach((id) => {
|
nodeIdList.forEach((id) => {
|
||||||
newFlowBuildStatus[id] = status;
|
newFlowBuildStatus[id] = {
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
if (status == BuildStatus.BUILT) {
|
||||||
|
const timestamp_string = new Date(Date.now()).toLocaleString();
|
||||||
|
newFlowBuildStatus[
|
||||||
|
id
|
||||||
|
].timestamp = `${RUN_TIMESTAMP_PREFIX} ${timestamp_string}`;
|
||||||
|
}
|
||||||
|
console.log("updateBuildStatus", newFlowBuildStatus);
|
||||||
});
|
});
|
||||||
set({ flowBuildStatus: newFlowBuildStatus });
|
set({ flowBuildStatus: newFlowBuildStatus });
|
||||||
},
|
},
|
||||||
revertBuiltStatusFromBuilding: () => {
|
revertBuiltStatusFromBuilding: () => {
|
||||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||||
Object.keys(newFlowBuildStatus).forEach((id) => {
|
Object.keys(newFlowBuildStatus).forEach((id) => {
|
||||||
if (newFlowBuildStatus[id] === BuildStatus.BUILDING) {
|
if (newFlowBuildStatus[id].status === BuildStatus.BUILDING) {
|
||||||
newFlowBuildStatus[id] = BuildStatus.BUILT;
|
newFlowBuildStatus[id].status = BuildStatus.BUILT;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -845,13 +845,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-modal-tabs {
|
.api-modal-tabs {
|
||||||
@apply flex h-full max-w-full flex-col overflow-hidden rounded-md border bg-muted text-center sm:w-[75vw] md:w-[75vw] lg:w-[75vw] xl:w-[76vw] 2xl:w-full;
|
@apply flex h-full flex-col overflow-hidden rounded-md border bg-muted text-center;
|
||||||
}
|
}
|
||||||
.api-modal-tablist-div {
|
.api-modal-tablist-div {
|
||||||
@apply flex items-center justify-between px-2 py-2;
|
@apply flex items-center justify-between px-2 py-2;
|
||||||
}
|
}
|
||||||
.api-modal-tabs-content {
|
.api-modal-tabs-content {
|
||||||
@apply -mt-1 h-full w-full px-4 pb-4;
|
@apply -mt-1 h-full w-full px-4 pb-4;
|
||||||
}
|
}
|
||||||
.api-modal-accordion-display {
|
.api-modal-accordion-display {
|
||||||
@apply mt-2 flex h-full w-full;
|
@apply mt-2 flex h-full w-full;
|
||||||
|
|
@ -902,7 +902,7 @@
|
||||||
@apply flex-max-width px-2 py-6 pl-4 pr-9;
|
@apply flex-max-width px-2 py-6 pl-4 pr-9;
|
||||||
}
|
}
|
||||||
.form-modal-chatbot-icon {
|
.form-modal-chatbot-icon {
|
||||||
@apply mb-3 ml-3 mr-6 mt-1;
|
@apply flex flex-col mb-3 ml-3 mr-6 mt-1;
|
||||||
}
|
}
|
||||||
.form-modal-chat-image {
|
.form-modal-chat-image {
|
||||||
@apply flex flex-col items-center gap-1;
|
@apply flex flex-col items-center gap-1;
|
||||||
|
|
|
||||||
|
|
@ -134,14 +134,15 @@ export type Component = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerticesOrderTypeAPI = {
|
export type VerticesOrderTypeAPI = {
|
||||||
ids: Array<Array<string>>;
|
ids: Array<string>;
|
||||||
run_id: string;
|
run_id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VertexBuildTypeAPI = {
|
export type VertexBuildTypeAPI = {
|
||||||
id: string;
|
id: string;
|
||||||
inactivated_vertices: Array<string> | null;
|
inactivated_vertices: Array<string> | null;
|
||||||
activated_layers: Array<Array<string>> | null;
|
next_vertices_ids: Array<string>;
|
||||||
|
run_id: string;
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
params: string;
|
params: string;
|
||||||
data: VertexDataTypeAPI;
|
data: VertexDataTypeAPI;
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,8 @@ export type CodeAreaComponentType = {
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
openModal?: boolean;
|
open?: boolean;
|
||||||
|
setOpen?: (open: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FileComponentType = {
|
export type FileComponentType = {
|
||||||
|
|
@ -519,7 +520,8 @@ export type codeAreaModalPropsType = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
openModal?: boolean;
|
open?: boolean;
|
||||||
|
setOpen?: (open: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type chatMessagePropsType = {
|
export type chatMessagePropsType = {
|
||||||
|
|
|
||||||
|
|
@ -90,10 +90,13 @@ export type FlowStoreType = {
|
||||||
onConnect: (connection: Connection) => void;
|
onConnect: (connection: Connection) => void;
|
||||||
unselectAll: () => void;
|
unselectAll: () => void;
|
||||||
buildFlow: ({
|
buildFlow: ({
|
||||||
nodeId,
|
startNodeId,
|
||||||
|
stopNodeId,
|
||||||
input_value,
|
input_value,
|
||||||
}: {
|
}: {
|
||||||
nodeId?: string;
|
nodeId?: string;
|
||||||
|
startNodeId?: string;
|
||||||
|
stopNodeId?: string;
|
||||||
input_value?: string;
|
input_value?: string;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
||||||
|
|
@ -113,7 +116,9 @@ export type FlowStoreType = {
|
||||||
} | null;
|
} | null;
|
||||||
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
|
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
|
||||||
revertBuiltStatusFromBuilding: () => void;
|
revertBuiltStatusFromBuilding: () => void;
|
||||||
flowBuildStatus: { [key: string]: BuildStatus };
|
flowBuildStatus: {
|
||||||
|
[key: string]: { status: BuildStatus; timestamp?: string };
|
||||||
|
};
|
||||||
updateFlowPool: (
|
updateFlowPool: (
|
||||||
nodeId: string,
|
nodeId: string,
|
||||||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import { VertexBuildTypeAPI } from "../types/api";
|
||||||
type BuildVerticesParams = {
|
type BuildVerticesParams = {
|
||||||
flowId: string; // Assuming FlowType is the type for your flow
|
flowId: string; // Assuming FlowType is the type for your flow
|
||||||
input_value?: any; // Replace any with the actual type if it's not any
|
input_value?: any; // Replace any with the actual type if it's not any
|
||||||
nodeId?: string | null; // Assuming nodeId is of type string, and it's optional
|
startNodeId?: string | null; // Assuming nodeId is of type string, and it's optional
|
||||||
|
stopNodeId?: string | null; // Assuming nodeId is of type string, and it's optional
|
||||||
onGetOrderSuccess?: () => void;
|
onGetOrderSuccess?: () => void;
|
||||||
onBuildUpdate?: (
|
onBuildUpdate?: (
|
||||||
data: VertexBuildTypeAPI,
|
data: VertexBuildTypeAPI,
|
||||||
|
|
@ -32,7 +33,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
||||||
data: inactiveData,
|
data: inactiveData,
|
||||||
params: "Inactive",
|
params: "Inactive",
|
||||||
inactivated_vertices: null,
|
inactivated_vertices: null,
|
||||||
activated_layers: null,
|
run_id: "",
|
||||||
|
next_vertices_ids: [],
|
||||||
|
inactive_vertices: null,
|
||||||
valid: false,
|
valid: false,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
@ -42,7 +45,8 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
||||||
|
|
||||||
export async function updateVerticesOrder(
|
export async function updateVerticesOrder(
|
||||||
flowId: string,
|
flowId: string,
|
||||||
nodeId: string | null
|
startNodeId?: string | null,
|
||||||
|
stopNodeId?: string | null
|
||||||
): Promise<{
|
): Promise<{
|
||||||
verticesLayers: string[][];
|
verticesLayers: string[][];
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
|
|
@ -52,7 +56,7 @@ export async function updateVerticesOrder(
|
||||||
const setErrorData = useAlertStore.getState().setErrorData;
|
const setErrorData = useAlertStore.getState().setErrorData;
|
||||||
let orderResponse;
|
let orderResponse;
|
||||||
try {
|
try {
|
||||||
orderResponse = await getVerticesOrder(flowId, nodeId);
|
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
setErrorData({
|
setErrorData({
|
||||||
|
|
@ -62,27 +66,26 @@ export async function updateVerticesOrder(
|
||||||
useFlowStore.getState().setIsBuilding(false);
|
useFlowStore.getState().setIsBuilding(false);
|
||||||
throw new Error("Invalid nodes");
|
throw new Error("Invalid nodes");
|
||||||
}
|
}
|
||||||
let verticesOrder: Array<Array<string>> = orderResponse.data.ids;
|
let verticesLayers: Array<Array<string>> = [orderResponse.data.ids];
|
||||||
let verticesLayers: Array<Array<string>> = [];
|
|
||||||
const runId = orderResponse.data.run_id;
|
const runId = orderResponse.data.run_id;
|
||||||
if (nodeId) {
|
// if (nodeId) {
|
||||||
for (let i = 0; i < verticesOrder.length; i += 1) {
|
// for (let i = 0; i < verticesOrder.length; i += 1) {
|
||||||
const innerArray = verticesOrder[i];
|
// const innerArray = verticesOrder[i];
|
||||||
const idIndex = innerArray.indexOf(nodeId);
|
// const idIndex = innerArray.indexOf(nodeId);
|
||||||
if (idIndex !== -1) {
|
// if (idIndex !== -1) {
|
||||||
// If there's a nodeId, we want to run just that component and not the entire layer
|
// // If there's a nodeId, we want to run just that component and not the entire layer
|
||||||
// because a layer contains dependencies for the next layer
|
// // because a layer contains dependencies for the next layer
|
||||||
// and we are stopping at the layer that contains the nodeId
|
// // and we are stopping at the layer that contains the nodeId
|
||||||
verticesLayers.push([innerArray[idIndex]]);
|
// verticesLayers.push([innerArray[idIndex]]);
|
||||||
break; // Stop searching after finding the first occurrence
|
// break; // Stop searching after finding the first occurrence
|
||||||
}
|
// }
|
||||||
// If the targetId is not found, include the entire inner array
|
// // If the targetId is not found, include the entire inner array
|
||||||
verticesLayers.push(innerArray);
|
// verticesLayers.push(innerArray);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
verticesLayers = verticesOrder;
|
// verticesLayers = verticesOrder;
|
||||||
}
|
// }
|
||||||
const verticesIds = verticesOrder.flat();
|
const verticesIds = orderResponse.data.ids;
|
||||||
useFlowStore.getState().updateVerticesBuild({
|
useFlowStore.getState().updateVerticesBuild({
|
||||||
verticesLayers,
|
verticesLayers,
|
||||||
verticesIds,
|
verticesIds,
|
||||||
|
|
@ -95,7 +98,8 @@ export async function updateVerticesOrder(
|
||||||
export async function buildVertices({
|
export async function buildVertices({
|
||||||
flowId,
|
flowId,
|
||||||
input_value,
|
input_value,
|
||||||
nodeId = null,
|
startNodeId,
|
||||||
|
stopNodeId,
|
||||||
onGetOrderSuccess,
|
onGetOrderSuccess,
|
||||||
onBuildUpdate,
|
onBuildUpdate,
|
||||||
onBuildComplete,
|
onBuildComplete,
|
||||||
|
|
@ -104,9 +108,13 @@ export async function buildVertices({
|
||||||
validateNodes,
|
validateNodes,
|
||||||
}: BuildVerticesParams) {
|
}: BuildVerticesParams) {
|
||||||
let verticesBuild = useFlowStore.getState().verticesBuild;
|
let verticesBuild = useFlowStore.getState().verticesBuild;
|
||||||
|
// if startNodeId and stopNodeId are provided
|
||||||
if (!verticesBuild || nodeId) {
|
// something is wrong
|
||||||
verticesBuild = await updateVerticesOrder(flowId, nodeId);
|
if (startNodeId && stopNodeId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!verticesBuild || startNodeId || stopNodeId) {
|
||||||
|
verticesBuild = await updateVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const verticesIds = verticesBuild?.verticesIds!;
|
const verticesIds = verticesBuild?.verticesIds!;
|
||||||
|
|
@ -126,98 +134,83 @@ export async function buildVertices({
|
||||||
|
|
||||||
useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD);
|
useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD);
|
||||||
useFlowStore.getState().setIsBuilding(true);
|
useFlowStore.getState().setIsBuilding(true);
|
||||||
let dynamicVerticesLayers: Array<Array<string>> = [...verticesLayers];
|
let currentLayerIndex = 0; // Start with the first layer
|
||||||
|
|
||||||
const handleBuildUpdate = (data: VertexBuildTypeAPI, status: BuildStatus) => {
|
|
||||||
// Handle activated vertices
|
|
||||||
console.log("handleBuildUpdate", data, status);
|
|
||||||
if (data.activated_layers && data.activated_layers.length > 0) {
|
|
||||||
const thisVertexLayer = dynamicVerticesLayers.findIndex((layer) =>
|
|
||||||
layer.includes(data.id)
|
|
||||||
);
|
|
||||||
let nextLayerIndex = thisVertexLayer + 1;
|
|
||||||
|
|
||||||
console.log("nextLayerIndex", nextLayerIndex);
|
|
||||||
console.log("dynamicVerticesLayers", dynamicVerticesLayers);
|
|
||||||
// This adds layers to the dynamicVerticesLayers array
|
|
||||||
// starting from the index of the current layer + 1
|
|
||||||
data.activated_layers.forEach((newLayer) => {
|
|
||||||
if (!dynamicVerticesLayers[nextLayerIndex]) {
|
|
||||||
dynamicVerticesLayers[nextLayerIndex] = [];
|
|
||||||
}
|
|
||||||
dynamicVerticesLayers[nextLayerIndex] = [
|
|
||||||
...dynamicVerticesLayers[nextLayerIndex],
|
|
||||||
...newLayer,
|
|
||||||
];
|
|
||||||
nextLayerIndex += 1;
|
|
||||||
});
|
|
||||||
// Let's implement one that just adds all layers to the end of the array
|
|
||||||
// data.activated_layers.forEach((newLayer) => {
|
|
||||||
// // filter the newLayer to remove any vertices that are already in the dynamicVerticesLayers
|
|
||||||
// // after thisVertexLayer
|
|
||||||
// newLayer = newLayer.filter((vertex) => {
|
|
||||||
// return !dynamicVerticesLayers
|
|
||||||
// .slice(thisVertexLayer)
|
|
||||||
// .flat()
|
|
||||||
// .includes(vertex);
|
|
||||||
// });
|
|
||||||
// if (newLayer.length > 0) {
|
|
||||||
// console.log("newLayer after filter", newLayer);
|
|
||||||
// dynamicVerticesLayers.push(newLayer);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
if (onBuildUpdate) onBuildUpdate(data, status, runId);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set each vertex state to building
|
// Set each vertex state to building
|
||||||
const buildResults: Array<boolean> = [];
|
const buildResults: Array<boolean> = [];
|
||||||
for (let i = 0; i < dynamicVerticesLayers.length; i++) {
|
|
||||||
const layer = dynamicVerticesLayers[i];
|
|
||||||
if (onBuildStart) onBuildStart(layer);
|
|
||||||
|
|
||||||
for (const id of layer) {
|
// Build each layer
|
||||||
// Check if id is in the list of inactive nodes
|
while (
|
||||||
// useFlowStore because it gets updated constantly
|
currentLayerIndex <
|
||||||
if (
|
(useFlowStore.getState().verticesBuild?.verticesLayers! || []).length
|
||||||
!useFlowStore.getState().verticesBuild?.verticesIds.includes(id) &&
|
) {
|
||||||
onBuildUpdate
|
// Get the current layer
|
||||||
) {
|
const currentLayer =
|
||||||
// If it is, skip building and set the state to inactive
|
useFlowStore.getState().verticesBuild?.verticesLayers![currentLayerIndex];
|
||||||
console.log("inactive", id);
|
// If there are no more layers, we are done
|
||||||
onBuildUpdate(getInactiveVertexData(id), BuildStatus.INACTIVE, runId);
|
if (!currentLayer) {
|
||||||
buildResults.push(false);
|
if (onBuildComplete) {
|
||||||
continue;
|
const allNodesValid = buildResults.every((result) => result);
|
||||||
}
|
onBuildComplete(allNodesValid);
|
||||||
|
useFlowStore.getState().setIsBuilding(false);
|
||||||
await buildVertex({
|
|
||||||
flowId,
|
|
||||||
id,
|
|
||||||
input_value,
|
|
||||||
onBuildUpdate: handleBuildUpdate,
|
|
||||||
onBuildError,
|
|
||||||
verticesIds,
|
|
||||||
buildResults,
|
|
||||||
stopBuild: () => {
|
|
||||||
stop = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (stop) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// If there is a callback for the start of the build, call it
|
||||||
|
if (onBuildStart) onBuildStart(currentLayer);
|
||||||
|
// Build each vertex in the current layer
|
||||||
|
await Promise.all(
|
||||||
|
currentLayer.map(async (vertexId) => {
|
||||||
|
// Check if id is in the list of inactive nodes
|
||||||
|
if (
|
||||||
|
!useFlowStore
|
||||||
|
.getState()
|
||||||
|
.verticesBuild?.verticesIds.includes(vertexId) &&
|
||||||
|
onBuildUpdate
|
||||||
|
) {
|
||||||
|
// If it is, skip building and set the state to inactive
|
||||||
|
onBuildUpdate(
|
||||||
|
getInactiveVertexData(vertexId),
|
||||||
|
BuildStatus.INACTIVE,
|
||||||
|
runId
|
||||||
|
);
|
||||||
|
buildResults.push(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the vertex
|
||||||
|
await buildVertex({
|
||||||
|
flowId,
|
||||||
|
id: vertexId,
|
||||||
|
input_value,
|
||||||
|
onBuildUpdate: (data: VertexBuildTypeAPI, status: BuildStatus) => {
|
||||||
|
if (onBuildUpdate) onBuildUpdate(data, status, runId);
|
||||||
|
},
|
||||||
|
onBuildError,
|
||||||
|
verticesIds,
|
||||||
|
buildResults,
|
||||||
|
stopBuild: () => {
|
||||||
|
stop = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (stop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// Once the current layer is built, move to the next layer
|
||||||
|
currentLayerIndex += 1;
|
||||||
|
|
||||||
if (stop) {
|
if (stop) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (onBuildComplete) {
|
if (onBuildComplete) {
|
||||||
const allNodesValid = buildResults.every((result) => result);
|
const allNodesValid = buildResults.every((result) => result);
|
||||||
onBuildComplete(allNodesValid);
|
onBuildComplete(allNodesValid);
|
||||||
useFlowStore.getState().setIsBuilding(false);
|
useFlowStore.getState().setIsBuilding(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildVertex({
|
async function buildVertex({
|
||||||
flowId,
|
flowId,
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import {
|
||||||
INPUT_TYPES,
|
INPUT_TYPES,
|
||||||
LANGFLOW_SUPPORTED_TYPES,
|
LANGFLOW_SUPPORTED_TYPES,
|
||||||
OUTPUT_TYPES,
|
OUTPUT_TYPES,
|
||||||
|
SUCCESS_BUILD,
|
||||||
specialCharsRegex,
|
specialCharsRegex,
|
||||||
successBuild,
|
|
||||||
} from "../constants/constants";
|
} from "../constants/constants";
|
||||||
import { downloadFlowsFromDatabase } from "../controllers/API";
|
import { downloadFlowsFromDatabase } from "../controllers/API";
|
||||||
import {
|
import {
|
||||||
|
|
@ -1092,7 +1092,7 @@ export function getGroupStatus(
|
||||||
flow: FlowType,
|
flow: FlowType,
|
||||||
ssData: { [key: string]: { valid: boolean; params: string } }
|
ssData: { [key: string]: { valid: boolean; params: string } }
|
||||||
) {
|
) {
|
||||||
let status = { valid: true, params: successBuild };
|
let status = { valid: true, params: SUCCESS_BUILD };
|
||||||
const { nodes } = flow.data!;
|
const { nodes } = flow.data!;
|
||||||
const ids = nodes.map((n: NodeType) => n.data.id);
|
const ids = nodes.map((n: NodeType) => n.data.id);
|
||||||
ids.forEach((id) => {
|
ids.forEach((id) => {
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ import {
|
||||||
Network,
|
Network,
|
||||||
Paperclip,
|
Paperclip,
|
||||||
Pencil,
|
Pencil,
|
||||||
|
PencilLine,
|
||||||
Pin,
|
Pin,
|
||||||
Play,
|
Play,
|
||||||
Plus,
|
Plus,
|
||||||
|
|
@ -418,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
Group,
|
Group,
|
||||||
LogIn,
|
LogIn,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
|
PencilLine,
|
||||||
Ungroup,
|
Ungroup,
|
||||||
BookMarked,
|
BookMarked,
|
||||||
Minus,
|
Minus,
|
||||||
|
|
|
||||||
|
|
@ -640,7 +640,5 @@ export function getFieldTitle(
|
||||||
): string {
|
): string {
|
||||||
return template[templateField].display_name
|
return template[templateField].display_name
|
||||||
? template[templateField].display_name!
|
? template[templateField].display_name!
|
||||||
: template[templateField].name
|
: template[templateField].name ?? templateField;
|
||||||
? toTitleCase(template[templateField].name!, true)
|
|
||||||
: toTitleCase(templateField, true);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue