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.deps import get_chat_service, get_session, get_session_service
|
||||
from langflow.services.monitor.utils import log_vertex_build
|
||||
from langflow.services.session.service import SessionService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.types import ChatVertex
|
||||
from langflow.services.session.service import SessionService
|
||||
|
||||
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)
|
||||
async def get_vertices(
|
||||
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),
|
||||
session=Depends(get_session),
|
||||
):
|
||||
|
|
@ -60,9 +61,9 @@ async def get_vertices(
|
|||
if cache := chat_service.get_cache(flow_id):
|
||||
graph = cache.get("result")
|
||||
graph = build_and_cache_graph(flow_id, session, chat_service, graph)
|
||||
if component_id:
|
||||
if stop_component_id or start_component_id:
|
||||
try:
|
||||
vertices = graph.sort_vertices(component_id)
|
||||
vertices = graph.sort_vertices(stop_component_id, start_component_id)
|
||||
except Exception as exc:
|
||||
logger.error(exc)
|
||||
vertices = graph.sort_vertices()
|
||||
|
|
@ -94,6 +95,7 @@ async def build_vertex(
|
|||
"""Build a vertex instead of the entire graph."""
|
||||
{"inputs": {"input_value": "some value"}}
|
||||
start_time = time.perf_counter()
|
||||
next_vertices_ids = []
|
||||
try:
|
||||
start_time = time.perf_counter()
|
||||
cache = chat_service.get_cache(flow_id)
|
||||
|
|
@ -123,6 +125,10 @@ async def build_vertex(
|
|||
artifacts = vertex.artifacts
|
||||
else:
|
||||
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())
|
||||
|
||||
|
|
@ -160,9 +166,16 @@ async def build_vertex(
|
|||
graph.reset_activated_vertices()
|
||||
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(
|
||||
inactivated_vertices=inactivated_vertices,
|
||||
activated_layers=activated_layers,
|
||||
next_vertices_ids=next_vertices_ids,
|
||||
valid=valid,
|
||||
params=params,
|
||||
id=vertex.id,
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ class ApiKeyCreateRequest(BaseModel):
|
|||
|
||||
|
||||
class VerticesOrderResponse(BaseModel):
|
||||
ids: List[List[str]]
|
||||
ids: List[str]
|
||||
run_id: UUID
|
||||
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ class ResultDataResponse(BaseModel):
|
|||
class VertexBuildResponse(BaseModel):
|
||||
id: Optional[str] = None
|
||||
inactivated_vertices: Optional[List[str]] = None
|
||||
activated_layers: Optional[List[List[str]]] = None
|
||||
next_vertices_ids: Optional[List[str]] = None
|
||||
valid: bool
|
||||
params: Optional[str]
|
||||
"""JSON string of the params."""
|
||||
|
|
|
|||
|
|
@ -31,16 +31,12 @@ class ConversationChainComponent(CustomComponent):
|
|||
chain = ConversationChain(llm=llm)
|
||||
else:
|
||||
chain = ConversationChain(llm=llm, memory=memory)
|
||||
result = chain.invoke({chain.input_key: input_value})
|
||||
# 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 = ""
|
||||
result = chain.invoke(inputs)
|
||||
if hasattr(result, "content") and isinstance(result.content, str):
|
||||
result_str = result.content
|
||||
result = result.content
|
||||
elif isinstance(result, str):
|
||||
result_str = result
|
||||
result = result
|
||||
else:
|
||||
# is dict
|
||||
result_str = Text(result.get("response"))
|
||||
self.status = result_str
|
||||
return result_str
|
||||
result = result.get("response")
|
||||
self.status = result
|
||||
return result
|
||||
|
|
|
|||
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
|
||||
from collections import defaultdict, deque
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Type, Union
|
||||
|
||||
from langchain.chains.base import Chain
|
||||
|
|
@ -59,6 +60,11 @@ class Graph:
|
|||
self._edges = self._graph_data["edges"]
|
||||
self.inactivated_vertices: set = set()
|
||||
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.vertices: List[Vertex] = []
|
||||
self._build_graph()
|
||||
|
|
@ -191,6 +197,14 @@ class Graph:
|
|||
outputs.extend(run_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
|
||||
def metadata(self):
|
||||
return {
|
||||
|
|
@ -336,9 +350,14 @@ class Graph:
|
|||
# Add new vertices
|
||||
for vertex_id in new_vertex_ids:
|
||||
new_vertex = other.get_vertex(vertex_id)
|
||||
new_vertex.graph = self
|
||||
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
|
||||
for vertex_id in existing_vertex_ids.intersection(other_vertex_ids):
|
||||
self_vertex = self.get_vertex(vertex_id)
|
||||
|
|
@ -385,12 +404,24 @@ class Graph:
|
|||
_vertex._build_params()
|
||||
|
||||
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.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
|
||||
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)
|
||||
|
||||
def _build_graph(self) -> None:
|
||||
|
|
@ -722,7 +753,7 @@ class Graph:
|
|||
)
|
||||
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."""
|
||||
# Initial setup
|
||||
visited = set() # To keep track of visited vertices
|
||||
|
|
@ -756,11 +787,19 @@ class Graph:
|
|||
if current_id == vertex_id:
|
||||
# We should add to visited all the vertices that are successors of the current vertex
|
||||
# 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:
|
||||
excluded.add(successor.id)
|
||||
if is_start:
|
||||
stack.append(successor.id)
|
||||
else:
|
||||
excluded.add(successor.id)
|
||||
all_successors = get_successors(successor)
|
||||
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`
|
||||
vertices_to_keep = [self.get_vertex(vid) for vid in visited]
|
||||
|
|
@ -871,12 +910,19 @@ class Graph:
|
|||
|
||||
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."""
|
||||
self.mark_all_vertices("ACTIVE")
|
||||
if component_id:
|
||||
vertices = self.sort_up_to_vertex(component_id)
|
||||
vertices_layers = self.layered_topological_sort(vertices)
|
||||
if stop_component_id:
|
||||
self.stop_vertex = stop_component_id
|
||||
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:
|
||||
vertices = self.vertices
|
||||
# without component_id we are probably running in the chat
|
||||
|
|
@ -886,10 +932,23 @@ class Graph:
|
|||
vertices, filter_graphs=True
|
||||
)
|
||||
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._sorted_vertices_layers = vertices_layers
|
||||
return vertices_layers
|
||||
first_layer = vertices_layers[0]
|
||||
# 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(
|
||||
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.TextSplitterVertex for t in textsplitter_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.ChatVertex for t in CHAT_COMPONENTS},
|
||||
**{t: types.RoutingVertex for t in ROUTING_COMPONENTS},
|
||||
|
|
|
|||
|
|
@ -2,13 +2,26 @@ import ast
|
|||
import inspect
|
||||
import types
|
||||
from enum import Enum
|
||||
from typing import (TYPE_CHECKING, Any, AsyncIterator, Callable, Coroutine,
|
||||
Dict, Iterator, List, Optional)
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
)
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from langflow.graph.schema import (INPUT_COMPONENTS, OUTPUT_COMPONENTS,
|
||||
InterfaceComponentTypes, ResultData)
|
||||
from langflow.graph.schema import (
|
||||
INPUT_COMPONENTS,
|
||||
OUTPUT_COMPONENTS,
|
||||
InterfaceComponentTypes,
|
||||
ResultData,
|
||||
)
|
||||
from langflow.graph.utils import UnbuiltObject, UnbuiltResult
|
||||
from langflow.graph.vertex.utils import generate_result
|
||||
from langflow.interface.initialize import loading
|
||||
|
|
@ -152,6 +165,10 @@ class Vertex:
|
|||
def successors(self) -> List["Vertex"]:
|
||||
return self.graph.get_successors(self)
|
||||
|
||||
@property
|
||||
def successors_ids(self) -> List[str]:
|
||||
return self.graph.successor_map.get(self.id, [])
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
"_data": self._data,
|
||||
|
|
@ -594,9 +611,6 @@ class Vertex:
|
|||
raise ValueError(
|
||||
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):
|
||||
self._built = False
|
||||
|
|
@ -606,6 +620,9 @@ class Vertex:
|
|||
self.steps_ran = []
|
||||
self._build_params()
|
||||
|
||||
def _is_chat_input(self):
|
||||
return False
|
||||
|
||||
def build_inactive(self):
|
||||
# Just set the results to None
|
||||
self._built = True
|
||||
|
|
@ -632,7 +649,7 @@ class Vertex:
|
|||
return await self.get_requester_result(requester)
|
||||
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)
|
||||
|
||||
# Run steps
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import yaml
|
|||
from langchain_core.messages import AIMessage
|
||||
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.vertex.base import Vertex
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
|
|
@ -455,18 +455,30 @@ class ChatVertex(Vertex):
|
|||
async for _ in self.stream():
|
||||
pass
|
||||
|
||||
def _is_chat_input(self):
|
||||
return self.vertex_type == InterfaceComponentTypes.ChatInput and self.is_input
|
||||
|
||||
|
||||
class RoutingVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="custom_components")
|
||||
self.use_result = True
|
||||
self.steps = [self._build, self._run]
|
||||
self.steps = [self._build]
|
||||
|
||||
def _built_object_repr(self):
|
||||
if self.artifacts and "repr" in self.artifacts:
|
||||
return self.artifacts["repr"] or 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):
|
||||
if self._built_object:
|
||||
condition = self._built_object.get("condition")
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ import TextAreaComponent from "../../../../components/textAreaComponent";
|
|||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import {
|
||||
INPUT_HANDLER_HOVER,
|
||||
LANGFLOW_SUPPORTED_TYPES,
|
||||
OUTPUT_HANDLER_HOVER,
|
||||
TOOLTIP_EMPTY,
|
||||
inputHandleHover,
|
||||
outputHandleHover,
|
||||
} from "../../../../constants/constants";
|
||||
import { postCustomComponentUpdate } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
|
|
@ -182,7 +182,7 @@ export default function ParameterComponent({
|
|||
return (
|
||||
<div key={index}>
|
||||
{index === 0 && (
|
||||
<span>{left ? inputHandleHover : outputHandleHover}</span>
|
||||
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
|
||||
)}
|
||||
<span
|
||||
key={index}
|
||||
|
|
@ -304,7 +304,10 @@ export default function ParameterComponent({
|
|||
ref={ref}
|
||||
className={
|
||||
"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 Xmark from "../../components/ui/xmark";
|
||||
import {
|
||||
STATUS_BUILD,
|
||||
STATUS_BUILDING,
|
||||
priorityFields,
|
||||
statusBuild,
|
||||
statusBuilding,
|
||||
} from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
|
|
@ -49,7 +49,12 @@ export default function GenericNode({
|
|||
const [nodeDescription, setNodeDescription] = useState(
|
||||
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] =
|
||||
useState<validationStatusType | null>(null);
|
||||
const [handles, setHandles] = useState<number>(0);
|
||||
|
|
@ -332,8 +337,8 @@ export default function GenericNode({
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="group flex items-center gap-2.5">
|
||||
<ShadTooltip content={data.id}>
|
||||
<div className="group flex items-start gap-1.5">
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div
|
||||
onDoubleClick={(event) => {
|
||||
if (nameEditable) {
|
||||
|
|
@ -359,8 +364,8 @@ export default function GenericNode({
|
|||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="hidden h-4 w-4 animate-pulse text-status-blue group-hover:block"
|
||||
name="PencilLine"
|
||||
className="hidden h-3 w-3 text-status-blue group-hover:block"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -390,13 +395,34 @@ export default function GenericNode({
|
|||
})}
|
||||
data={data}
|
||||
color={
|
||||
nodeColors[
|
||||
types[data.node?.template[templateField].type!]
|
||||
] ??
|
||||
nodeColors[
|
||||
data.node?.template[templateField].type!
|
||||
] ??
|
||||
nodeColors.unknown
|
||||
data.node?.template[templateField].input_types &&
|
||||
data.node?.template[templateField].input_types!
|
||||
.length > 0
|
||||
? nodeColors[
|
||||
data.node?.template[templateField]
|
||||
.input_types![
|
||||
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(
|
||||
data.node?.template!,
|
||||
|
|
@ -458,23 +484,14 @@ export default function GenericNode({
|
|||
)}
|
||||
</div>
|
||||
{showNode && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={"group h-9 px-1.5"}
|
||||
onClick={() => {
|
||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||
return;
|
||||
setValidationStatus(null);
|
||||
buildFlow({ nodeId: data.id });
|
||||
}}
|
||||
>
|
||||
<Button variant="secondary" className={"group h-9 px-1.5"}>
|
||||
<div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
buildStatus === BuildStatus.BUILDING ? (
|
||||
<span> {statusBuilding} </span>
|
||||
<span> {STATUS_BUILDING} </span>
|
||||
) : !validationStatus ? (
|
||||
<span className="flex">{statusBuild}</span>
|
||||
<span className="flex">{STATUS_BUILD}</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{typeof validationStatus.params === "string"
|
||||
|
|
@ -489,7 +506,15 @@ export default function GenericNode({
|
|||
}
|
||||
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)}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
|
|
@ -607,13 +632,18 @@ export default function GenericNode({
|
|||
data.node?.template[templateField].input_types!
|
||||
.length > 0
|
||||
? nodeColors[
|
||||
data.node?.template[templateField]
|
||||
.input_types![0]
|
||||
data.node?.template[templateField].input_types![
|
||||
data.node?.template[templateField]
|
||||
.input_types!.length - 1
|
||||
]
|
||||
] ??
|
||||
nodeColors[
|
||||
types[
|
||||
data.node?.template[templateField]
|
||||
.input_types![0]
|
||||
.input_types![
|
||||
data.node?.template[templateField]
|
||||
.input_types!.length - 1
|
||||
]
|
||||
]
|
||||
]
|
||||
: nodeColors[
|
||||
|
|
@ -695,6 +725,15 @@ export default function GenericNode({
|
|||
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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "../../components/ui/popover";
|
||||
import { zeroNotifications } from "../../constants/constants";
|
||||
import { ZERO_NOTIFICATIONS } from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { AlertDropdownType } from "../../types/alerts";
|
||||
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">
|
||||
{zeroNotifications}
|
||||
{ZERO_NOTIFICATIONS}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default function IOOutputView({
|
|||
case "TextOutput":
|
||||
return (
|
||||
<Textarea
|
||||
className="w-full h-full custom-scroll"
|
||||
className="w-full custom-scroll"
|
||||
placeholder={"Empty"}
|
||||
// update to real value on flowPool
|
||||
value={
|
||||
|
|
@ -31,7 +31,7 @@ export default function IOOutputView({
|
|||
default:
|
||||
return (
|
||||
<Textarea
|
||||
className="w-full h-full custom-scroll"
|
||||
className="w-full custom-scroll"
|
||||
placeholder={"Enter text..."}
|
||||
value={node.data.node!.template["input_value"]}
|
||||
onChange={(e) => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
CHAT_FORM_DIALOG_SUBTITLE,
|
||||
outputsModalTitle,
|
||||
textInputModalTitle,
|
||||
OUTPUTS_MODAL_TITLE,
|
||||
TEXT_INPUT_MODAL_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import BaseModal from "../../modals/baseModal";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { NodeType } from "../../types/flow";
|
||||
import { updateVerticesOrder } from "../../utils/buildUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import AccordionComponent from "../AccordionComponent";
|
||||
|
|
@ -51,6 +52,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
|||
const [chatValue, setChatValue] = useState("");
|
||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
|
||||
async function updateVertices() {
|
||||
return updateVerticesOrder(currentFlow!.id, null);
|
||||
|
|
@ -68,12 +70,22 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
|||
setLockChat(true);
|
||||
setChatValue("");
|
||||
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);
|
||||
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(() => {
|
||||
|
|
@ -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">
|
||||
<IconComponent className="h-4 w-4" name={"Type"} />
|
||||
{textInputModalTitle}
|
||||
{TEXT_INPUT_MODAL_TITLE}
|
||||
</div>
|
||||
{nodes
|
||||
.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">
|
||||
<IconComponent className="h-4 w-4" name={"FileType2"} />
|
||||
{outputsModalTitle}
|
||||
{OUTPUTS_MODAL_TITLE}
|
||||
</div>
|
||||
{nodes
|
||||
.filter((node) =>
|
||||
|
|
@ -220,7 +232,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
|||
>
|
||||
<div>
|
||||
<Badge variant="gray" size="md">
|
||||
{output.displayName}
|
||||
{node.data.node.display_name}
|
||||
</Badge>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
|
|
@ -263,7 +275,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
|||
)}
|
||||
|
||||
{haveChat ? (
|
||||
<div className="flex h-full w-full">
|
||||
<div className="flex h-full min-w-96 flex-grow">
|
||||
{selectedViewField && (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ export default function CodeAreaComponent({
|
|||
setNodeClass,
|
||||
id = "",
|
||||
readonly = false,
|
||||
openModal,
|
||||
open,
|
||||
setOpen,
|
||||
}: CodeAreaComponentType) {
|
||||
const [myValue, setMyValue] = useState(
|
||||
typeof value == "string" ? value : JSON.stringify(value)
|
||||
|
|
@ -33,7 +34,8 @@ export default function CodeAreaComponent({
|
|||
return (
|
||||
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
||||
<CodeAreaModal
|
||||
openModal={openModal}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
readonly={readonly}
|
||||
dynamic={dynamic}
|
||||
value={myValue}
|
||||
|
|
|
|||
|
|
@ -193,9 +193,9 @@ export default function CodeTabsComponent({
|
|||
></div>
|
||||
)}
|
||||
<SyntaxHighlighter
|
||||
className="mt-0 h-full w-full overflow-auto custom-scroll"
|
||||
language={tab.mode}
|
||||
language={tab.language}
|
||||
style={oneDark}
|
||||
className="mt-0 h-full overflow-auto custom-scroll rounded-sm text-left"
|
||||
>
|
||||
{tab.code}
|
||||
</SyntaxHighlighter>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Node } from "reactflow";
|
||||
import { savedHover } from "../../../../constants/constants";
|
||||
import { SAVED_HOVER } from "../../../../constants/constants";
|
||||
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
|
|
@ -128,7 +128,7 @@ export const MenuBar = ({
|
|||
</div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
savedHover +
|
||||
SAVED_HOVER +
|
||||
new Date(currentFlow.updated_at ?? "").toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { useEffect, useState } from "react";
|
|||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import {
|
||||
chatInputPlaceholder,
|
||||
chatInputPlaceholderSend,
|
||||
CHAT_INPUT_PLACEHOLDER,
|
||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||
} from "../../../constants/constants";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { chatInputType } from "../../../types/components";
|
||||
|
|
@ -84,7 +84,7 @@ export default function ChatInput({
|
|||
"form-modal-lockchat"
|
||||
)}
|
||||
placeholder={
|
||||
noInput ? chatInputPlaceholder : chatInputPlaceholderSend
|
||||
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||
}
|
||||
/>
|
||||
<div className="form-modal-send-icon-position">
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default function ChatMessage({
|
|||
chat.isSend ? "" : " "
|
||||
)}
|
||||
>
|
||||
<div className={classNames("form-modal-chatbot-icon ")}>
|
||||
<div className={classNames("form-modal-chatbot-icon")}>
|
||||
{!chat.isSend ? (
|
||||
<div className="form-modal-chat-image">
|
||||
<div className="form-modal-chat-bot-icon ">
|
||||
|
|
@ -134,7 +134,7 @@ export default function ChatMessage({
|
|||
)}
|
||||
</div>
|
||||
{!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">
|
||||
{hidden && chat.thought && chat.thought !== "" && (
|
||||
<div
|
||||
|
|
@ -155,9 +155,9 @@ export default function ChatMessage({
|
|||
/>
|
||||
)}
|
||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
||||
<div className="w-full">
|
||||
<div className="w-full dark:text-white">
|
||||
<div className="w-full">
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="w-full flex flex-col dark:text-white">
|
||||
<div className="w-full flex flex-col">
|
||||
{useMemo(
|
||||
() =>
|
||||
chatMessage === "" && lockChat ? (
|
||||
|
|
@ -169,7 +169,7 @@ export default function ChatMessage({
|
|||
<Markdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
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"
|
||||
components={{
|
||||
pre({ node, ...props }) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { useEffect, useRef, useState } from "react";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
|
||||
import {
|
||||
chatFirstInitialText,
|
||||
chatSecondInitialText,
|
||||
CHAT_FIRST_INITIAL_TEXT,
|
||||
CHAT_SECOND_INITIAL_TEXT,
|
||||
} from "../../constants/constants";
|
||||
import { deleteFlowPool } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
|
|
@ -182,14 +182,14 @@ export default function NewChatView({
|
|||
<br />
|
||||
<div className="langflow-chat-desc">
|
||||
<span className="langflow-chat-desc-span">
|
||||
{chatFirstInitialText}{" "}
|
||||
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
||||
<span>
|
||||
<IconComponent
|
||||
name="MessageSquare"
|
||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||
/>
|
||||
</span>{" "}
|
||||
{chatSecondInitialText}
|
||||
{CHAT_SECOND_INITIAL_TEXT}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect } from "react";
|
||||
import { editTextModalTitle } from "../../constants/constants";
|
||||
import { EDIT_TEXT_MODAL_TITLE } from "../../constants/constants";
|
||||
import { TypeModal } from "../../constants/enums";
|
||||
import GenericModal from "../../modals/genericModal";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
|
|
@ -38,7 +38,7 @@ export default function TextAreaComponent({
|
|||
<GenericModal
|
||||
type={TypeModal.TEXT}
|
||||
buttonText="Finish Editing"
|
||||
modalTitle={editTextModalTitle}
|
||||
modalTitle={EDIT_TEXT_MODAL_TITLE}
|
||||
value={value}
|
||||
setValue={(value: string) => {
|
||||
onChange(value);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
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
|
||||
)}
|
||||
{...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 FLOW_BUILD_SUCCESS_ALERT = `Flow built 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 OUTPUT_TYPES = new Set(["ChatOutput", "TextOutput"]);
|
||||
|
||||
export const chatFirstInitialText =
|
||||
export const CHAT_FIRST_INITIAL_TEXT =
|
||||
"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.";
|
||||
|
||||
export const saveWApiCheckbox = "Save with my API keys";
|
||||
export const editTextModalTitle = "Edit Text";
|
||||
export const editTextPlaceholder = "Type message here.";
|
||||
export const inputHandleHover = "Avaliable input components:";
|
||||
export const outputHandleHover = "Avaliable output components:";
|
||||
export const textInputModalTitle = "Text Inputs";
|
||||
export const outputsModalTitle = "Text Outputs";
|
||||
export const langflowChatTitle = "Langflow Chat";
|
||||
export const chatInputPlaceholder =
|
||||
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||
export const EDIT_TEXT_PLACEHOLDER = "Type message here.";
|
||||
export const INPUT_HANDLER_HOVER = "Avaliable input components:";
|
||||
export const OUTPUT_HANDLER_HOVER = "Avaliable output components:";
|
||||
export const TEXT_INPUT_MODAL_TITLE = "Text Inputs";
|
||||
export const OUTPUTS_MODAL_TITLE = "Text Outputs";
|
||||
export const LANGFLOW_CHAT_TITLE = "Langflow Chat";
|
||||
export const CHAT_INPUT_PLACEHOLDER =
|
||||
"No chat input variables found. Click to run your flow.";
|
||||
export const chatInputPlaceholderSend = "Send a message...";
|
||||
export const editCodeTitle = "Edit Code";
|
||||
export const myCollectionDesc =
|
||||
export const CHAT_INPUT_PLACEHOLDER_SEND = "Send a message...";
|
||||
export const EDIT_CODE_TITLE = "Edit Code";
|
||||
export const MY_COLLECTION_DESC =
|
||||
"Manage your personal projects. Download and upload entire collections.";
|
||||
export const storeDesc = "Explore community-shared flows and components.";
|
||||
export const storeTitle = "Langflow Store";
|
||||
export const noApi = "You don't have an API key. ";
|
||||
export const insertApi = "Insert your Langflow API key.";
|
||||
export const invalidApi = "Your API key is not valid. ";
|
||||
export const createApi = `Don’t have an API key? Sign up at`;
|
||||
export const statusBuild = "Build to validate status.";
|
||||
export const statusBuilding = "Building...";
|
||||
export const savedHover = "Last saved at ";
|
||||
export const STORE_DESC = "Explore community-shared flows and components.";
|
||||
export const STORE_TITLE = "Langflow Store";
|
||||
export const NO_API_KEY = "You don't have an API key. ";
|
||||
export const INSERT_API_KEY = "Insert your Langflow API key.";
|
||||
export const INVALID_API_KEY = "Your API key is not valid. ";
|
||||
export const CREATE_API_KEY = `Don’t have an API key? Sign up at`;
|
||||
export const STATUS_BUILD = "Build to validate status.";
|
||||
export const STATUS_BUILDING = "Building...";
|
||||
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(
|
||||
flowId: string,
|
||||
nodeId?: string | null
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
|
||||
// nodeId is optional and is a query parameter
|
||||
// if nodeId is not provided, the API will return all vertices
|
||||
const config = {};
|
||||
if (nodeId) {
|
||||
config["params"] = { component_id: nodeId };
|
||||
if (stopNodeId) {
|
||||
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);
|
||||
}
|
||||
|
|
@ -872,6 +875,7 @@ export async function postBuildVertex(
|
|||
vertexId: string,
|
||||
input_value: string
|
||||
): Promise<AxiosResponse<VertexBuildTypeAPI>> {
|
||||
// input_value is optional and is a query parameter
|
||||
return await api.post(
|
||||
`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`,
|
||||
input_value ? { inputs: { input_value: input_value } } : undefined
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ const EditNodeModal = forwardRef(
|
|||
)
|
||||
) ?? false;
|
||||
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">
|
||||
<ShadTooltip
|
||||
content={
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {
|
|||
API_SUCCESS_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
createApi,
|
||||
insertApi,
|
||||
invalidApi,
|
||||
noApi,
|
||||
CREATE_API_KEY,
|
||||
INSERT_API_KEY,
|
||||
INVALID_API_KEY,
|
||||
NO_API_KEY,
|
||||
} from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { addApiKeyStore } from "../../controllers/API";
|
||||
|
|
@ -68,8 +68,11 @@ export default function StoreApiKeyModal({
|
|||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header
|
||||
description={
|
||||
(hasApiKey && !validApiKey ? invalidApi : !hasApiKey ? noApi : "") +
|
||||
insertApi
|
||||
(hasApiKey && !validApiKey
|
||||
? INVALID_API_KEY
|
||||
: !hasApiKey
|
||||
? NO_API_KEY
|
||||
: "") + INSERT_API_KEY
|
||||
}
|
||||
>
|
||||
<span className="pr-2">API Key</span>
|
||||
|
|
@ -104,7 +107,7 @@ export default function StoreApiKeyModal({
|
|||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<span className="pr-1 text-xs text-muted-foreground">
|
||||
{createApi}{" "}
|
||||
{CREATE_API_KEY}{" "}
|
||||
<a
|
||||
className="text-high-indigo underline"
|
||||
href="https://langflow.store/"
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ function BaseModal({
|
|||
minWidth = "min-w-[60vw]";
|
||||
break;
|
||||
case "large":
|
||||
minWidth = "min-w-[80vw]";
|
||||
minWidth = "min-w-[85vw]";
|
||||
height = "h-[80vh]";
|
||||
break;
|
||||
case "large-thin":
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
CODE_PROMPT_DIALOG_SUBTITLE,
|
||||
editCodeTitle,
|
||||
EDIT_CODE_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
|
|
@ -35,9 +35,14 @@ export default function CodeAreaModal({
|
|||
children,
|
||||
dynamic,
|
||||
readonly = false,
|
||||
openModal,
|
||||
open: myOpen,
|
||||
setOpen: mySetOpen,
|
||||
}: codeAreaModalPropsType): JSX.Element {
|
||||
const [code, setCode] = useState(value);
|
||||
const [open, setOpen] =
|
||||
mySetOpen !== undefined && myOpen !== undefined
|
||||
? [myOpen, mySetOpen]
|
||||
: useState(false);
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
const unselectAll = useFlowStore((state) => state.unselectAll);
|
||||
|
||||
|
|
@ -56,10 +61,6 @@ export default function CodeAreaModal({
|
|||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (openModal) setOpen(true);
|
||||
}, [openModal]);
|
||||
|
||||
function processNonDynamicField() {
|
||||
postValidateCode(code)
|
||||
.then((apiReturn) => {
|
||||
|
|
@ -143,8 +144,6 @@ export default function CodeAreaModal({
|
|||
};
|
||||
}, [error, setHeight]);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setCode(value);
|
||||
}, [value, open]);
|
||||
|
|
@ -153,7 +152,7 @@ export default function CodeAreaModal({
|
|||
<BaseModal open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
|
||||
<span className="pr-2"> {editCodeTitle} </span>
|
||||
<span className="pr-2"> {EDIT_CODE_TITLE} </span>
|
||||
<IconComponent
|
||||
name="prompts"
|
||||
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 { API_WARNING_NOTICE_ALERT } from "../../constants/alerts_constants";
|
||||
import {
|
||||
ALERT_SAVE_WITH_API,
|
||||
EXPORT_DIALOG_SUBTITLE,
|
||||
alertSaveWApi,
|
||||
saveWApiCheckbox,
|
||||
SAVE_WITH_API_CHECKBOX,
|
||||
} from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
|
|
@ -56,10 +56,12 @@ const ExportModal = forwardRef(
|
|||
}}
|
||||
/>
|
||||
<label htmlFor="terms" className="export-modal-save-api text-sm ">
|
||||
{saveWApiCheckbox}
|
||||
{SAVE_WITH_API_CHECKBOX}
|
||||
</label>
|
||||
</div>
|
||||
<span className=" text-xs text-destructive ">{alertSaveWApi}</span>
|
||||
<span className=" text-xs text-destructive ">
|
||||
{ALERT_SAVE_WITH_API}
|
||||
</span>
|
||||
</BaseModal.Content>
|
||||
|
||||
<BaseModal.Footer>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { useEffect } from "react";
|
|||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import {
|
||||
chatInputPlaceholder,
|
||||
chatInputPlaceholderSend,
|
||||
CHAT_INPUT_PLACEHOLDER,
|
||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||
} from "../../../constants/constants";
|
||||
import { chatInputType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
|
|
@ -55,7 +55,7 @@ export default function ChatInput({
|
|||
? "Thinking..."
|
||||
: typeof chatValue === "object" &&
|
||||
Object.keys(chatValue)?.length === 0
|
||||
? chatInputPlaceholder
|
||||
? CHAT_INPUT_PLACEHOLDER
|
||||
: chatValue
|
||||
}
|
||||
onChange={(event): void => {
|
||||
|
|
@ -70,7 +70,9 @@ export default function ChatInput({
|
|||
|
||||
"form-modal-lockchat"
|
||||
)}
|
||||
placeholder={noInput ? chatInputPlaceholder : chatInputPlaceholderSend}
|
||||
placeholder={
|
||||
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||
}
|
||||
/>
|
||||
<div className="form-modal-send-icon-position">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import {
|
|||
MSG_ERROR_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
CHAT_FIRST_INITIAL_TEXT,
|
||||
CHAT_FORM_DIALOG_SUBTITLE,
|
||||
chatFirstInitialText,
|
||||
chatSecondInitialText,
|
||||
langflowChatTitle,
|
||||
CHAT_SECOND_INITIAL_TEXT,
|
||||
LANGFLOW_CHAT_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
|
|
@ -594,20 +594,20 @@ export default function FormModal({
|
|||
<span>
|
||||
👋{" "}
|
||||
<span className="langflow-chat-span">
|
||||
{langflowChatTitle}
|
||||
{LANGFLOW_CHAT_TITLE}
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<div className="langflow-chat-desc">
|
||||
<span className="langflow-chat-desc-span">
|
||||
{chatFirstInitialText}{" "}
|
||||
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
||||
<span>
|
||||
<IconComponent
|
||||
name="MessageSquare"
|
||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||
/>
|
||||
</span>{" "}
|
||||
{chatSecondInitialText}
|
||||
{CHAT_SECOND_INITIAL_TEXT}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import {
|
|||
TEMP_NOTICE_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
EDIT_TEXT_PLACEHOLDER,
|
||||
INVALID_CHARACTERS,
|
||||
MAX_WORDS_HIGHLIGHT,
|
||||
PROMPT_DIALOG_SUBTITLE,
|
||||
TEXT_DIALOG_SUBTITLE,
|
||||
editTextPlaceholder,
|
||||
regexHighlight,
|
||||
} from "../../constants/constants";
|
||||
import { TypeModal } from "../../constants/enums";
|
||||
|
|
@ -227,7 +227,7 @@ export default function GenericModal({
|
|||
setInputValue(event.target.value);
|
||||
checkVariables(event.target.value);
|
||||
}}
|
||||
placeholder={editTextPlaceholder}
|
||||
placeholder={EDIT_TEXT_PLACEHOLDER}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, inputValue, "");
|
||||
}}
|
||||
|
|
@ -249,7 +249,7 @@ export default function GenericModal({
|
|||
onChange={(event) => {
|
||||
setInputValue(event.target.value);
|
||||
}}
|
||||
placeholder={editTextPlaceholder}
|
||||
placeholder={EDIT_TEXT_PLACEHOLDER}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -219,23 +219,6 @@ export default function NodeToolbarComponent({
|
|||
}}
|
||||
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" />
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
|
@ -492,6 +475,26 @@ export default function NodeToolbarComponent({
|
|||
is_component={true}
|
||||
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>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default function ComponentsComponent({
|
|||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [pageIndex, setPageIndex] = useState(1);
|
||||
const [loadingScreen, setLoadingScreen] = useState(true);
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ export default function ComponentsComponent({
|
|||
|
||||
function resetFilter() {
|
||||
setPageIndex(1);
|
||||
setPageSize(10);
|
||||
setPageSize(20);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import SidebarNav from "../../components/sidebarComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
||||
import {
|
||||
MY_COLLECTION_DESC,
|
||||
USER_PROJECTS_HEADER,
|
||||
myCollectionDesc,
|
||||
} from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
|
@ -75,7 +75,7 @@ export default function HomePage(): JSX.Element {
|
|||
return (
|
||||
<PageLayout
|
||||
title={USER_PROJECTS_HEADER}
|
||||
description={myCollectionDesc}
|
||||
description={MY_COLLECTION_DESC}
|
||||
button={
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
INVALID_API_ERROR_ALERT,
|
||||
NOAPI_ERROR_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import { storeDesc, storeTitle } from "../../constants/constants";
|
||||
import { STORE_DESC, STORE_TITLE } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
||||
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
||||
|
|
@ -174,8 +174,8 @@ export default function StorePage(): JSX.Element {
|
|||
return (
|
||||
<PageLayout
|
||||
betaIcon
|
||||
title={storeTitle}
|
||||
description={storeDesc}
|
||||
title={STORE_TITLE}
|
||||
description={STORE_DESC}
|
||||
button={
|
||||
<>
|
||||
{StoreApiKeyModal && (
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
FLOW_BUILD_SUCCESS_ALERT,
|
||||
MISSED_ERROR_ALERT,
|
||||
} from "../constants/alerts_constants";
|
||||
import { RUN_TIMESTAMP_PREFIX } from "../constants/constants";
|
||||
import { BuildStatus } from "../constants/enums";
|
||||
import { getFlowPool } from "../controllers/API";
|
||||
import { VertexBuildTypeAPI } from "../types/api";
|
||||
|
|
@ -416,10 +417,12 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
},
|
||||
buildFlow: async ({
|
||||
nodeId,
|
||||
startNodeId,
|
||||
stopNodeId,
|
||||
input_value,
|
||||
}: {
|
||||
nodeId?: string;
|
||||
startNodeId?: string;
|
||||
stopNodeId?: string;
|
||||
input_value?: string;
|
||||
}) => {
|
||||
get().setIsBuilding(true);
|
||||
|
|
@ -444,7 +447,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
function handleBuildUpdate(
|
||||
vertexBuildData: VertexBuildTypeAPI,
|
||||
status: BuildStatus,
|
||||
buildId: string
|
||||
runId: string
|
||||
) {
|
||||
if (vertexBuildData && vertexBuildData.inactivated_vertices) {
|
||||
get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices);
|
||||
|
|
@ -452,20 +455,47 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
if (vertexBuildData && vertexBuildData.activated_layers) {
|
||||
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(
|
||||
{ ...vertexBuildData, buildId },
|
||||
{ ...vertexBuildData, buildId: runId },
|
||||
vertexBuildData.id
|
||||
);
|
||||
|
||||
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
|
||||
}
|
||||
await buildVertices({
|
||||
input_value,
|
||||
flowId: currentFlow!.id,
|
||||
nodeId,
|
||||
startNodeId,
|
||||
stopNodeId,
|
||||
onGetOrderSuccess: () => {
|
||||
setNoticeData({ title: "Running components" });
|
||||
},
|
||||
onBuildComplete: () => {
|
||||
const nodeId = startNodeId || stopNodeId;
|
||||
if (nodeId) {
|
||||
setSuccessData({
|
||||
title: `${
|
||||
|
|
@ -482,6 +512,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
onBuildError: (title, list, idList) => {
|
||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILT);
|
||||
setErrorData({ list, title });
|
||||
get().setIsBuilding(false);
|
||||
},
|
||||
onBuildStart: (idList) => {
|
||||
console.log("onBuildStart", idList);
|
||||
|
|
@ -489,6 +520,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
},
|
||||
validateNodes: validateSubgraph,
|
||||
});
|
||||
get().setIsBuilding(false);
|
||||
get().revertBuiltStatusFromBuilding();
|
||||
},
|
||||
getFlow: () => {
|
||||
|
|
@ -505,6 +537,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
runId: string;
|
||||
} | null
|
||||
) => {
|
||||
console.log("updateVerticesBuild", vertices);
|
||||
set({ verticesBuild: vertices });
|
||||
},
|
||||
verticesBuild: null,
|
||||
|
|
@ -531,17 +564,27 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
},
|
||||
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {
|
||||
console.log("updateBuildStatus", nodeIdList, status);
|
||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||
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 });
|
||||
},
|
||||
revertBuiltStatusFromBuilding: () => {
|
||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||
Object.keys(newFlowBuildStatus).forEach((id) => {
|
||||
if (newFlowBuildStatus[id] === BuildStatus.BUILDING) {
|
||||
newFlowBuildStatus[id] = BuildStatus.BUILT;
|
||||
if (newFlowBuildStatus[id].status === BuildStatus.BUILDING) {
|
||||
newFlowBuildStatus[id].status = BuildStatus.BUILT;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -845,13 +845,13 @@
|
|||
}
|
||||
|
||||
.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 {
|
||||
@apply flex items-center justify-between px-2 py-2;
|
||||
}
|
||||
.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 {
|
||||
@apply mt-2 flex h-full w-full;
|
||||
|
|
@ -902,7 +902,7 @@
|
|||
@apply flex-max-width px-2 py-6 pl-4 pr-9;
|
||||
}
|
||||
.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 {
|
||||
@apply flex flex-col items-center gap-1;
|
||||
|
|
|
|||
|
|
@ -134,14 +134,15 @@ export type Component = {
|
|||
};
|
||||
|
||||
export type VerticesOrderTypeAPI = {
|
||||
ids: Array<Array<string>>;
|
||||
ids: Array<string>;
|
||||
run_id: string;
|
||||
};
|
||||
|
||||
export type VertexBuildTypeAPI = {
|
||||
id: string;
|
||||
inactivated_vertices: Array<string> | null;
|
||||
activated_layers: Array<Array<string>> | null;
|
||||
next_vertices_ids: Array<string>;
|
||||
run_id: string;
|
||||
valid: boolean;
|
||||
params: string;
|
||||
data: VertexDataTypeAPI;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ export type CodeAreaComponentType = {
|
|||
dynamic?: boolean;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
openModal?: boolean;
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type FileComponentType = {
|
||||
|
|
@ -519,7 +520,8 @@ export type codeAreaModalPropsType = {
|
|||
children: ReactNode;
|
||||
dynamic?: boolean;
|
||||
readonly?: boolean;
|
||||
openModal?: boolean;
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type chatMessagePropsType = {
|
||||
|
|
|
|||
|
|
@ -90,10 +90,13 @@ export type FlowStoreType = {
|
|||
onConnect: (connection: Connection) => void;
|
||||
unselectAll: () => void;
|
||||
buildFlow: ({
|
||||
nodeId,
|
||||
startNodeId,
|
||||
stopNodeId,
|
||||
input_value,
|
||||
}: {
|
||||
nodeId?: string;
|
||||
startNodeId?: string;
|
||||
stopNodeId?: string;
|
||||
input_value?: string;
|
||||
}) => Promise<void>;
|
||||
getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
||||
|
|
@ -113,7 +116,9 @@ export type FlowStoreType = {
|
|||
} | null;
|
||||
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
|
||||
revertBuiltStatusFromBuilding: () => void;
|
||||
flowBuildStatus: { [key: string]: BuildStatus };
|
||||
flowBuildStatus: {
|
||||
[key: string]: { status: BuildStatus; timestamp?: string };
|
||||
};
|
||||
updateFlowPool: (
|
||||
nodeId: string,
|
||||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import { VertexBuildTypeAPI } from "../types/api";
|
|||
type BuildVerticesParams = {
|
||||
flowId: string; // Assuming FlowType is the type for your flow
|
||||
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;
|
||||
onBuildUpdate?: (
|
||||
data: VertexBuildTypeAPI,
|
||||
|
|
@ -32,7 +33,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
|||
data: inactiveData,
|
||||
params: "Inactive",
|
||||
inactivated_vertices: null,
|
||||
activated_layers: null,
|
||||
run_id: "",
|
||||
next_vertices_ids: [],
|
||||
inactive_vertices: null,
|
||||
valid: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
|
@ -42,7 +45,8 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
|||
|
||||
export async function updateVerticesOrder(
|
||||
flowId: string,
|
||||
nodeId: string | null
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
): Promise<{
|
||||
verticesLayers: string[][];
|
||||
verticesIds: string[];
|
||||
|
|
@ -52,7 +56,7 @@ export async function updateVerticesOrder(
|
|||
const setErrorData = useAlertStore.getState().setErrorData;
|
||||
let orderResponse;
|
||||
try {
|
||||
orderResponse = await getVerticesOrder(flowId, nodeId);
|
||||
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
setErrorData({
|
||||
|
|
@ -62,27 +66,26 @@ export async function updateVerticesOrder(
|
|||
useFlowStore.getState().setIsBuilding(false);
|
||||
throw new Error("Invalid nodes");
|
||||
}
|
||||
let verticesOrder: Array<Array<string>> = orderResponse.data.ids;
|
||||
let verticesLayers: Array<Array<string>> = [];
|
||||
let verticesLayers: Array<Array<string>> = [orderResponse.data.ids];
|
||||
const runId = orderResponse.data.run_id;
|
||||
if (nodeId) {
|
||||
for (let i = 0; i < verticesOrder.length; i += 1) {
|
||||
const innerArray = verticesOrder[i];
|
||||
const idIndex = innerArray.indexOf(nodeId);
|
||||
if (idIndex !== -1) {
|
||||
// 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
|
||||
// and we are stopping at the layer that contains the nodeId
|
||||
verticesLayers.push([innerArray[idIndex]]);
|
||||
break; // Stop searching after finding the first occurrence
|
||||
}
|
||||
// If the targetId is not found, include the entire inner array
|
||||
verticesLayers.push(innerArray);
|
||||
}
|
||||
} else {
|
||||
verticesLayers = verticesOrder;
|
||||
}
|
||||
const verticesIds = verticesOrder.flat();
|
||||
// if (nodeId) {
|
||||
// for (let i = 0; i < verticesOrder.length; i += 1) {
|
||||
// const innerArray = verticesOrder[i];
|
||||
// const idIndex = innerArray.indexOf(nodeId);
|
||||
// if (idIndex !== -1) {
|
||||
// // 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
|
||||
// // and we are stopping at the layer that contains the nodeId
|
||||
// verticesLayers.push([innerArray[idIndex]]);
|
||||
// break; // Stop searching after finding the first occurrence
|
||||
// }
|
||||
// // If the targetId is not found, include the entire inner array
|
||||
// verticesLayers.push(innerArray);
|
||||
// }
|
||||
// } else {
|
||||
// verticesLayers = verticesOrder;
|
||||
// }
|
||||
const verticesIds = orderResponse.data.ids;
|
||||
useFlowStore.getState().updateVerticesBuild({
|
||||
verticesLayers,
|
||||
verticesIds,
|
||||
|
|
@ -95,7 +98,8 @@ export async function updateVerticesOrder(
|
|||
export async function buildVertices({
|
||||
flowId,
|
||||
input_value,
|
||||
nodeId = null,
|
||||
startNodeId,
|
||||
stopNodeId,
|
||||
onGetOrderSuccess,
|
||||
onBuildUpdate,
|
||||
onBuildComplete,
|
||||
|
|
@ -104,9 +108,13 @@ export async function buildVertices({
|
|||
validateNodes,
|
||||
}: BuildVerticesParams) {
|
||||
let verticesBuild = useFlowStore.getState().verticesBuild;
|
||||
|
||||
if (!verticesBuild || nodeId) {
|
||||
verticesBuild = await updateVerticesOrder(flowId, nodeId);
|
||||
// if startNodeId and stopNodeId are provided
|
||||
// something is wrong
|
||||
if (startNodeId && stopNodeId) {
|
||||
return;
|
||||
}
|
||||
if (!verticesBuild || startNodeId || stopNodeId) {
|
||||
verticesBuild = await updateVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||
}
|
||||
|
||||
const verticesIds = verticesBuild?.verticesIds!;
|
||||
|
|
@ -126,98 +134,83 @@ export async function buildVertices({
|
|||
|
||||
useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD);
|
||||
useFlowStore.getState().setIsBuilding(true);
|
||||
let dynamicVerticesLayers: Array<Array<string>> = [...verticesLayers];
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
let currentLayerIndex = 0; // Start with the first layer
|
||||
// Set each vertex state to building
|
||||
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) {
|
||||
// Check if id is in the list of inactive nodes
|
||||
// useFlowStore because it gets updated constantly
|
||||
if (
|
||||
!useFlowStore.getState().verticesBuild?.verticesIds.includes(id) &&
|
||||
onBuildUpdate
|
||||
) {
|
||||
// If it is, skip building and set the state to inactive
|
||||
console.log("inactive", id);
|
||||
onBuildUpdate(getInactiveVertexData(id), BuildStatus.INACTIVE, runId);
|
||||
buildResults.push(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
await buildVertex({
|
||||
flowId,
|
||||
id,
|
||||
input_value,
|
||||
onBuildUpdate: handleBuildUpdate,
|
||||
onBuildError,
|
||||
verticesIds,
|
||||
buildResults,
|
||||
stopBuild: () => {
|
||||
stop = true;
|
||||
},
|
||||
});
|
||||
if (stop) {
|
||||
break;
|
||||
// Build each layer
|
||||
while (
|
||||
currentLayerIndex <
|
||||
(useFlowStore.getState().verticesBuild?.verticesLayers! || []).length
|
||||
) {
|
||||
// Get the current layer
|
||||
const currentLayer =
|
||||
useFlowStore.getState().verticesBuild?.verticesLayers![currentLayerIndex];
|
||||
// If there are no more layers, we are done
|
||||
if (!currentLayer) {
|
||||
if (onBuildComplete) {
|
||||
const allNodesValid = buildResults.every((result) => result);
|
||||
onBuildComplete(allNodesValid);
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
}
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (onBuildComplete) {
|
||||
const allNodesValid = buildResults.every((result) => result);
|
||||
onBuildComplete(allNodesValid);
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
if (onBuildComplete) {
|
||||
const allNodesValid = buildResults.every((result) => result);
|
||||
onBuildComplete(allNodesValid);
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function buildVertex({
|
||||
flowId,
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import {
|
|||
INPUT_TYPES,
|
||||
LANGFLOW_SUPPORTED_TYPES,
|
||||
OUTPUT_TYPES,
|
||||
SUCCESS_BUILD,
|
||||
specialCharsRegex,
|
||||
successBuild,
|
||||
} from "../constants/constants";
|
||||
import { downloadFlowsFromDatabase } from "../controllers/API";
|
||||
import {
|
||||
|
|
@ -1092,7 +1092,7 @@ export function getGroupStatus(
|
|||
flow: FlowType,
|
||||
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 ids = nodes.map((n: NodeType) => n.data.id);
|
||||
ids.forEach((id) => {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ import {
|
|||
Network,
|
||||
Paperclip,
|
||||
Pencil,
|
||||
PencilLine,
|
||||
Pin,
|
||||
Play,
|
||||
Plus,
|
||||
|
|
@ -418,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Group,
|
||||
LogIn,
|
||||
ChevronUp,
|
||||
PencilLine,
|
||||
Ungroup,
|
||||
BookMarked,
|
||||
Minus,
|
||||
|
|
|
|||
|
|
@ -640,7 +640,5 @@ export function getFieldTitle(
|
|||
): string {
|
||||
return template[templateField].display_name
|
||||
? template[templateField].display_name!
|
||||
: template[templateField].name
|
||||
? toTitleCase(template[templateField].name!, true)
|
||||
: toTitleCase(templateField, true);
|
||||
: template[templateField].name ?? templateField;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue