diff --git a/src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx b/src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx
new file mode 100644
index 000000000..2e699a5c7
--- /dev/null
+++ b/src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx
@@ -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 (
+
+
Code Block
+
+ )
+}
diff --git a/src/frontend/src/modals/chatModal/chatMessage/index.tsx b/src/frontend/src/modals/chatModal/chatMessage/index.tsx
index 033d02b48..3ae1c6b6a 100644
--- a/src/frontend/src/modals/chatModal/chatMessage/index.tsx
+++ b/src/frontend/src/modals/chatModal/chatMessage/index.tsx
@@ -65,6 +65,14 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose"
+ // components={{
+ // code: (props) => (
+ //
+ // ),
+ // }}
>
{message}
diff --git a/src/frontend/src/modals/chatModal/index.tsx b/src/frontend/src/modals/chatModal/index.tsx
index c6caffb26..8070569e2 100644
--- a/src/frontend/src/modals/chatModal/index.tsx
+++ b/src/frontend/src/modals/chatModal/index.tsx
@@ -14,383 +14,383 @@ import ChatInput from "./chatInput";
const _ = require("lodash");
export default function ChatModal({
- flow,
- open,
- setOpen,
+ flow,
+ open,
+ setOpen,
}: {
- open: boolean;
- setOpen: Function;
- flow: FlowType;
+ open: boolean;
+ setOpen: Function;
+ flow: FlowType;
}) {
- const [chatValue, setChatValue] = useState("");
- const [chatHistory, setChatHistory] = useState([]);
- const { reactFlowInstance } = useContext(typesContext);
- const { setErrorData, setNoticeData } = useContext(alertContext);
- const ws = useRef(null);
- const [lockChat, setLockChat] = useState(false);
- const isOpen = useRef(open);
+ const [chatValue, setChatValue] = useState("");
+ const [chatHistory, setChatHistory] = useState([]);
+ const { reactFlowInstance } = useContext(typesContext);
+ const { setErrorData, setNoticeData } = useContext(alertContext);
+ const ws = useRef(null);
+ const [lockChat, setLockChat] = useState(false);
+ const isOpen = useRef(open);
- useEffect(() => {
- isOpen.current = open;
- }, [open]);
- var isStream = false;
+ useEffect(() => {
+ isOpen.current = open;
+ }, [open]);
+ var isStream = false;
- const addChatHistory = (
- message: string,
- isSend: boolean,
- thought?: string,
- files?: Array
- ) => {
- setChatHistory((old) => {
- let newChat = _.cloneDeep(old);
- if (files) {
- newChat.push({ message, isSend, files, thought });
- } else if (thought) {
- newChat.push({ message, isSend, thought });
- } else {
- newChat.push({ message, isSend });
- }
- return newChat;
- });
- };
+ const addChatHistory = (
+ message: string,
+ isSend: boolean,
+ thought?: string,
+ files?: Array
+ ) => {
+ setChatHistory((old) => {
+ let newChat = _.cloneDeep(old);
+ if (files) {
+ newChat.push({ message, isSend, files, thought });
+ } else if (thought) {
+ newChat.push({ message, isSend, thought });
+ } else {
+ newChat.push({ message, isSend });
+ }
+ return newChat;
+ });
+ };
- //add proper type signature for function
+ //add proper type signature for function
- function updateLastMessage({
- str,
- thought,
- end = false,
- }: {
- str?: string;
- thought?: string;
- // end param default is false
- end?: boolean;
- }) {
- setChatHistory((old) => {
- let newChat = [...old];
- if (str) {
- if (end && !newChat[newChat.length - 1].message) {
- newChat[newChat.length - 1].message = str;
- }
- newChat[newChat.length - 1].message =
- newChat[newChat.length - 1].message + str;
- }
- if (thought) {
- newChat[newChat.length - 1].thought = thought;
- }
- return newChat;
- });
- }
+ function updateLastMessage({
+ str,
+ thought,
+ end = false,
+ }: {
+ str?: string;
+ thought?: string;
+ // end param default is false
+ end?: boolean;
+ }) {
+ setChatHistory((old) => {
+ let newChat = [...old];
+ if (str) {
+ if (end) {
+ newChat[newChat.length - 1].message = str;
+ } else {
+ newChat[newChat.length - 1].message =
+ newChat[newChat.length - 1].message + str;
+ }
+ }
+ if (thought) {
+ newChat[newChat.length - 1].thought = thought;
+ }
+ return newChat;
+ });
+ }
- function handleOnClose(event: CloseEvent) {
- if (isOpen.current) {
- setLockChat(false);
- setTimeout(() => {
- connectWS();
- }, 1000);
- }
- }
+ function handleOnClose(event: CloseEvent) {
+ if (isOpen.current) {
+ setLockChat(false);
+ setTimeout(() => {
+ connectWS();
+ }, 1000);
+ }
+ }
- function handleWsMessage(data: any) {
- if (Array.isArray(data)) {
- //set chat history
- setChatHistory((_) => {
- let newChatHistory: ChatMessageType[] = [];
- data.forEach(
- (chatItem: {
- intermediate_steps?: "string";
- is_bot: boolean;
- message: string;
- type: string;
- files?: Array;
- }) => {
- if (chatItem.message) {
- newChatHistory.push(
- chatItem.files
- ? {
- isSend: !chatItem.is_bot,
- message: chatItem.message,
- thought: chatItem.intermediate_steps,
- files: chatItem.files,
- }
- : {
- isSend: !chatItem.is_bot,
- message: chatItem.message,
- thought: chatItem.intermediate_steps,
- }
- );
- }
- }
- );
- return newChatHistory;
- });
- }
- if (data.type === "start") {
- console.log("start");
- addChatHistory("", false);
- isStream = true;
- }
- if (data.type === "end") {
- if (data.intermediate_steps) {
- updateLastMessage({
- str: data.message,
- thought: data.intermediate_steps,
- end: true,
- });
-
- }
- setLockChat(false);
- isStream = false;
- }
- if (data.type === "file") {
- console.log(data);
- }
- if (data.type === "stream" && isStream) {
- updateLastMessage({ str: data.message });
- }
- }
+ function handleWsMessage(data: any) {
+ if (Array.isArray(data)) {
+ //set chat history
+ setChatHistory((_) => {
+ let newChatHistory: ChatMessageType[] = [];
+ data.forEach(
+ (chatItem: {
+ intermediate_steps?: "string";
+ is_bot: boolean;
+ message: string;
+ type: string;
+ files?: Array;
+ }) => {
+ if (chatItem.message) {
+ newChatHistory.push(
+ chatItem.files
+ ? {
+ isSend: !chatItem.is_bot,
+ message: chatItem.message,
+ thought: chatItem.intermediate_steps,
+ files: chatItem.files,
+ }
+ : {
+ isSend: !chatItem.is_bot,
+ message: chatItem.message,
+ thought: chatItem.intermediate_steps,
+ }
+ );
+ }
+ }
+ );
+ return newChatHistory;
+ });
+ }
+ if (data.type === "start") {
+ console.log("start");
+ addChatHistory("", false);
+ isStream = true;
+ }
+ if (data.type === "end") {
+ if (data.intermediate_steps) {
+ updateLastMessage({
+ str: data.message,
+ thought: data.intermediate_steps,
+ end: true,
+ });
+ }
+ setLockChat(false);
+ isStream = false;
+ }
+ if (data.type === "file") {
+ console.log(data);
+ }
+ if (data.type === "stream" && isStream) {
+ updateLastMessage({ str: data.message });
+ }
+ }
- function connectWS() {
- try {
- const urlWs =
- process.env.NODE_ENV === "development"
- ? `ws://localhost:7860/chat/${flow.id}`
- : `${window.location.protocol === "https:" ? "wss" : "ws"}://${
- window.location.host
- }/chat/${flow.id}`;
+ function connectWS() {
+ try {
+ const urlWs =
+ process.env.NODE_ENV === "development"
+ ? `ws://localhost:7860/chat/${flow.id}`
+ : `${window.location.protocol === "https:" ? "wss" : "ws"}://${
+ window.location.host
+ }/chat/${flow.id}`;
- const newWs = new WebSocket(urlWs);
- newWs.onopen = () => {
- console.log("WebSocket connection established!");
- };
- console.log(flow.id);
- newWs.onmessage = (event) => {
- const data = JSON.parse(event.data);
- 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);
+ newWs.onopen = () => {
+ console.log("WebSocket connection established!");
+ };
+ console.log(flow.id);
+ newWs.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+ 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(() => {
- connectWS();
- return () => {
- console.log("unmount");
- console.log(ws);
- if (ws) {
- ws.current.close();
- }
- };
- }, []);
+ useEffect(() => {
+ connectWS();
+ return () => {
+ console.log("unmount");
+ console.log(ws);
+ if (ws) {
+ ws.current.close();
+ }
+ };
+ }, []);
- async function sendAll(data: sendAllProps) {
- try {
- if (ws) {
- ws.current.send(JSON.stringify(data));
- }
- } catch (error) {
- setErrorData({
- title: "There was an erro sending the message",
- list: [error.message],
- });
- setChatValue(data.message);
- connectWS();
- }
- }
+ async function sendAll(data: sendAllProps) {
+ try {
+ if (ws) {
+ 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(() => {
- if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
- }, [chatHistory]);
+ useEffect(() => {
+ if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
+ }, [chatHistory]);
- function validateNode(n: NodeType): Array {
- if (!n.data?.node?.template || !Object.keys(n.data.node.template)) {
- 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 {
+ if (!n.data?.node?.template || !Object.keys(n.data.node.template)) {
+ 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 [];
+ }
- const {
- type,
- node: { template },
- } = n.data;
+ const {
+ type,
+ node: { template },
+ } = n.data;
- return Object.keys(template).reduce(
- (errors: Array, t) =>
- errors.concat(
- 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(
+ (errors: Array, t) =>
+ errors.concat(
+ 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[]
+ );
+ }
- function validateNodes() {
- return reactFlowInstance
- .getNodes()
- .flatMap((n: NodeType) => validateNode(n));
- }
+ function validateNodes() {
+ return reactFlowInstance
+ .getNodes()
+ .flatMap((n: NodeType) => validateNode(n));
+ }
- const ref = useRef(null);
+ 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() {
+ 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 setModalOpen(x: boolean) {
- setOpen(x);
- }
- return (
-
-
-
-
-
+ function setModalOpen(x: boolean) {
+ setOpen(x);
+ }
+ return (
+
+
+
+
+
-
-
-
-
-
- clearChat()}
- className="absolute top-2 right-2 hover:text-red-500"
- >
-
-
-
-
- {chatHistory.length > 0 ? (
- chatHistory.map((c, i) =>
)
- ) : (
-
-
- 👋{" "}
-
- LangFlow Chat
-
-
-
-
-
- Start a conversation and click the agent’s thoughts{" "}
-
-
- {" "}
- to inspect the chaining process.
-
-
-
- )}
-
-
-
-
-
-
-
-
-
- );
+
+
+
+
+
+ clearChat()}
+ className="absolute top-2 right-2 hover:text-red-500 z-30"
+ >
+
+
+
+
+ {chatHistory.length > 0 ? (
+ chatHistory.map((c, i) =>
)
+ ) : (
+
+
+ 👋{" "}
+
+ LangFlow Chat
+
+
+
+
+
+ Start a conversation and click the agent’s thoughts{" "}
+
+
+ {" "}
+ to inspect the chaining process.
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
}