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 23914963fe4..b7e540cb66e 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 (
+ {isLastMessage &&
+ !message?.includes("Workspace chat memory was reset!") && (
+
+ )}
{chatId && (
<>
+
+
+
+ );
+}
+
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 0371d64e50b..5f4e6c672d4 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 (
)}
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
index c0eb5bf4cca..3c2c47a0523 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 b3cc0d942ba..494ee57d9bf 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}
/>
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 e9df2613c9d..f85c213fc28 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],