Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions src/frontend/src/modals/IOModal/playground-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//import LangflowLogoColor from "@/assets/LangflowLogocolor.svg?react";

import { useCallback, useEffect, useRef, useState } from "react";
import { v4 as uuid } from "uuid";
import { useShallow } from "zustand/react/shallow";
import { markOptimisticMessageFailed } from "@/utils/optimisticMessageUtils";
import ThemeButtons from "@/components/core/appHeaderComponent/components/ThemeButtons";
import { useGetMessagesQuery } from "@/controllers/API/queries/messages";
import { useDeleteSession } from "@/controllers/API/queries/messages/use-delete-sessions";
Expand Down Expand Up @@ -43,7 +45,6 @@ export default function IOModal({
const outputs = useFlowStore((state) => state.outputs);
const nodes = useFlowStore((state) => state.nodes);
const buildFlow = useFlowStore((state) => state.buildFlow);
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
const isBuilding = useFlowStore((state) => state.isBuilding);
const newChatOnPlayground = useFlowStore(
(state) => state.newChatOnPlayground,
Expand Down Expand Up @@ -75,6 +76,7 @@ export default function IOModal({
const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const deleteSession = useMessagesStore((state) => state.deleteSession);
const addMessage = useMessagesStore((state) => state.addMessage);
const currentFlowId = useGetFlowId();
const [sidebarOpen, setSidebarOpen] = useState(true);

Expand Down Expand Up @@ -209,22 +211,58 @@ export default function IOModal({
files?: string[];
}): Promise<void> => {
if (isBuilding) return;
const optimisticMessage =
playgroundPage && (chatValue || (files && files.length > 0))
? {
id: `optimistic-${uuid()}`,
text: chatValue,
sender: "User",
sender_name: "User",
session_id: sessionId,
timestamp: new Date().toISOString(),
files: files ?? [],
edit: false,
background_color: "",
text_color: "",
flow_id: currentFlowId,
properties: { optimistic: true },
}
: null;

if (optimisticMessage) {
addMessage(optimisticMessage);
}
setChatValue("");
for (let i = 0; i < repeat; i++) {
await buildFlow({
input_value: chatValue,
startNodeId: chatInput?.id,
files: files,
silent: true,
session: sessionId,
eventDelivery: eventDeliveryConfig,
}).catch((err) => {
console.error(err);
throw err;
});
try {
for (let i = 0; i < repeat; i++) {
await buildFlow({
input_value: chatValue,
startNodeId: chatInput?.id,
files: files,
silent: true,
session: sessionId,
eventDelivery: eventDeliveryConfig,
});
}
} catch (error) {
if (optimisticMessage) {
addMessage(markOptimisticMessageFailed(optimisticMessage));
}
throw error;
}
},
[isBuilding, setIsBuilding, chatValue, chatInput?.id, sessionId, buildFlow],
[
isBuilding,
addMessage,
chatValue,
chatInput?.id,
sessionId,
buildFlow,
currentFlowId,
eventDeliveryConfig,
playgroundPage,
setChatValue,
],
);

useEffect(() => {
Expand Down
17 changes: 17 additions & 0 deletions src/frontend/src/stores/messagesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ export const useMessagesStore = create<MessagesStoreType>((set, get) => ({
}
return;
}
if (message.sender === "User") {
const messages = get().messages;
for (let i = messages.length - 1; i >= 0; i--) {
const candidate = messages[i];
if (
candidate.sender === "User" &&
(candidate.properties?.optimistic || candidate.properties?.failed) &&
candidate.session_id === message.session_id &&
candidate.text === message.text
) {
const updatedMessages = [...messages];
updatedMessages[i] = message;
set(() => ({ messages: updatedMessages }));
return;
}
}
}
if (message.sender === "Machine") {
set(() => ({ displayLoadingMessage: false }));
}
Expand Down
18 changes: 18 additions & 0 deletions src/frontend/src/utils/optimisticMessageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MessageType } from "@/types/messages";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Import existing message type to avoid build failure

The new utility imports MessageType from @/types/messages, but that module only exports Message (see src/frontend/src/types/messages/index.ts). Because this is a value import (not import type) and the project uses isolatedModules/noEmit, bundlers like Vite/esbuild will treat it as a runtime import and error with “export not found,” and TypeScript will also fail the build. Use the existing Message type (ideally via import type { Message }) or add a real MessageType export.

Useful? React with 👍 / 👎.


/**
* Marks an optimistic user message as failed without removing it from the UI.
* This preserves user intent while allowing global error handling.
*/
export function markOptimisticMessageFailed(
optimisticMessage: MessageType
): MessageType {
return {
...optimisticMessage,
properties: {
...optimisticMessage.properties,
optimistic: false,
failed: true,
},
};
}
Loading