fixed duplicated message

This commit is contained in:
anovazzi1 2023-05-09 18:02:43 -03:00
commit 4ca064ebdc
3 changed files with 374 additions and 358 deletions

View file

@ -0,0 +1,8 @@
export default function codeBlock() {
// a html custom element for code blocks in markdown texts, it will allow to download the code as a file or copy it to the clipboard
return (
<div>
<h1>Code Block</h1>
</div>
)
}

View file

@ -65,6 +65,14 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
remarkPlugins={[remarkGfm, remarkMath]} remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]} rehypePlugins={[rehypeMathjax]}
className="markdown prose" className="markdown prose"
// components={{
// code: (props) => (
// <code
// className="bg-pink-600 dark:bg-gray-700 rounded-md p-1"
// {...props}
// />
// ),
// }}
> >
{message} {message}
</ReactMarkdown> </ReactMarkdown>

View file

@ -14,383 +14,383 @@ import ChatInput from "./chatInput";
const _ = require("lodash"); const _ = require("lodash");
export default function ChatModal({ export default function ChatModal({
flow, flow,
open, open,
setOpen, setOpen,
}: { }: {
open: boolean; open: boolean;
setOpen: Function; setOpen: Function;
flow: FlowType; flow: FlowType;
}) { }) {
const [chatValue, setChatValue] = useState(""); const [chatValue, setChatValue] = useState("");
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]); const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const { setErrorData, setNoticeData } = useContext(alertContext); const { setErrorData, setNoticeData } = useContext(alertContext);
const ws = useRef<WebSocket | null>(null); const ws = useRef<WebSocket | null>(null);
const [lockChat, setLockChat] = useState(false); const [lockChat, setLockChat] = useState(false);
const isOpen = useRef(open); const isOpen = useRef(open);
useEffect(() => { useEffect(() => {
isOpen.current = open; isOpen.current = open;
}, [open]); }, [open]);
var isStream = false; var isStream = false;
const addChatHistory = ( const addChatHistory = (
message: string, message: string,
isSend: boolean, isSend: boolean,
thought?: string, thought?: string,
files?: Array<any> files?: Array<any>
) => { ) => {
setChatHistory((old) => { setChatHistory((old) => {
let newChat = _.cloneDeep(old); let newChat = _.cloneDeep(old);
if (files) { if (files) {
newChat.push({ message, isSend, files, thought }); newChat.push({ message, isSend, files, thought });
} else if (thought) { } else if (thought) {
newChat.push({ message, isSend, thought }); newChat.push({ message, isSend, thought });
} else { } else {
newChat.push({ message, isSend }); newChat.push({ message, isSend });
} }
return newChat; return newChat;
}); });
}; };
//add proper type signature for function //add proper type signature for function
function updateLastMessage({ function updateLastMessage({
str, str,
thought, thought,
end = false, end = false,
}: { }: {
str?: string; str?: string;
thought?: string; thought?: string;
// end param default is false // end param default is false
end?: boolean; end?: boolean;
}) { }) {
setChatHistory((old) => { setChatHistory((old) => {
let newChat = [...old]; let newChat = [...old];
if (str) { if (str) {
if (end && !newChat[newChat.length - 1].message) { if (end) {
newChat[newChat.length - 1].message = str; newChat[newChat.length - 1].message = str;
} } else {
newChat[newChat.length - 1].message = newChat[newChat.length - 1].message =
newChat[newChat.length - 1].message + str; newChat[newChat.length - 1].message + str;
} }
if (thought) { }
newChat[newChat.length - 1].thought = thought; if (thought) {
} newChat[newChat.length - 1].thought = thought;
return newChat; }
}); return newChat;
} });
}
function handleOnClose(event: CloseEvent) { function handleOnClose(event: CloseEvent) {
if (isOpen.current) { if (isOpen.current) {
setLockChat(false); setLockChat(false);
setTimeout(() => { setTimeout(() => {
connectWS(); connectWS();
}, 1000); }, 1000);
} }
} }
function handleWsMessage(data: any) { function handleWsMessage(data: any) {
if (Array.isArray(data)) { if (Array.isArray(data)) {
//set chat history //set chat history
setChatHistory((_) => { setChatHistory((_) => {
let newChatHistory: ChatMessageType[] = []; let newChatHistory: ChatMessageType[] = [];
data.forEach( data.forEach(
(chatItem: { (chatItem: {
intermediate_steps?: "string"; intermediate_steps?: "string";
is_bot: boolean; is_bot: boolean;
message: string; message: string;
type: string; type: string;
files?: Array<any>; files?: Array<any>;
}) => { }) => {
if (chatItem.message) { if (chatItem.message) {
newChatHistory.push( newChatHistory.push(
chatItem.files chatItem.files
? { ? {
isSend: !chatItem.is_bot, isSend: !chatItem.is_bot,
message: chatItem.message, message: chatItem.message,
thought: chatItem.intermediate_steps, thought: chatItem.intermediate_steps,
files: chatItem.files, files: chatItem.files,
} }
: { : {
isSend: !chatItem.is_bot, isSend: !chatItem.is_bot,
message: chatItem.message, message: chatItem.message,
thought: chatItem.intermediate_steps, thought: chatItem.intermediate_steps,
} }
); );
} }
} }
); );
return newChatHistory; return newChatHistory;
}); });
} }
if (data.type === "start") { if (data.type === "start") {
console.log("start"); console.log("start");
addChatHistory("", false); addChatHistory("", false);
isStream = true; isStream = true;
} }
if (data.type === "end") { if (data.type === "end") {
if (data.intermediate_steps) { if (data.intermediate_steps) {
updateLastMessage({ updateLastMessage({
str: data.message, str: data.message,
thought: data.intermediate_steps, thought: data.intermediate_steps,
end: true, end: true,
}); });
}
setLockChat(false);
isStream = false;
}
if (data.type === "file") {
console.log(data);
}
if (data.type === "stream" && isStream) {
updateLastMessage({ str: data.message });
}
}
} function connectWS() {
setLockChat(false); try {
isStream = false; const urlWs =
} process.env.NODE_ENV === "development"
if (data.type === "file") { ? `ws://localhost:7860/chat/${flow.id}`
console.log(data); : `${window.location.protocol === "https:" ? "wss" : "ws"}://${
} window.location.host
if (data.type === "stream" && isStream) { }/chat/${flow.id}`;
updateLastMessage({ str: data.message });
}
}
function connectWS() { const newWs = new WebSocket(urlWs);
try { newWs.onopen = () => {
const urlWs = console.log("WebSocket connection established!");
process.env.NODE_ENV === "development" };
? `ws://localhost:7860/chat/${flow.id}` console.log(flow.id);
: `${window.location.protocol === "https:" ? "wss" : "ws"}://${ newWs.onmessage = (event) => {
window.location.host const data = JSON.parse(event.data);
}/chat/${flow.id}`; console.log("Received data:", data);
handleWsMessage(data);
//get chat history
};
newWs.onclose = (event) => {
handleOnClose(event);
};
newWs.onerror = (ev) => {
console.log(ev, "error");
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
],
});
};
ws.current = newWs;
} catch {
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
],
});
}
}
const newWs = new WebSocket(urlWs); useEffect(() => {
newWs.onopen = () => { connectWS();
console.log("WebSocket connection established!"); return () => {
}; console.log("unmount");
console.log(flow.id); console.log(ws);
newWs.onmessage = (event) => { if (ws) {
const data = JSON.parse(event.data); ws.current.close();
console.log("Received data:", data); }
handleWsMessage(data); };
//get chat history }, []);
};
newWs.onclose = (event) => {
handleOnClose(event);
};
newWs.onerror = (ev) => {
console.log(ev, "error");
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
],
});
};
ws.current = newWs;
} catch {
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
],
});
}
}
useEffect(() => { async function sendAll(data: sendAllProps) {
connectWS(); try {
return () => { if (ws) {
console.log("unmount"); ws.current.send(JSON.stringify(data));
console.log(ws); }
if (ws) { } catch (error) {
ws.current.close(); setErrorData({
} title: "There was an erro sending the message",
}; list: [error.message],
}, []); });
setChatValue(data.message);
connectWS();
}
}
async function sendAll(data: sendAllProps) { useEffect(() => {
try { if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
if (ws) { }, [chatHistory]);
ws.current.send(JSON.stringify(data));
}
} catch (error) {
setErrorData({
title: "There was an erro sending the message",
list: [error.message],
});
setChatValue(data.message);
connectWS();
}
}
useEffect(() => { function validateNode(n: NodeType): Array<string> {
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" }); if (!n.data?.node?.template || !Object.keys(n.data.node.template)) {
}, [chatHistory]); setNoticeData({
title:
"We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!",
});
return [];
}
function validateNode(n: NodeType): Array<string> { const {
if (!n.data?.node?.template || !Object.keys(n.data.node.template)) { type,
setNoticeData({ node: { template },
title: } = n.data;
"We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!",
});
return [];
}
const { return Object.keys(template).reduce(
type, (errors: Array<string>, t) =>
node: { template }, errors.concat(
} = n.data; template[t].required &&
template[t].show &&
(!template[t].value || template[t].value === "") &&
!reactFlowInstance
.getEdges()
.some(
(e) =>
e.targetHandle.split("|")[1] === t &&
e.targetHandle.split("|")[2] === n.id
)
? [
`${type} is missing ${
template.display_name
? template.display_name
: toNormalCase(template[t].name)
}.`,
]
: []
),
[] as string[]
);
}
return Object.keys(template).reduce( function validateNodes() {
(errors: Array<string>, t) => return reactFlowInstance
errors.concat( .getNodes()
template[t].required && .flatMap((n: NodeType) => validateNode(n));
template[t].show && }
(!template[t].value || template[t].value === "") &&
!reactFlowInstance
.getEdges()
.some(
(e) =>
e.targetHandle.split("|")[1] === t &&
e.targetHandle.split("|")[2] === n.id
)
? [
`${type} is missing ${
template.display_name
? template.display_name
: toNormalCase(template[t].name)
}.`,
]
: []
),
[] as string[]
);
}
function validateNodes() { const ref = useRef(null);
return reactFlowInstance
.getNodes()
.flatMap((n: NodeType) => validateNode(n));
}
const ref = useRef(null); function sendMessage() {
if (chatValue !== "") {
let nodeValidationErrors = validateNodes();
if (nodeValidationErrors.length === 0) {
setLockChat(true);
let message = chatValue;
setChatValue("");
addChatHistory(message, true);
sendAll({
...reactFlowInstance.toObject(),
message,
chatHistory,
name: flow.name,
description: flow.description,
});
} else {
setErrorData({
title: "Oops! Looks like you missed some required information:",
list: nodeValidationErrors,
});
}
} else {
setErrorData({
title: "Error sending message",
list: ["The message cannot be empty."],
});
}
}
function clearChat() {
setChatHistory([]);
ws.current.send(JSON.stringify({ clear_history: true }));
}
function sendMessage() { function setModalOpen(x: boolean) {
if (chatValue !== "") { setOpen(x);
let nodeValidationErrors = validateNodes(); }
if (nodeValidationErrors.length === 0) { return (
setLockChat(true); <Transition.Root show={open} appear={open} as={Fragment}>
let message = chatValue; <Dialog
setChatValue(""); as="div"
addChatHistory(message, true); className="relative z-10"
sendAll({ onClose={setModalOpen}
...reactFlowInstance.toObject(), initialFocus={ref}
message, >
chatHistory, <Transition.Child
name: flow.name, as={Fragment}
description: flow.description, enter="ease-out duration-300"
}); enterFrom="opacity-0"
} else { enterTo="opacity-100"
setErrorData({ leave="ease-in duration-200"
title: "Oops! Looks like you missed some required information:", leaveFrom="opacity-100"
list: nodeValidationErrors, leaveTo="opacity-0"
}); >
} <div className="fixed inset-0 bg-black backdrop-blur-sm dark:bg-gray-600 dark:bg-opacity-80 bg-opacity-80 transition-opacity" />
} else { </Transition.Child>
setErrorData({
title: "Error sending message",
list: ["The message cannot be empty."],
});
}
}
function clearChat() {
setChatHistory([]);
ws.current.send(JSON.stringify({ clear_history: true }));
}
function setModalOpen(x: boolean) { <div className="fixed inset-0 z-10 overflow-y-auto">
setOpen(x); <div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
} <Transition.Child
return ( as={Fragment}
<Transition.Root show={open} appear={open} as={Fragment}> enter="ease-out duration-300"
<Dialog enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
as="div" enterTo="opacity-100 translate-y-0 sm:scale-100"
className="relative z-10" leave="ease-in duration-200"
onClose={setModalOpen} leaveFrom="opacity-100 translate-y-0 sm:scale-100"
initialFocus={ref} leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Transition.Child <Dialog.Panel className=" drop-shadow-2xl relative flex flex-col justify-between transform h-[95%] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[690px]">
as={Fragment} <div className="relative w-full">
enter="ease-out duration-300" <button
enterFrom="opacity-0" onClick={() => clearChat()}
enterTo="opacity-100" className="absolute top-2 right-2 hover:text-red-500 z-30"
leave="ease-in duration-200" >
leaveFrom="opacity-100" <FaEraser className="w-4 h-4" />
leaveTo="opacity-0" </button>
> </div>
<div className="fixed inset-0 bg-black backdrop-blur-sm dark:bg-gray-600 dark:bg-opacity-80 bg-opacity-80 transition-opacity" /> <div className="w-full h-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex-col flex items-center overflow-scroll scrollbar-hide">
</Transition.Child> {chatHistory.length > 0 ? (
chatHistory.map((c, i) => <ChatMessage chat={c} key={i} />)
<div className="fixed inset-0 z-10 overflow-y-auto"> ) : (
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0"> <div className="flex flex-col h-full text-center justify-center w-full items-center align-middle ">
<Transition.Child <span>
as={Fragment} 👋{" "}
enter="ease-out duration-300" <span className="text-gray-600 text-lg">
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" LangFlow Chat
enterTo="opacity-100 translate-y-0 sm:scale-100" </span>
leave="ease-in duration-200" </span>
leaveFrom="opacity-100 translate-y-0 sm:scale-100" <br />
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" <div className="bg-gray-100 rounded-md w-2/4 px-6 py-8 border border-gray-200">
> <span className="text-base text-gray-500">
<Dialog.Panel className=" drop-shadow-2xl relative flex flex-col justify-between transform h-[95%] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[690px]"> Start a conversation and click the agent’s thoughts{" "}
<div className="relative w-full"> <span>
<button <ChatBubbleOvalLeftEllipsisIcon className="w-6 h-6 inline animate-bounce " />
onClick={() => clearChat()} </span>{" "}
className="absolute top-2 right-2 hover:text-red-500" to inspect the chaining process.
> </span>
<FaEraser className="w-4 h-4" /> </div>
</button> </div>
</div> )}
<div className="w-full h-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex-col flex items-center overflow-scroll scrollbar-hide"> <div ref={ref}></div>
{chatHistory.length > 0 ? ( </div>
chatHistory.map((c, i) => <ChatMessage chat={c} key={i} />) <div className="w-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex-col flex items-center justify-between p-3">
) : ( <div className="relative w-full mt-1 rounded-md shadow-sm">
<div className="flex flex-col h-full text-center justify-center w-full items-center align-middle "> <ChatInput
<span> chatValue={chatValue}
👋{" "} lockChat={lockChat}
<span className="text-gray-600 text-lg"> sendMessage={sendMessage}
LangFlow Chat setChatValue={setChatValue}
</span> />
</span> </div>
<br /> </div>
<div className="bg-gray-100 rounded-md w-2/4 px-6 py-8 border border-gray-200"> </Dialog.Panel>
<span className="text-base text-gray-500"> </Transition.Child>
Start a conversation and click the agent’s thoughts{" "} </div>
<span> </div>
<ChatBubbleOvalLeftEllipsisIcon className="w-6 h-6 inline animate-bounce " /> </Dialog>
</span>{" "} </Transition.Root>
to inspect the chaining process. );
</span>
</div>
</div>
)}
<div ref={ref}></div>
</div>
<div className="w-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex-col flex items-center justify-between p-3">
<div className="relative w-full mt-1 rounded-md shadow-sm">
<ChatInput
chatValue={chatValue}
lockChat={lockChat}
sendMessage={sendMessage}
setChatValue={setChatValue}
/>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
} }