diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx
index 23914963f..b7e540cb6 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx
@@ -5,11 +5,19 @@ import {
   ClipboardText,
   ThumbsUp,
   ThumbsDown,
+  ArrowsClockwise,
 } from "@phosphor-icons/react";
 import { Tooltip } from "react-tooltip";
 import Workspace from "@/models/workspace";
 
-const Actions = ({ message, feedbackScore, chatId, slug }) => {
+const Actions = ({
+  message,
+  feedbackScore,
+  chatId,
+  slug,
+  isLastMessage,
+  regenerateMessage,
+}) => {
   const [selectedFeedback, setSelectedFeedback] = useState(feedbackScore);
 
   const handleFeedback = async (newFeedback) => {
@@ -22,6 +30,14 @@ const Actions = ({ message, feedbackScore, chatId, slug }) => {
   return (
     <div className="flex justify-start items-center gap-x-4">
       <CopyMessage message={message} />
+      {isLastMessage &&
+        !message?.includes("Workspace chat memory was reset!") && (
+          <RegenerateMessage
+            regenerateMessage={regenerateMessage}
+            slug={slug}
+            chatId={chatId}
+          />
+        )}
       {chatId && (
         <>
           <FeedbackButton
@@ -106,4 +122,26 @@ function CopyMessage({ message }) {
   );
 }
 
+function RegenerateMessage({ regenerateMessage, chatId }) {
+  return (
+    <div className="mt-3 relative">
+      <button
+        onClick={() => regenerateMessage(chatId)}
+        data-tooltip-id="regenerate-assistant-text"
+        data-tooltip-content="Regenerate response"
+        className="border-none text-zinc-300"
+        aria-label="Regenerate"
+      >
+        <ArrowsClockwise size={18} className="mb-1" weight="fill" />
+      </button>
+      <Tooltip
+        id="regenerate-assistant-text"
+        place="bottom"
+        delayShow={300}
+        className="tooltip !text-xs"
+      />
+    </div>
+  );
+}
+
 export default memo(Actions);
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
index 0371d64e5..5f4e6c672 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
@@ -19,6 +19,8 @@ const HistoricalMessage = ({
   error = false,
   feedbackScore = null,
   chatId = null,
+  isLastMessage = false,
+  regenerateMessage,
 }) => {
   return (
     <div
@@ -59,6 +61,8 @@ const HistoricalMessage = ({
               feedbackScore={feedbackScore}
               chatId={chatId}
               slug={workspace?.slug}
+              isLastMessage={isLastMessage}
+              regenerateMessage={regenerateMessage}
             />
           </div>
         )}
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
index c0eb5bf4c..3c2c47a05 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
@@ -8,7 +8,12 @@ import debounce from "lodash.debounce";
 import useUser from "@/hooks/useUser";
 import Chartable from "./Chartable";
 
-export default function ChatHistory({ history = [], workspace, sendCommand }) {
+export default function ChatHistory({
+  history = [],
+  workspace,
+  sendCommand,
+  regenerateAssistantMessage,
+}) {
   const { user } = useUser();
   const { showing, showModal, hideModal } = useManageWorkspaceModal();
   const [isAtBottom, setIsAtBottom] = useState(true);
@@ -165,6 +170,8 @@ export default function ChatHistory({ history = [], workspace, sendCommand }) {
             feedbackScore={props.feedbackScore}
             chatId={props.chatId}
             error={props.error}
+            regenerateMessage={regenerateAssistantMessage}
+            isLastMessage={isLastBotReply}
           />
         );
       })}
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
index b3cc0d942..494ee57d9 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
@@ -26,7 +26,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
     setMessage(event.target.value);
   };
 
-  // Emit an update to the sate of the prompt input without directly
+  // Emit an update to the state of the prompt input without directly
   // passing a prop in so that it does not re-render constantly.
   function setMessageEmit(messageContent = "") {
     setMessage(messageContent);
@@ -56,24 +56,47 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
     setLoadingResponse(true);
   };
 
-  const sendCommand = async (command, submit = false) => {
+  const regenerateAssistantMessage = (chatId) => {
+    const updatedHistory = chatHistory.slice(0, -1);
+    const lastUserMessage = updatedHistory.slice(-1)[0];
+    Workspace.deleteChats(workspace.slug, [chatId])
+      .then(() => sendCommand(lastUserMessage.content, true, updatedHistory))
+      .catch((e) => console.error(e));
+  };
+
+  const sendCommand = async (command, submit = false, history = []) => {
     if (!command || command === "") return false;
     if (!submit) {
       setMessageEmit(command);
       return;
     }
 
-    const prevChatHistory = [
-      ...chatHistory,
-      { content: command, role: "user" },
-      {
-        content: "",
-        role: "assistant",
-        pending: true,
-        userMessage: command,
-        animate: true,
-      },
-    ];
+    let prevChatHistory;
+    if (history.length > 0) {
+      // use pre-determined history chain.
+      prevChatHistory = [
+        ...history,
+        {
+          content: "",
+          role: "assistant",
+          pending: true,
+          userMessage: command,
+          animate: true,
+        },
+      ];
+    } else {
+      prevChatHistory = [
+        ...chatHistory,
+        { content: command, role: "user" },
+        {
+          content: "",
+          role: "assistant",
+          pending: true,
+          userMessage: command,
+          animate: true,
+        },
+      ];
+    }
 
     setChatHistory(prevChatHistory);
     setMessageEmit("");
@@ -217,6 +240,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
           history={chatHistory}
           workspace={workspace}
           sendCommand={sendCommand}
+          regenerateAssistantMessage={regenerateAssistantMessage}
         />
         <PromptInput
           submit={handleSubmit}
diff --git a/frontend/src/models/workspace.js b/frontend/src/models/workspace.js
index 7bc95ef8c..91f4a2db3 100644
--- a/frontend/src/models/workspace.js
+++ b/frontend/src/models/workspace.js
@@ -74,6 +74,22 @@ const Workspace = {
       .catch(() => false);
     return result;
   },
+
+  deleteChats: async function (slug = "", chatIds = []) {
+    return await fetch(`${API_BASE}/workspace/${slug}/delete-chats`, {
+      method: "DELETE",
+      headers: baseHeaders(),
+      body: JSON.stringify({ chatIds }),
+    })
+      .then((res) => {
+        if (res.ok) return true;
+        throw new Error("Failed to delete chats.");
+      })
+      .catch((e) => {
+        console.log(e);
+        return false;
+      });
+  },
   streamChat: async function ({ slug }, message, handleChat) {
     const ctrl = new AbortController();
 
diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js
index e9df2613c..f85c213fc 100644
--- a/server/endpoints/workspaces.js
+++ b/server/endpoints/workspaces.js
@@ -372,6 +372,37 @@ function workspaceEndpoints(app) {
     }
   );
 
+  app.delete(
+    "/workspace/:slug/delete-chats",
+    [validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug],
+    async (request, response) => {
+      try {
+        const { chatIds = [] } = reqBody(request);
+        const user = await userFromSession(request, response);
+        const workspace = response.locals.workspace;
+
+        if (!workspace || !Array.isArray(chatIds)) {
+          response.sendStatus(400).end();
+          return;
+        }
+
+        // This works for both workspace and threads.
+        // we simplify this by just looking at workspace<>user overlap
+        // since they are all on the same table.
+        await WorkspaceChats.delete({
+          id: { in: chatIds.map((id) => Number(id)) },
+          user_id: user?.id ?? null,
+          workspaceId: workspace.id,
+        });
+
+        response.sendStatus(200).end();
+      } catch (e) {
+        console.log(e.message, e);
+        response.sendStatus(500).end();
+      }
+    }
+  );
+
   app.post(
     "/workspace/:slug/chat-feedback/:chatId",
     [validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug],