diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/components/build-status-display.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/components/build-status-display.tsx
index e56b6d79a..78cd03d7e 100644
--- a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/components/build-status-display.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/components/build-status-display.tsx
@@ -3,6 +3,7 @@ import {
STATUS_BUILD,
STATUS_BUILDING,
STATUS_INACTIVE,
+ STATUS_MISSING_FIELDS_ERROR,
} from "@/constants/constants";
import { BuildStatus } from "@/constants/enums";
@@ -58,6 +59,11 @@ const BuildStatusDisplay = ({
return {STATUS_INACTIVE};
}
+ if (buildStatus === BuildStatus.ERROR && !validationStatus) {
+ // If the build status is error and there is no validation status, it means that it failed before building, so show the Missing Required Fields error message
+ return {STATUS_MISSING_FIELDS_ERROR};
+ }
+
if (!validationStatus) {
return {STATUS_BUILD};
}
diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts
index 1b908f88e..c8ecac2d9 100644
--- a/src/frontend/src/constants/constants.ts
+++ b/src/frontend/src/constants/constants.ts
@@ -735,6 +735,8 @@ 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_MISSING_FIELDS_ERROR =
+ "Please fill all the required fields.";
export const STATUS_INACTIVE = "Execution blocked";
export const STATUS_BUILDING = "Building...";
export const SAVED_HOVER = "Last saved: ";
diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/chat-input.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/chat-input.tsx
index e2796cc93..435f231b1 100644
--- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/chat-input.tsx
+++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/chat-input.tsx
@@ -162,11 +162,21 @@ export default function ChatInput({
};
}, [handleFileChange, currentFlowId, isBuilding]);
- const send = () => {
- sendMessage({
- repeat: 1,
- files: files.map((file) => file.path ?? "").filter((file) => file !== ""),
- });
+ const setChatValueStore = useUtilityStore((state) => state.setChatValueStore);
+
+ const send = async () => {
+ const storedChatValue = chatValue;
+ try {
+ await sendMessage({
+ repeat: 1,
+ files: files
+ .map((file) => file.path ?? "")
+ .filter((file) => file !== ""),
+ });
+ } catch (error) {
+ setChatValueStore(storedChatValue);
+ }
+
setFiles([]);
};
diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/text-area-wrapper.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/text-area-wrapper.tsx
index 9060aa0f4..4d9132c5c 100644
--- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/text-area-wrapper.tsx
+++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/components/text-area-wrapper.tsx
@@ -46,6 +46,7 @@ const TextAreaWrapper = ({
data-testid="input-chat-playground"
onKeyDown={(event) => {
if (checkSendingOk(event)) {
+ event.preventDefault();
send();
}
}}
diff --git a/src/frontend/src/modals/IOModal/components/chatView/components/chat-view.tsx b/src/frontend/src/modals/IOModal/components/chatView/components/chat-view.tsx
index 236e14dd0..a014608a8 100644
--- a/src/frontend/src/modals/IOModal/components/chatView/components/chat-view.tsx
+++ b/src/frontend/src/modals/IOModal/components/chatView/components/chat-view.tsx
@@ -294,8 +294,8 @@ export default function ChatView({
{
- sendMessage({ repeat, files });
+ sendMessage={async ({ repeat, files }) => {
+ await sendMessage({ repeat, files });
track("Playground Message Sent");
}}
inputRef={ref}
diff --git a/src/frontend/src/modals/IOModal/playground-modal.tsx b/src/frontend/src/modals/IOModal/playground-modal.tsx
index 6d2b941f5..728b7bc38 100644
--- a/src/frontend/src/modals/IOModal/playground-modal.tsx
+++ b/src/frontend/src/modals/IOModal/playground-modal.tsx
@@ -227,6 +227,7 @@ export default function IOModal({
eventDelivery: eventDeliveryConfig,
}).catch((err) => {
console.error(err);
+ throw err;
});
}
},
diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts
index 839e39bb9..2b4588db4 100644
--- a/src/frontend/src/stores/flowStore.ts
+++ b/src/frontend/src/stores/flowStore.ts
@@ -39,6 +39,7 @@ import {
checkChatInput,
cleanEdges,
detectBrokenEdgesEdges,
+ getConnectedSubgraph,
getHandleId,
getNodeId,
scapeJSONParse,
@@ -658,52 +659,56 @@ const useFlowStore = create((set, get) => ({
get().setIsBuilding(true);
set({ flowBuildStatus: {} });
const currentFlow = useFlowsManagerStore.getState().currentFlow;
- const setSuccessData = useAlertStore.getState().setSuccessData;
const setErrorData = useAlertStore.getState().setErrorData;
const edges = get().edges;
- let error = false;
let errors: string[] = [];
- for (const edge of edges) {
- const errorsEdge = validateEdge(edge, get().nodes, edges);
+
+ // Only validate upstream nodes/edges if startNodeId is provided
+ let nodesToValidate = get().nodes;
+ let edgesToValidate = edges;
+ if (startNodeId) {
+ const downstream = getConnectedSubgraph(
+ startNodeId,
+ get().nodes,
+ edges,
+ "downstream",
+ );
+ nodesToValidate = downstream.nodes;
+ edgesToValidate = downstream.edges;
+ } else if (stopNodeId) {
+ const upstream = getConnectedSubgraph(
+ stopNodeId,
+ get().nodes,
+ edges,
+ "upstream",
+ );
+ nodesToValidate = upstream.nodes;
+ edgesToValidate = upstream.edges;
+ }
+
+ for (const edge of edgesToValidate) {
+ const errorsEdge = validateEdge(edge, nodesToValidate, edgesToValidate);
if (errorsEdge.length > 0) {
- error = true;
errors.push(errorsEdge.join("\n"));
- useAlertStore.getState().addNotificationToHistory({
- title: MISSED_ERROR_ALERT,
- type: "error",
- list: errorsEdge,
- });
}
}
- if (error) {
+ const errorsObjs = validateNodes(nodesToValidate, edges);
+
+ errors = errors.concat(errorsObjs.map((obj) => obj.errors).flat());
+ if (errors.length > 0) {
+ setErrorData({
+ title: MISSED_ERROR_ALERT,
+ list: errors,
+ });
+ const ids = errorsObjs.map((obj) => obj.id).flat();
+ get().updateBuildStatus(ids, BuildStatus.ERROR); // Set only the build status as error without adding info to the flow pool
+
get().setIsBuilding(false);
- get().setBuildInfo({ error: errors, success: false });
throw new Error("Invalid components");
}
- function validateSubgraph(nodes: string[]) {
- const errorsObjs = validateNodes(
- get().nodes.filter((node) => nodes.includes(node.id)),
- get().edges,
- );
-
- const errors = errorsObjs.map((obj) => obj.errors).flat();
- if (errors.length > 0) {
- get().setBuildInfo({ error: errors, success: false });
- useAlertStore.getState().addNotificationToHistory({
- title: MISSED_ERROR_ALERT,
- type: "error",
- list: errors,
- });
- get().setIsBuilding(false);
- const ids = errorsObjs.map((obj) => obj.id).flat();
-
- get().updateBuildStatus(ids, BuildStatus.ERROR);
- throw new Error("Invalid components");
- }
- // get().updateEdgesRunningByNodes(nodes, true);
- }
+ function validateSubgraph() {}
function handleBuildUpdate(
vertexBuildData: VertexBuildTypeAPI,
status: BuildStatus,
diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts
index 87716e3d6..8c3860ada 100644
--- a/src/frontend/src/types/components/index.ts
+++ b/src/frontend/src/types/components/index.ts
@@ -551,7 +551,7 @@ export type ChatInputType = {
}: {
repeat: number;
files?: string[];
- }) => void;
+ }) => Promise;
playgroundPage: boolean;
};
@@ -840,7 +840,7 @@ export type chatViewProps = {
}: {
repeat: number;
files?: string[];
- }) => void;
+ }) => Promise;
visibleSession?: string;
focusChat?: string;
closeChat?: () => void;
diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts
index 51304b1bd..2eb1a1880 100644
--- a/src/frontend/src/utils/buildUtils.ts
+++ b/src/frontend/src/utils/buildUtils.ts
@@ -1,6 +1,5 @@
import { MISSED_ERROR_ALERT } from "@/constants/alerts_constants";
import {
- BASE_URL_API,
BUILD_POLLING_INTERVAL,
POLLING_MESSAGES,
} from "@/constants/constants";
diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts
index 218902df9..0c0a2cddb 100644
--- a/src/frontend/src/utils/reactflowUtils.ts
+++ b/src/frontend/src/utils/reactflowUtils.ts
@@ -2094,3 +2094,44 @@ export function buildPositionDictionary(nodes: AllNodeType[]) {
export function hasStreaming(nodes: AllNodeType[]) {
return nodes.some((node) => node.data.node?.template?.stream?.value);
}
+
+// Utility to get all connected nodes and edges from a given nodeId, in a given direction
+export function getConnectedSubgraph(
+ nodeId: string,
+ nodes: AllNodeType[],
+ edges: EdgeType[],
+ direction: "upstream" | "downstream",
+): { nodes: AllNodeType[]; edges: EdgeType[] } {
+ const visited = new Set();
+ const resultNodes: AllNodeType[] = [];
+ const resultEdges: EdgeType[] = [];
+
+ function dfs(currentId: string) {
+ if (visited.has(currentId)) return;
+ visited.add(currentId);
+ const node = nodes.find((n) => n.id === currentId);
+ if (node) {
+ resultNodes.push(node);
+ if (direction === "upstream") {
+ // Find all incoming edges
+ const incomingEdges = edges.filter((e) => e.target === currentId);
+ for (const edge of incomingEdges) {
+ resultEdges.push(edge);
+ dfs(edge.source);
+ }
+ } else {
+ // downstream: Find all outgoing edges
+ const outgoingEdges = edges.filter((e) => e.source === currentId);
+ for (const edge of outgoingEdges) {
+ resultEdges.push(edge);
+ dfs(edge.target);
+ }
+ }
+ }
+ }
+ dfs(nodeId);
+ return {
+ nodes: resultNodes,
+ edges: resultEdges,
+ };
+}