From ec0af06573e9296fa5df72bae0e5f13a0b87bbd1 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 23 Apr 2024 09:26:15 -0300 Subject: [PATCH 01/78] Add playground button to flow cards in MainPage (#1752) * Add playground button to flow cards in MainPage * Add IOModal component for playground functionality * Update playground button styling not working yet --- .../src/components/cardComponent/index.tsx | 3 ++ .../MainPage/components/components/index.tsx | 40 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 2ec60e0a2..f9eef310e 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -25,11 +25,13 @@ export default function CollectionCardComponent({ disabled = false, button, onDelete, + playground }: { data: storeComponent; authorized?: boolean; disabled?: boolean; button?: JSX.Element; + playground?:JSX.Element; onDelete?: () => void; }) { const addFlow = useFlowsManagerStore((state) => state.addFlow); @@ -345,6 +347,7 @@ export default function CollectionCardComponent({ )} {button && button} + {playground && playground} diff --git a/src/frontend/src/pages/MainPage/components/components/index.tsx b/src/frontend/src/pages/MainPage/components/components/index.tsx index 65189fa8a..ba5879747 100644 --- a/src/frontend/src/pages/MainPage/components/components/index.tsx +++ b/src/frontend/src/pages/MainPage/components/components/index.tsx @@ -14,6 +14,7 @@ import { import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { FlowType } from "../../../../types/flow"; +import IOModal from "../../../../modals/IOModal"; export default function ComponentsComponent({ is_component = true, @@ -21,6 +22,7 @@ export default function ComponentsComponent({ is_component?: boolean; }) { const addFlow = useFlowsManagerStore((state) => state.addFlow); + const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const removeFlow = useFlowsManagerStore((state) => state.removeFlow); const isLoading = useFlowsManagerStore((state) => state.isLoading); @@ -31,6 +33,7 @@ export default function ComponentsComponent({ const [pageSize, setPageSize] = useState(20); const [pageIndex, setPageIndex] = useState(1); const [loadingScreen, setLoadingScreen] = useState(true); + const [openPlayground, setOpenPlayground] = useState(false); const navigate = useNavigate(); @@ -75,9 +78,8 @@ export default function ComponentsComponent({ }) .then(() => { setSuccessData({ - title: `${ - is_component ? "Component" : "Flow" - } uploaded successfully`, + title: `${is_component ? "Component" : "Flow" + } uploaded successfully`, }); }) .catch((error) => { @@ -142,9 +144,8 @@ export default function ComponentsComponent({ onDelete={() => { removeFlow(item.id); setSuccessData({ - title: `${ - item.is_component ? "Component" : "Flow" - } deleted successfully!`, + title: `${item.is_component ? "Component" : "Flow" + } deleted successfully!`, }); resetFilter(); }} @@ -174,6 +175,33 @@ export default function ComponentsComponent({ <> ) } + playground={ + !is_component ? ( + + + + ) : ( + <> + ) + } /> )) ) : ( From bb6ad3939b6b06f505772ea091a48220a20b69a8 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 23 Apr 2024 16:48:00 -0300 Subject: [PATCH 02/78] first implementation, working but not stable --- .../src/components/cardComponent/index.tsx | 56 ++++++++++++++++--- src/frontend/src/modals/IOModal/index.tsx | 1 + .../MainPage/components/components/index.tsx | 33 +---------- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index f9eef310e..21701f459 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -18,6 +18,8 @@ import { CardHeader, CardTitle, } from "../ui/card"; +import IOModal from "../../modals/IOModal"; +import useFlowStore from "../../stores/flowStore"; export default function CollectionCardComponent({ data, @@ -31,7 +33,7 @@ export default function CollectionCardComponent({ authorized?: boolean; disabled?: boolean; button?: JSX.Element; - playground?:JSX.Element; + playground?: boolean; onDelete?: () => void; }) { const addFlow = useFlowsManagerStore((state) => state.addFlow); @@ -48,9 +50,24 @@ export default function CollectionCardComponent({ const [downloads_count, setDownloads_count] = useState( data?.downloads_count ?? 0 ); + const currentFlow = useFlowsManagerStore((state) => state.currentFlow); + const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); + const setNodes = useFlowStore((state) => state.setNodes); + const setEdges = useFlowStore((state) => state.setEdges); + const [openPlayground, setOpenPlayground] = useState(false); + const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId); + + const name = data.is_component ? "Component" : "Flow"; + useEffect(() => { + if (currentFlowId && playground) { + setNodes(currentFlow?.data?.nodes ?? []); + setEdges(currentFlow?.data?.edges ?? []); + } + }, [currentFlowId]); + useEffect(() => { if (data) { setLiked_by_user(data?.liked_by_user ?? false); @@ -69,18 +86,16 @@ export default function CollectionCardComponent({ addFlow(true, newFlow) .then((id) => { setSuccessData({ - title: `${name} ${ - isStore ? "Downloaded" : "Installed" - } Successfully.`, + title: `${name} ${isStore ? "Downloaded" : "Installed" + } Successfully.`, }); setLoading(false); }) .catch((error) => { setLoading(false); setErrorData({ - title: `Error ${ - isStore ? "downloading" : "installing" - } the ${name}`, + title: `Error ${isStore ? "downloading" : "installing" + } the ${name}`, list: [error["response"]["data"]["detail"]], }); }); @@ -347,10 +362,33 @@ export default function CollectionCardComponent({ )} {button && button} - {playground && playground} + {playground && + + } - + ); } diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index c81aecc35..c59368fe5 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -78,6 +78,7 @@ export default function IOModal({ const isBuilding = useFlowStore((state) => state.isBuilding); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const setNode = useFlowStore((state) => state.setNode); + console.log(nodes) async function updateVertices() { return updateVerticesOrder(currentFlow!.id, null); diff --git a/src/frontend/src/pages/MainPage/components/components/index.tsx b/src/frontend/src/pages/MainPage/components/components/index.tsx index ba5879747..55bc6a511 100644 --- a/src/frontend/src/pages/MainPage/components/components/index.tsx +++ b/src/frontend/src/pages/MainPage/components/components/index.tsx @@ -14,15 +14,12 @@ import { import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { FlowType } from "../../../../types/flow"; -import IOModal from "../../../../modals/IOModal"; - export default function ComponentsComponent({ is_component = true, }: { is_component?: boolean; }) { const addFlow = useFlowsManagerStore((state) => state.addFlow); - const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const removeFlow = useFlowsManagerStore((state) => state.removeFlow); const isLoading = useFlowsManagerStore((state) => state.isLoading); @@ -33,8 +30,6 @@ export default function ComponentsComponent({ const [pageSize, setPageSize] = useState(20); const [pageIndex, setPageIndex] = useState(1); const [loadingScreen, setLoadingScreen] = useState(true); - const [openPlayground, setOpenPlayground] = useState(false); - const navigate = useNavigate(); useEffect(() => { @@ -175,33 +170,7 @@ export default function ComponentsComponent({ <> ) } - playground={ - !is_component ? ( - - - - ) : ( - <> - ) - } + playground={!is_component} /> )) ) : ( From 34ac7f359a44e5e5c729afed968fa8bdf14c4c6e Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 23 Apr 2024 16:53:38 -0300 Subject: [PATCH 03/78] removed console.log --- src/frontend/src/modals/IOModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index c59368fe5..c81aecc35 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -78,7 +78,6 @@ export default function IOModal({ const isBuilding = useFlowStore((state) => state.isBuilding); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const setNode = useFlowStore((state) => state.setNode); - console.log(nodes) async function updateVertices() { return updateVerticesOrder(currentFlow!.id, null); From b07b937b20430d5b6d0bd8e1c40e107424238077 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 23 Apr 2024 20:38:04 -0300 Subject: [PATCH 04/78] Update build_and_cache_graph function to handle graph data from database and from input data (#1750) * Update build_and_cache_graph function to handle graph data from database and from input data * Update get_vertices endpoint to handle graph data from input * Update build_vertex function to handle graph data from database * Update build_vertex function to handle graph data from input * Update FlowDataRequest schema to include optional viewport field --- src/backend/base/langflow/api/utils.py | 13 ++++++++++++- src/backend/base/langflow/api/v1/chat.py | 20 +++++++++++++++----- src/backend/base/langflow/api/v1/schemas.py | 12 ++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/backend/base/langflow/api/utils.py b/src/backend/base/langflow/api/utils.py index 0500917a3..b9f10d2c4 100644 --- a/src/backend/base/langflow/api/utils.py +++ b/src/backend/base/langflow/api/utils.py @@ -201,7 +201,7 @@ def format_elapsed_time(elapsed_time: float) -> str: return f"{minutes} {minutes_unit}, {seconds} {seconds_unit}" -async def build_and_cache_graph( +async def build_and_cache_graph_from_db( flow_id: str, session: Session, chat_service: "ChatService", @@ -220,6 +220,17 @@ async def build_and_cache_graph( return graph +async def build_and_cache_graph_from_data( + flow_id: str, + chat_service: "ChatService", + graph_data: dict, +): # -> Graph | Any: + """Build and cache the graph.""" + graph = Graph.from_payload(graph_data, flow_id) + await chat_service.set_cache(flow_id, graph) + return graph + + def format_syntax_error_message(exc: SyntaxError) -> str: """Format a SyntaxError message for returning to the frontend.""" if exc.text is None: diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 9ee4edb03..6a2be44ef 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -8,13 +8,15 @@ from fastapi.responses import StreamingResponse from loguru import logger from langflow.api.utils import ( - build_and_cache_graph, + build_and_cache_graph_from_data, + build_and_cache_graph_from_db, format_elapsed_time, format_exception_message, get_top_level_vertices, parse_exception, ) from langflow.api.v1.schemas import ( + FlowDataRequest, InputValueRequest, ResultDataResponse, StreamData, @@ -52,6 +54,7 @@ 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, + data: Optional[FlowDataRequest] = None, stop_component_id: Optional[str] = None, start_component_id: Optional[str] = None, chat_service: "ChatService" = Depends(get_chat_service), @@ -76,9 +79,14 @@ async def get_vertices( try: # First, we need to check if the flow_id is in the cache graph = None - if cache := await chat_service.get_cache(flow_id): - graph = cache.get("result") - graph = await build_and_cache_graph(flow_id, session, chat_service, graph) + if not data: + if cache := await chat_service.get_cache(flow_id): + graph = cache.get("result") + graph = await build_and_cache_graph_from_db( + flow_id=flow_id, session=session, chat_service=chat_service, graph=graph + ) + else: + graph = await build_and_cache_graph_from_data(flow_id=flow_id, data=data, chat_service=chat_service) if stop_component_id or start_component_id: try: first_layer = graph.sort_vertices(stop_component_id, start_component_id) @@ -144,7 +152,9 @@ async def build_vertex( if not cache: # If there's no cache logger.warning(f"No cache found for {flow_id}. Building graph starting at {vertex_id}") - graph = await build_and_cache_graph(flow_id=flow_id, session=next(get_session()), chat_service=chat_service) + graph = await build_and_cache_graph_from_db( + flow_id=flow_id, session=next(get_session()), chat_service=chat_service + ) else: graph = cache.get("result") result_data_response = ResultDataResponse(results={}) diff --git a/src/backend/base/langflow/api/v1/schemas.py b/src/backend/base/langflow/api/v1/schemas.py index f5d0b97d1..f47637280 100644 --- a/src/backend/base/langflow/api/v1/schemas.py +++ b/src/backend/base/langflow/api/v1/schemas.py @@ -294,3 +294,15 @@ class SimplifiedAPIRequest(BaseModel): ) tweaks: Optional[Tweaks] = Field(default=None, description="The tweaks") session_id: Optional[str] = Field(default=None, description="The session id") + + +# (alias) type ReactFlowJsonObject = { +# nodes: Node[]; +# edges: Edge[]; +# viewport: Viewport; +# } +# import ReactFlowJsonObject +class FlowDataRequest(BaseModel): + nodes: List[dict] + edges: List[dict] + viewport: Optional[dict] = None From 55e56e19f5204ebe8194754eb45b5e0157b8b56c Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 24 Apr 2024 11:12:54 -0300 Subject: [PATCH 05/78] merge from origin/dev --- src/frontend/src/types/components/index.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 4fb2ca1b7..87495b10b 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -80,14 +80,6 @@ export type InputGlobalComponentType = { editNode?: boolean; }; -export type InputGlobalComponentType = { - disabled: boolean; - onChange: (value: string) => void; - setDb: (value: boolean) => void; - name: string; - data: NodeDataType; - editNode?: boolean; -}; export type KeyPairListComponentType = { value: any; @@ -601,6 +593,7 @@ export type IOModalPropsType = { open: boolean; setOpen: (open: boolean) => void; disable?: boolean; + isPlayground?: boolean; }; export type buttonBoxPropsType = { From ed10322af2171832c6f334ca299905ee8d594c5d Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Wed, 24 Apr 2024 17:50:02 -0300 Subject: [PATCH 06/78] Update setNodes and setEdges functions in flowStore.ts to skip saving changes when skipSave parameter is true --- src/frontend/src/components/cardComponent/index.tsx | 5 +++-- src/frontend/src/modals/IOModal/index.tsx | 10 +++++----- src/frontend/src/stores/flowStore.ts | 8 ++++---- src/frontend/src/types/zustand/flow/index.ts | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 21701f459..20af31351 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -63,8 +63,8 @@ export default function CollectionCardComponent({ useEffect(() => { if (currentFlowId && playground) { - setNodes(currentFlow?.data?.nodes ?? []); - setEdges(currentFlow?.data?.edges ?? []); + setNodes(currentFlow?.data?.nodes ?? [],true); + setEdges(currentFlow?.data?.edges ?? [],true); } }, [currentFlowId]); @@ -364,6 +364,7 @@ export default function CollectionCardComponent({ {button && button} {playground && diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 920335a97..9dc0b0384 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -1,5 +1,5 @@ -import { AxiosResponse } from "axios"; -import { ReactFlowJsonObject } from "reactflow"; +import { AxiosRequestConfig, AxiosResponse } from "axios"; +import { Edge, ReactFlowJsonObject,Node } from "reactflow"; import { BASE_URL_API } from "../../constants/constants"; import { api } from "../../controllers/API/api"; import { @@ -906,16 +906,21 @@ export async function updateGlobalVariable( export async function getVerticesOrder( flowId: string, startNodeId?: string | null, - stopNodeId?: string | null + stopNodeId?: string | null, + nodes?:Node[], + Edges?:Edge[] ): Promise> { // nodeId is optional and is a query parameter // if nodeId is not provided, the API will return all vertices - const config = {}; + const config:AxiosRequestConfig = {}; if (stopNodeId) { config["params"] = { stop_component_id: stopNodeId }; } else if (startNodeId) { config["params"] = { start_component_id: startNodeId }; } + if(nodes && Edges){ + config.data = {nodes,Edges} + } return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config); } diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 857f1ed25..86c9d6591 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -31,8 +31,10 @@ export default function IOModal({ open, setOpen, disable, + cleanOnClose=false, }: IOModalPropsType): JSX.Element { const allNodes = useFlowStore((state) => state.nodes); + const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool); const inputs = useFlowStore((state) => state.inputs).filter( (input) => input.type !== "ChatInput" ); @@ -127,6 +129,13 @@ export default function IOModal({ open={open} setOpen={setOpen} disable={disable} + onChangeOpenModal={(open)=>{ + if(!open && cleanOnClose){ + console.log("cleaning flow pool") + cleanFlowPool(); + } + + }} > {children} {/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */} diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index 8fb792702..b69015395 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -6,18 +6,25 @@ import { useDarkStore } from "../../stores/darkStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import Page from "./components/PageComponent"; import ExtraSidebar from "./components/extraSidebarComponent"; +import useFlowStore from "../../stores/flowStore"; export default function FlowPage({ view }: { view?: boolean }): JSX.Element { const setCurrentFlowId = useFlowsManagerStore( (state) => state.setCurrentFlowId ); const version = useDarkStore((state) => state.version); + const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const { id } = useParams(); // Set flow tab id useEffect(() => { setCurrentFlowId(id!); + setOnFlowPage(true); + + return () => { + setOnFlowPage(false); + }; }, [id]); return ( <> diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index 17e32f090..736c70d5b 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -44,9 +44,12 @@ import { getInputsAndOutputs } from "../utils/storeUtils"; import useAlertStore from "./alertStore"; import { useDarkStore } from "./darkStore"; import useFlowsManagerStore from "./flowsManagerStore"; +import FlowPage from "../pages/FlowPage"; // this is our useStore hook that we can use in our components to get parts of the store and call actions const useFlowStore = create((set, get) => ({ + onFlowPage: false, + setOnFlowPage:(FlowPage=>set({onFlowPage:FlowPage})), flowState: undefined, flowBuildStatus: {}, nodes: [], @@ -164,7 +167,7 @@ const useFlowStore = create((set, get) => ({ }); const flowsManager = useFlowsManagerStore.getState(); - if (!get().isBuilding && !skipSave) { + if (!get().isBuilding && !skipSave && get().onFlowPage) { flowsManager.autoSaveCurrentFlow( newChange, newEdges, @@ -180,7 +183,7 @@ const useFlowStore = create((set, get) => ({ }); const flowsManager = useFlowsManagerStore.getState(); - if (!get().isBuilding && !skipSave) { + if (!get().isBuilding && !skipSave && get().onFlowPage) { flowsManager.autoSaveCurrentFlow( get().nodes, newChange, @@ -560,6 +563,8 @@ const useFlowStore = create((set, get) => ({ useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING); }, onValidateNodes: validateSubgraph, + nodes: !get().onFlowPage ? get().nodes : undefined, + edges: !get().onFlowPage ? get().edges : undefined, }); get().setIsBuilding(false); get().revertBuiltStatusFromBuilding(); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 87495b10b..8cf20a80f 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -594,6 +594,7 @@ export type IOModalPropsType = { setOpen: (open: boolean) => void; disable?: boolean; isPlayground?: boolean; + cleanOnClose?: boolean; }; export type buttonBoxPropsType = { diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index dac3cec52..c975eec8d 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -45,6 +45,8 @@ export type FlowPoolType = { }; export type FlowStoreType = { + onFlowPage: boolean; + setOnFlowPage: (onFlowPage: boolean) => void; flowPool: FlowPoolType; inputs: Array<{ type: string; id: string; displayName: string }>; outputs: Array<{ type: string; id: string; displayName: string }>; diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index bc2cc62e7..ac648acb1 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -5,6 +5,7 @@ import useAlertStore from "../stores/alertStore"; import useFlowStore from "../stores/flowStore"; import { VertexBuildTypeAPI } from "../types/api"; import { VertexLayerElementType } from "../types/zustand/flow"; +import { Edge, Node } from "reactflow"; type BuildVerticesParams = { flowId: string; // Assuming FlowType is the type for your flow @@ -21,6 +22,8 @@ type BuildVerticesParams = { onBuildError?: (title, list, idList: VertexLayerElementType[]) => void; onBuildStart?: (idList: VertexLayerElementType[]) => void; onValidateNodes?: (nodes: string[]) => void; + nodes?: Node[]; + edges?: Edge[]; }; function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { @@ -48,7 +51,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { export async function updateVerticesOrder( flowId: string, startNodeId?: string | null, - stopNodeId?: string | null + stopNodeId?: string | null, + nodes?:Node[], + edges?:Edge[] ): Promise<{ verticesLayers: VertexLayerElementType[][]; verticesIds: string[]; @@ -59,7 +64,7 @@ export async function updateVerticesOrder( const setErrorData = useAlertStore.getState().setErrorData; let orderResponse; try { - orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId); + orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId, nodes, edges); } catch (error: any) { setErrorData({ title: "Oops! Looks like you missed something", @@ -101,6 +106,8 @@ export async function buildVertices({ onBuildError, onBuildStart, onValidateNodes, + nodes, + edges, }: BuildVerticesParams) { let verticesBuild = useFlowStore.getState().verticesBuild; // if startNodeId and stopNodeId are provided @@ -113,7 +120,9 @@ export async function buildVertices({ let verticesOrderResponse = await updateVerticesOrder( flowId, startNodeId, - stopNodeId + stopNodeId, + nodes, + edges ); if (onValidateNodes) { try { From e22a3300cb5e9b8bd647813172b7ccc1630d6af2 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 25 Apr 2024 13:03:53 -0300 Subject: [PATCH 10/78] Refactor get_vertices function to retrieve_vertices_order in chat.py --- src/backend/base/langflow/api/v1/chat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 6a2be44ef..e839df1c6 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -51,8 +51,8 @@ async def try_running_celery_task(vertex, user_id): return vertex -@router.get("/build/{flow_id}/vertices", response_model=VerticesOrderResponse) -async def get_vertices( +@router.post("/build/{flow_id}/vertices", response_model=VerticesOrderResponse) +async def retrieve_vertices_order( flow_id: str, data: Optional[FlowDataRequest] = None, stop_component_id: Optional[str] = None, @@ -65,6 +65,7 @@ async def get_vertices( Args: flow_id (str): The ID of the flow. + data (Optional[FlowDataRequest], optional): The flow data. Defaults to None. stop_component_id (str, optional): The ID of the stop component. Defaults to None. start_component_id (str, optional): The ID of the start component. Defaults to None. chat_service (ChatService, optional): The chat service dependency. Defaults to Depends(get_chat_service). From dc71ad261b879ac83fe924d22ad35a51a164a415 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 25 Apr 2024 15:33:34 -0300 Subject: [PATCH 11/78] Fix cleanFlowPool function not being called in CollectionCardComponent --- src/frontend/src/components/cardComponent/index.tsx | 2 ++ src/frontend/src/controllers/API/index.ts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 261cd009e..7c71c110e 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -40,6 +40,7 @@ export default function CollectionCardComponent({ const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const setValidApiKey = useStoreStore((state) => state.updateValidApiKey); + const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool); const isStore = false; const [loading, setLoading] = useState(false); const [loadingLike, setLoadingLike] = useState(false); @@ -64,6 +65,7 @@ export default function CollectionCardComponent({ if (currentFlowId && playground) { setNodes(currentFlow?.data?.nodes ?? [],true); setEdges(currentFlow?.data?.edges ?? [],true); + cleanFlowPool(); } }, [currentFlowId]); diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 9dc0b0384..be9572398 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -918,10 +918,12 @@ export async function getVerticesOrder( } else if (startNodeId) { config["params"] = { start_component_id: startNodeId }; } + const data = {} if(nodes && Edges){ - config.data = {nodes,Edges} + data["nodes"] = nodes + data["edges"] = Edges } - return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config); + return await api.post(`${BASE_URL_API}build/${flowId}/vertices`,data, config); } export async function postBuildVertex( From 4317ab35b75b6116b38be0408647307341ef6809 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 25 Apr 2024 16:06:14 -0300 Subject: [PATCH 12/78] Refactor build_and_cache_graph_from_data function in chat.py to use model_dump() method for graph data --- src/backend/base/langflow/api/v1/chat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index e839df1c6..5d8e4ace3 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -87,7 +87,9 @@ async def retrieve_vertices_order( flow_id=flow_id, session=session, chat_service=chat_service, graph=graph ) else: - graph = await build_and_cache_graph_from_data(flow_id=flow_id, data=data, chat_service=chat_service) + graph = await build_and_cache_graph_from_data( + flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service + ) if stop_component_id or start_component_id: try: first_layer = graph.sort_vertices(stop_component_id, start_component_id) From 089446b1b76db6d9832086c7a9dbaefd629398ad Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 25 Apr 2024 16:28:03 -0300 Subject: [PATCH 13/78] initial implementation of playground on store, not working yet --- .../src/components/cardComponent/index.tsx | 15 +++++++++++++-- src/frontend/src/pages/StorePage/index.tsx | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 7c71c110e..9e5db5433 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -63,8 +63,19 @@ export default function CollectionCardComponent({ useEffect(() => { if (currentFlowId && playground) { - setNodes(currentFlow?.data?.nodes ?? [],true); - setEdges(currentFlow?.data?.edges ?? [],true); + console.log(currentFlowId) + if(currentFlow){ + setNodes(currentFlow?.data?.nodes ?? [],true); + setEdges(currentFlow?.data?.edges ?? [],true); + } + else{ + getComponent(data.id) + .then((res) => { + const newFlow = cloneFLowWithParent(res, res.id, data.is_component); + setNodes(newFlow?.data?.nodes??[]); + setEdges(newFlow?.data?.edges??[]); + }); + } cleanFlowPool(); } }, [currentFlowId]); diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index 64df241fb..4bc482f56 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -371,6 +371,7 @@ export default function StorePage(): JSX.Element { data={item} authorized={validApiKey} disabled={loading} + playground={item.last_tested_version?.includes("1.0.0")&& !item.is_component} /> ); From b8a62a3c7bd771c2f9ffe1984979a1e19abd6e8f Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 25 Apr 2024 18:37:23 -0300 Subject: [PATCH 14/78] running flows from store, still need fixes --- .../src/components/cardComponent/index.tsx | 32 +++++++++++-------- .../MainPage/components/components/index.tsx | 2 +- src/frontend/src/stores/flowsManagerStore.ts | 10 ++++++ .../src/types/zustand/flowsManager/index.ts | 2 ++ src/frontend/src/utils/storeUtils.ts | 7 ++-- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 9e5db5433..04f009d22 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -52,6 +52,8 @@ export default function CollectionCardComponent({ data?.downloads_count ?? 0 ); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); + const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); + const getFlowById = useFlowsManagerStore((state) => state.getFlowById); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const setNodes = useFlowStore((state) => state.setNodes); const setEdges = useFlowStore((state) => state.setEdges); @@ -61,21 +63,16 @@ export default function CollectionCardComponent({ const name = data.is_component ? "Component" : "Flow"; + async function getFlowData(){ + const res = await getComponent(data.id) + const newFlow = cloneFLowWithParent(res, res.id, data.is_component,true); + return newFlow; + } + useEffect(() => { if (currentFlowId && playground) { - console.log(currentFlowId) - if(currentFlow){ setNodes(currentFlow?.data?.nodes ?? [],true); setEdges(currentFlow?.data?.edges ?? [],true); - } - else{ - getComponent(data.id) - .then((res) => { - const newFlow = cloneFLowWithParent(res, res.id, data.is_component); - setNodes(newFlow?.data?.nodes??[]); - setEdges(newFlow?.data?.edges??[]); - }); - } cleanFlowPool(); } }, [currentFlowId]); @@ -385,8 +382,17 @@ export default function CollectionCardComponent({ "playground-flow-button-" + data.id } onClick={() => { - setCurrentFlowId(data.id); - setOpenPlayground(true); + if(getFlowById(data.id)){ + setCurrentFlowId(data.id); + setOpenPlayground(true); + } + else{ + getFlowData().then((res) => { + setCurrentFlow(res); + setOpenPlayground(true); + }); + + } }} > ([]); diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index 9471889c3..62e88f99c 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -46,6 +46,16 @@ const useFlowsManagerStore = create((set, get) => ({ set({ examples }); }, currentFlowId: "", + setCurrentFlow: (flow: FlowType) => { + set((state) => ({ + currentFlow: flow, + currentFlowId: flow.id, + })); + + }, + getFlowById: (id: string) => { + return get().flows.find((flow) => flow.id === id); + }, setCurrentFlowId: (currentFlowId: string) => { set((state) => ({ currentFlowId, diff --git a/src/frontend/src/types/zustand/flowsManager/index.ts b/src/frontend/src/types/zustand/flowsManager/index.ts index 817ddd3d1..16985105a 100644 --- a/src/frontend/src/types/zustand/flowsManager/index.ts +++ b/src/frontend/src/types/zustand/flowsManager/index.ts @@ -2,6 +2,7 @@ import { Edge, Node, Viewport, XYPosition } from "reactflow"; import { FlowType } from "../../flow"; export type FlowsManagerStoreType = { + getFlowById: (id: string) => FlowType | undefined; flows: Array; setFlows: (flows: FlowType[]) => void; currentFlow: FlowType | undefined; @@ -50,6 +51,7 @@ export type FlowsManagerStoreType = { takeSnapshot: () => void; examples: Array; setExamples: (examples: FlowType[]) => void; + setCurrentFlow: (flow: FlowType) => void; }; export type UseUndoRedoOptions = { diff --git a/src/frontend/src/utils/storeUtils.ts b/src/frontend/src/utils/storeUtils.ts index e51cf8933..78b7ee7ff 100644 --- a/src/frontend/src/utils/storeUtils.ts +++ b/src/frontend/src/utils/storeUtils.ts @@ -6,11 +6,14 @@ import { isInputNode, isOutputNode } from "./reactflowUtils"; export default function cloneFLowWithParent( flow: FlowType, parent: string, - is_component: boolean + is_component: boolean, + keepId=false ) { let childFLow = cloneDeep(flow); childFLow.parent = parent; - childFLow.id = ""; + if(!keepId){ + childFLow.id = ""; + } childFLow.is_component = is_component; return childFLow; } From 2474cd348ef3a7dc5ef0dc17f27c52dfb9776e85 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 26 Apr 2024 13:40:54 -0300 Subject: [PATCH 15/78] Refactor CollectionCardComponent to improve code readability and fix loadingPlayground state handling --- .../src/components/cardComponent/index.tsx | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 04f009d22..f342960ab 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -20,6 +20,10 @@ import { } from "../ui/card"; import IOModal from "../../modals/IOModal"; import useFlowStore from "../../stores/flowStore"; +import { set } from "lodash"; +import LoadingComponent from "../loadingComponent"; +import { f } from "million/dist/shared/million.9d4df3c1"; +import Loading from "../ui/loading"; export default function CollectionCardComponent({ data, @@ -59,20 +63,21 @@ export default function CollectionCardComponent({ const setEdges = useFlowStore((state) => state.setEdges); const [openPlayground, setOpenPlayground] = useState(false); const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId); + const [loadingPlayground, setLoadingPlayground] = useState(false); const name = data.is_component ? "Component" : "Flow"; - async function getFlowData(){ + async function getFlowData() { const res = await getComponent(data.id) - const newFlow = cloneFLowWithParent(res, res.id, data.is_component,true); + const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true); return newFlow; } useEffect(() => { if (currentFlowId && playground) { - setNodes(currentFlow?.data?.nodes ?? [],true); - setEdges(currentFlow?.data?.edges ?? [],true); + setNodes(currentFlow?.data?.nodes ?? [], true); + setEdges(currentFlow?.data?.edges ?? [], true); cleanFlowPool(); } }, [currentFlowId]); @@ -373,6 +378,7 @@ export default function CollectionCardComponent({ {button && button} {playground && - } + )} - + ); } diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index 78c344714..3666c61a6 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -267,7 +267,7 @@ export default function CodeTabsComponent({
{node.data.node.template[ templateField - ].list ? ( + ]?.list ? (
diff --git a/src/frontend/src/components/keypairListComponent/index.tsx b/src/frontend/src/components/keypairListComponent/index.tsx index fcd35e01e..2fa12e3b8 100644 --- a/src/frontend/src/components/keypairListComponent/index.tsx +++ b/src/frontend/src/components/keypairListComponent/index.tsx @@ -20,7 +20,13 @@ export default function KeypairListComponent({ } }, [disabled]); - const ref = useRef(value.length === 0 ? [{ "": "" }] : value); + const checkValueType = (value) => { + return Array.isArray(value) ? value : [value]; + }; + + const ref = useRef([]); + ref.current = + !value || value?.length === 0 ? [{ "": "" }] : checkValueType(value); useEffect(() => { if (JSON.stringify(value) !== JSON.stringify(ref.current)) { diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 49483875c..f8d45da02 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -711,13 +711,19 @@ export const LANGFLOW_SUPPORTED_TYPES = new Set([ export const priorityFields = new Set(["code", "template"]); -export const INPUT_TYPES = new Set(["ChatInput", "TextInput", "KeyPairInput"]); +export const INPUT_TYPES = new Set([ + "ChatInput", + "TextInput", + "KeyPairInput", + "JsonInput", +]); export const OUTPUT_TYPES = new Set([ "ChatOutput", "TextOutput", "PDFOutput", "ImageOutput", "CSVOutput", + "JsonOutput", ]); export const CHAT_FIRST_INITIAL_TEXT = diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx index 17da8c73b..87c72d1df 100644 --- a/src/frontend/src/modals/EditNodeModal/index.tsx +++ b/src/frontend/src/modals/EditNodeModal/index.tsx @@ -203,7 +203,7 @@ const EditNodeModal = forwardRef( !myData.node.template[templateParam].options ? (
{myData.node.template[templateParam] - .list ? ( + ?.list ? (
@@ -420,6 +420,10 @@ const EditNodeModal = forwardRef( .type === "int" ? (
{ + if (value) onChange(value); + }, [value]); + const isDark = useDarkStore((state) => state.dark); + + const ref = useRef(null); + ref.current = value; + + const getClassNames = () => { + if (!isDark && !left) return "json-view-playground-white"; + if (!isDark && left) return "json-view-playground-white-left"; + if (isDark && left) return "json-view-playground-dark-left"; + if (isDark && !left) return "json-view-playground-dark"; + }; + + return ( +
+ { + ref.current = edit["src"]; + }} + onChange={(edit) => { + ref.current = edit["src"]; + }} + src={ref.current} + /> +
+ ); +} diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx new file mode 100644 index 000000000..b0b557c55 --- /dev/null +++ b/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx @@ -0,0 +1,91 @@ +import _ from "lodash"; +import { useRef } from "react"; +import IconComponent from "../../../../../../components/genericIconComponent"; +import { Input } from "../../../../../../components/ui/input"; +import { classNames } from "../../../../../../utils/utils"; + +const IOKeyPairInput = ({ value, onChange, duplicateKey, isList = true }) => { + const checkValueType = (value) => { + return Array.isArray(value) ? value : [value]; + }; + + const ref = useRef([]); + ref.current = + !value || value?.length === 0 ? [{ "": "" }] : checkValueType(value); + + const handleChangeKey = (event, idx) => { + const oldKey = Object.keys(ref.current[idx])[0]; + const updatedObj = { [event.target.value]: ref.current[idx][oldKey] }; + ref.current[idx] = updatedObj; + onChange(ref.current); + }; + + const handleChangeValue = (newValue, idx) => { + const key = Object.keys(ref.current[idx])[0]; + ref.current[idx][key] = newValue; + onChange(ref.current); + }; + + return ( + <> +
+ {ref.current?.map((obj, index) => { + return Object.keys(obj).map((key, idx) => { + return ( +
+ handleChangeKey(event, index)} + /> + + + handleChangeValue(event.target.value, index) + } + /> + + {isList && index === ref.current.length - 1 ? ( + + ) : isList ? ( + + ) : ( + "" + )} +
+ ); + }); + })} +
+ + ); +}; + +export default IOKeyPairInput; diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx index dd02d19ca..468b090fe 100644 --- a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx @@ -1,4 +1,5 @@ import { cloneDeep } from "lodash"; +import { useState } from "react"; import ImageViewer from "../../../../components/ImageViewer"; import CsvOutputComponent from "../../../../components/csvOutputComponent"; import PdfViewer from "../../../../components/pdfViewer"; @@ -15,7 +16,13 @@ import { PDFViewConstant } from "../../../../constants/constants"; import { InputOutput } from "../../../../constants/enums"; import useFlowStore from "../../../../stores/flowStore"; import { IOFieldViewProps } from "../../../../types/components"; +import { + convertValuesToNumbers, + hasDuplicateKeys, +} from "../../../../utils/reactflowUtils"; import IOFileInput from "./components/FileInput"; +import IoJsonInput from "./components/JSONInput"; +import IOKeyPairInput from "./components/keyPairInput"; export default function IOFieldView({ type, @@ -39,6 +46,7 @@ export default function IOFieldView({ } } }; + const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); function handleOutputType() { if (!node) return <>"No node found!"; @@ -78,6 +86,39 @@ export default function IOFieldView({ /> ); + case "KeyPairInput": + return ( + { + if (node) { + let newNode = cloneDeep(node); + newNode.data.node!.template["input_value"].value = e; + setNode(node.id, newNode); + } + const valueToNumbers = convertValuesToNumbers(e); + setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers)); + }} + duplicateKey={errorDuplicateKey} + isList={node.data.node!.template["input_value"]?.list ?? false} + /> + ); + + case "JsonInput": + return ( + { + if (node) { + let newNode = cloneDeep(node); + newNode.data.node!.template["input_value"].value = e; + setNode(node.id, newNode); + } + }} + left={left} + /> + ); + default: return (