diff --git a/src/backend/langflow/api/validate.py b/src/backend/langflow/api/validate.py index 07f113a8b..d80010be4 100644 --- a/src/backend/langflow/api/validate.py +++ b/src/backend/langflow/api/validate.py @@ -47,7 +47,7 @@ def post_validate_node(node_id: str, data: dict): if node is not None: _ = node.build() return node.params - raise + raise Exception(f"Node {node_id} not found") except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index ff13af901..c96591cc9 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -7,9 +7,10 @@ import { } from "../../utils"; import ParameterComponent from "./components/parameterComponent"; import { typesContext } from "../../contexts/typesContext"; -import { useContext, useRef } from "react"; +import { useContext, useState, useEffect, useRef } from "react"; import { NodeDataType } from "../../types/flow"; import { alertContext } from "../../contexts/alertContext"; +import { useCallback } from 'react'; export default function GenericNode({ data, @@ -22,6 +23,53 @@ export default function GenericNode({ const showError = useRef(true); const { types, deleteNode } = useContext(typesContext); const Icon = nodeIcons[types[data.type]]; + + // State for outline color + const [isGreenOutline, setIsGreenOutline] = useState(false); + const [isRedOutline, setIsRedOutline] = useState(false); + const { reactFlowInstance } = useContext(typesContext); + + const validateNode = useCallback(async () => { + try { + const response = await fetch(`/validate/node/${data.id}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(reactFlowInstance.toObject()), + }); + + if (response.status === 200) { + setIsGreenOutline(true); + setIsRedOutline(false); + } else if (response.status === 500) { + setIsRedOutline(true); + setIsGreenOutline(false); + } + } catch (error) { + console.error('Error validating node:', error); + setIsRedOutline(true); + } + }, [data.id, reactFlowInstance]); + + useEffect(() => { + validateNode(); + }, [ + validateNode, + ...Object.values(data.node.template).flatMap((t) => Object.values(t)), + + ]); + + useEffect(() => { + if (isGreenOutline) { + setTimeout(() => { + setIsGreenOutline(false); + }, 1000); + } + }, [isGreenOutline]); + + const outlineColor = isGreenOutline ? 'animate-pulse-green' : isRedOutline ? 'border-red-outline' : ''; + if (!Icon) { if (showError.current) { setErrorData({ @@ -34,9 +82,11 @@ export default function GenericNode({ deleteNode(data.id); return; } + return (