From 51dbff0dcb550fa2f705694aafc5c44bc70555e8 Mon Sep 17 00:00:00 2001
From: Timothy Carambat <rambat1010@gmail.com>
Date: Fri, 16 Feb 2024 14:50:40 -0800
Subject: [PATCH] Breakout Chat/Query mode as a workspace setting (#734)

Remove useless icons in prompt bar
Add chatMode column to workspaces that defaults to chat
Add UI for toggle of chat mode with hint
Update UI for workspace settings to match designs
---
 .../ChatContainer/PromptInput/index.jsx       | 71 +------------------
 .../WorkspaceChat/ChatContainer/index.jsx     |  6 --
 frontend/src/index.css                        |  4 ++
 frontend/src/models/workspace.js              |  4 +-
 frontend/src/models/workspaceThread.js        |  3 +-
 .../ChatHistorySettings/index.jsx             |  5 +-
 .../ChatSettings/ChatModeSelection/index.jsx  | 57 +++++++++++++++
 .../ChatSettings/ChatModelSelection/index.jsx |  7 +-
 .../ChatSettings/ChatPromptSettings/index.jsx |  2 +-
 .../ChatTemperatureSettings/index.jsx         |  2 +-
 .../WorkspaceSettings/ChatSettings/index.jsx  |  2 +
 .../SuggestedChatMessages/index.jsx           |  8 +--
 .../GeneralAppearance/VectorCount/index.jsx   |  4 +-
 .../GeneralAppearance/WorkspaceName/index.jsx |  2 +-
 .../DocumentSimilarityThreshold/index.jsx     |  2 +-
 .../MaxContextSnippets/index.jsx              |  2 +-
 .../VectorDBIdentifier/index.jsx              | 10 +--
 server/endpoints/chat.js                      | 46 +++++-------
 server/models/workspace.js                    |  3 +-
 .../20240216214639_init/migration.sql         |  2 +
 server/prisma/schema.prisma                   |  1 +
 21 files changed, 105 insertions(+), 138 deletions(-)
 create mode 100644 frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModeSelection/index.jsx
 create mode 100644 server/prisma/migrations/20240216214639_init/migration.sql

diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
index 75316308e..45193df57 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
@@ -1,21 +1,10 @@
-import {
-  Chats,
-  CircleNotch,
-  Gear,
-  PaperPlaneRight,
-  Quotes,
-} from "@phosphor-icons/react";
+import { CircleNotch, PaperPlaneRight } from "@phosphor-icons/react";
 import React, { useState, useRef } from "react";
-import ManageWorkspace, {
-  useManageWorkspaceModal,
-} from "../../../Modals/MangeWorkspace";
-import useUser from "@/hooks/useUser";
 import SlashCommandsButton, {
   SlashCommands,
   useSlashCommands,
 } from "./SlashCommands";
 import { isMobile } from "react-device-detect";
-import { Tooltip } from "react-tooltip";
 
 export default function PromptInput({
   workspace,
@@ -27,10 +16,8 @@ export default function PromptInput({
   sendCommand,
 }) {
   const { showSlashCommand, setShowSlashCommand } = useSlashCommands();
-  const { showing, showModal, hideModal } = useManageWorkspaceModal();
   const formRef = useRef(null);
   const [_, setFocused] = useState(false);
-  const { user } = useUser();
 
   const handleSubmit = (e) => {
     setFocused(false);
@@ -100,24 +87,6 @@ export default function PromptInput({
             </div>
             <div className="flex justify-between py-3.5">
               <div className="flex gap-x-2">
-                {user?.role !== "default" && (
-                  <div>
-                    <Gear
-                      onClick={showModal}
-                      data-tooltip-id="tooltip-workspace-settings-prompt"
-                      data-tooltip-content={`Open the ${workspace.name} workspace settings`}
-                      className="w-7 h-7 text-white/60 hover:text-white cursor-pointer"
-                      weight="fill"
-                    />
-                    <Tooltip
-                      id="tooltip-workspace-settings-prompt"
-                      place="top"
-                      delayShow={300}
-                      className="tooltip !text-xs z-99"
-                    />
-                  </div>
-                )}
-                <ChatModeSelector workspace={workspace} />
                 <SlashCommandsButton
                   showing={showSlashCommand}
                   setShowSlashCommand={setShowSlashCommand}
@@ -127,44 +96,6 @@ export default function PromptInput({
           </div>
         </div>
       </form>
-      {showing && (
-        <ManageWorkspace hideModal={hideModal} providedSlug={workspace.slug} />
-      )}
-    </div>
-  );
-}
-
-function ChatModeSelector({ workspace }) {
-  const STORAGE_KEY = `workspace_chat_mode_${workspace.slug}`;
-  const [chatMode, setChatMode] = useState(
-    window.localStorage.getItem(STORAGE_KEY) ?? "chat"
-  );
-
-  function toggleMode() {
-    const newChatMode = chatMode === "chat" ? "query" : "chat";
-    setChatMode(newChatMode);
-    window.localStorage.setItem(STORAGE_KEY, newChatMode);
-  }
-
-  const ModeIcon = chatMode === "chat" ? Chats : Quotes;
-  return (
-    <div
-      data-tooltip-id="chat-mode-toggle"
-      data-tooltip-content={`You are currently in ${chatMode} mode. Click to switch to ${
-        chatMode === "chat" ? "query" : "chat"
-      } mode.`}
-    >
-      <ModeIcon
-        onClick={toggleMode}
-        className="w-7 h-7 text-white/60 hover:text-white cursor-pointer"
-        weight="fill"
-      />
-      <Tooltip
-        id="chat-mode-toggle"
-        place="top"
-        delayShow={300}
-        className="tooltip !text-xs z-99"
-      />
     </div>
   );
 }
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
index 543d6105e..8a99a62a2 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx
@@ -77,9 +77,6 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
         await Workspace.threads.streamChat(
           { workspaceSlug: workspace.slug, threadSlug },
           promptMessage.userMessage,
-          window.localStorage.getItem(
-            `workspace_chat_mode_${workspace.slug}`
-          ) ?? "chat",
           (chatResult) =>
             handleChat(
               chatResult,
@@ -93,9 +90,6 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
         await Workspace.streamChat(
           workspace,
           promptMessage.userMessage,
-          window.localStorage.getItem(
-            `workspace_chat_mode_${workspace.slug}`
-          ) ?? "chat",
           (chatResult) =>
             handleChat(
               chatResult,
diff --git a/frontend/src/index.css b/frontend/src/index.css
index b9e6976da..2c4379822 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -439,3 +439,7 @@ dialog::backdrop {
 .slide-up {
   animation: slideUp 0.3s ease-out forwards;
 }
+
+.input-label {
+  @apply text-[14px] font-bold text-white;
+}
diff --git a/frontend/src/models/workspace.js b/frontend/src/models/workspace.js
index 3b31646df..d77e2ad55 100644
--- a/frontend/src/models/workspace.js
+++ b/frontend/src/models/workspace.js
@@ -73,11 +73,11 @@ const Workspace = {
       .catch(() => false);
     return result;
   },
-  streamChat: async function ({ slug }, message, mode = "query", handleChat) {
+  streamChat: async function ({ slug }, message, handleChat) {
     const ctrl = new AbortController();
     await fetchEventSource(`${API_BASE}/workspace/${slug}/stream-chat`, {
       method: "POST",
-      body: JSON.stringify({ message, mode }),
+      body: JSON.stringify({ message }),
       headers: baseHeaders(),
       signal: ctrl.signal,
       openWhenHidden: true,
diff --git a/frontend/src/models/workspaceThread.js b/frontend/src/models/workspaceThread.js
index 256ea496e..f9fad3173 100644
--- a/frontend/src/models/workspaceThread.js
+++ b/frontend/src/models/workspaceThread.js
@@ -77,7 +77,6 @@ const WorkspaceThread = {
   streamChat: async function (
     { workspaceSlug, threadSlug },
     message,
-    mode = "query",
     handleChat
   ) {
     const ctrl = new AbortController();
@@ -85,7 +84,7 @@ const WorkspaceThread = {
       `${API_BASE}/workspace/${workspaceSlug}/thread/${threadSlug}/stream-chat`,
       {
         method: "POST",
-        body: JSON.stringify({ message, mode }),
+        body: JSON.stringify({ message }),
         headers: baseHeaders(),
         signal: ctrl.signal,
         openWhenHidden: true,
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatHistorySettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatHistorySettings/index.jsx
index 29083574c..9d46bc3bf 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatHistorySettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatHistorySettings/index.jsx
@@ -2,10 +2,7 @@ export default function ChatHistorySettings({ workspace, setHasChanges }) {
   return (
     <div>
       <div className="flex flex-col gap-y-1 mb-4">
-        <label
-          htmlFor="name"
-          className="block mb-2 text-sm font-medium text-white"
-        >
+        <label htmlFor="name" className="block mb-2 input-label">
           Chat History
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium">
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModeSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModeSelection/index.jsx
new file mode 100644
index 000000000..0b3bb3b71
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModeSelection/index.jsx
@@ -0,0 +1,57 @@
+import { useState } from "react";
+export default function ChatModeSelection({ workspace, setHasChanges }) {
+  const [chatMode, setChatMode] = useState(workspace?.chatMode || "chat");
+
+  return (
+    <div>
+      <div className="flex flex-col">
+        <label htmlFor="chatMode" className="block input-label">
+          Chat mode
+        </label>
+      </div>
+
+      <div className="flex flex-col gap-y-1 mt-2">
+        <div className="w-fit flex gap-x-1 items-center p-1 rounded-lg bg-zinc-800 ">
+          <input type="hidden" name="chatMode" value={chatMode} />
+          <button
+            type="button"
+            disabled={chatMode === "chat"}
+            onClick={() => {
+              setChatMode("chat");
+              setHasChanges(true);
+            }}
+            className="transition-bg duration-200 px-6 py-1 text-md text-white/60 disabled:text-white bg-transparent disabled:bg-[#687280] rounded-md"
+          >
+            Chat
+          </button>
+          <button
+            type="button"
+            disabled={chatMode === "query"}
+            onClick={() => {
+              setChatMode("query");
+              setHasChanges(true);
+            }}
+            className="transition-bg duration-200 px-6 py-1 text-md text-white/60 disabled:text-white bg-transparent disabled:bg-[#687280] rounded-md"
+          >
+            Query
+          </button>
+        </div>
+        <p className="text-sm text-white/60">
+          {chatMode === "chat" ? (
+            <>
+              <b>Chat</b> will provide answers with the LLM's general knowledge{" "}
+              <i className="font-semibold">and</i> document context that is
+              found.
+            </>
+          ) : (
+            <>
+              <b>Query</b> will provide answers{" "}
+              <i className="font-semibold">only</i> if document context is
+              found.
+            </>
+          )}
+        </p>
+      </div>
+    </div>
+  );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx
index 7affa88fc..3ef7bb7ac 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx
@@ -16,10 +16,7 @@ export default function ChatModelSelection({
     return (
       <div>
         <div className="flex flex-col">
-          <label
-            htmlFor="name"
-            className="block text-sm font-medium text-white"
-          >
+          <label htmlFor="name" className="block input-label">
             Chat model
           </label>
           <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
@@ -44,7 +41,7 @@ export default function ChatModelSelection({
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           Chat model{" "}
           <span className="font-normal">({settings?.LLMProvider})</span>
         </label>
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/index.jsx
index 63b309ad6..7ac0c10a3 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/index.jsx
@@ -4,7 +4,7 @@ export default function ChatPromptSettings({ workspace, setHasChanges }) {
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           Prompt
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatTemperatureSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatTemperatureSettings/index.jsx
index 563211629..bd2178a67 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatTemperatureSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatTemperatureSettings/index.jsx
@@ -16,7 +16,7 @@ export default function ChatTemperatureSettings({
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           LLM Temperature
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
index d1f5a5014..3004b871c 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
@@ -7,6 +7,7 @@ import ChatModelSelection from "./ChatModelSelection";
 import ChatHistorySettings from "./ChatHistorySettings";
 import ChatPromptSettings from "./ChatPromptSettings";
 import ChatTemperatureSettings from "./ChatTemperatureSettings";
+import ChatModeSelection from "./ChatModeSelection";
 
 export default function ChatSettings({ workspace }) {
   const [settings, setSettings] = useState({});
@@ -48,6 +49,7 @@ export default function ChatSettings({ workspace }) {
       onSubmit={handleUpdate}
       className="w-1/2 flex flex-col gap-y-6"
     >
+      <ChatModeSelection workspace={workspace} setHasChanges={setHasChanges} />
       <ChatModelSelection
         settings={settings}
         workspace={workspace}
diff --git a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/SuggestedChatMessages/index.jsx b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/SuggestedChatMessages/index.jsx
index 0713f029e..12bc2e97d 100644
--- a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/SuggestedChatMessages/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/SuggestedChatMessages/index.jsx
@@ -91,9 +91,7 @@ export default function SuggestedChatMessages({ slug }) {
   if (loading)
     return (
       <div className="flex flex-col">
-        <label className="block text-sm font-medium text-white">
-          Suggested Chat Messages
-        </label>
+        <label className="block input-label">Suggested Chat Messages</label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
           Customize the messages that will be suggested to your workspace users.
         </p>
@@ -105,9 +103,7 @@ export default function SuggestedChatMessages({ slug }) {
   return (
     <div className="w-screen">
       <div className="flex flex-col">
-        <label className="block text-sm font-medium text-white">
-          Suggested Chat Messages
-        </label>
+        <label className="block input-label">Suggested Chat Messages</label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
           Customize the messages that will be suggested to your workspace users.
         </p>
diff --git a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/VectorCount/index.jsx b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/VectorCount/index.jsx
index 898ab7964..9a882875f 100644
--- a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/VectorCount/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/VectorCount/index.jsx
@@ -16,7 +16,7 @@ export default function VectorCount({ reload, workspace }) {
   if (totalVectors === null)
     return (
       <div>
-        <h3 className="text-white text-sm font-semibold">Number of vectors</h3>
+        <h3 className="input-label">Number of vectors</h3>
         <p className="text-white text-opacity-60 text-xs font-medium py-1">
           Total number of vectors in your vector database.
         </p>
@@ -27,7 +27,7 @@ export default function VectorCount({ reload, workspace }) {
     );
   return (
     <div>
-      <h3 className="text-white text-sm font-semibold">Number of vectors</h3>
+      <h3 className="input-label">Number of vectors</h3>
       <p className="text-white text-opacity-60 text-xs font-medium py-1">
         Total number of vectors in your vector database.
       </p>
diff --git a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/WorkspaceName/index.jsx b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/WorkspaceName/index.jsx
index 8b75977b7..b7c7f30f1 100644
--- a/frontend/src/pages/WorkspaceSettings/GeneralAppearance/WorkspaceName/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/GeneralAppearance/WorkspaceName/index.jsx
@@ -2,7 +2,7 @@ export default function WorkspaceName({ workspace, setHasChanges }) {
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           Workspace Name
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/DocumentSimilarityThreshold/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/DocumentSimilarityThreshold/index.jsx
index e627962ee..122e1144b 100644
--- a/frontend/src/pages/WorkspaceSettings/VectorDatabase/DocumentSimilarityThreshold/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/DocumentSimilarityThreshold/index.jsx
@@ -5,7 +5,7 @@ export default function DocumentSimilarityThreshold({
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           Document similarity threshold
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/MaxContextSnippets/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/MaxContextSnippets/index.jsx
index ba3f4c4fd..504779066 100644
--- a/frontend/src/pages/WorkspaceSettings/VectorDatabase/MaxContextSnippets/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/MaxContextSnippets/index.jsx
@@ -2,7 +2,7 @@ export default function MaxContextSnippets({ workspace, setHasChanges }) {
   return (
     <div>
       <div className="flex flex-col">
-        <label htmlFor="name" className="block text-sm font-medium text-white">
+        <label htmlFor="name" className="block input-label">
           Max Context Snippets
         </label>
         <p className="text-white text-opacity-60 text-xs font-medium py-1.5">
diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/VectorDBIdentifier/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/VectorDBIdentifier/index.jsx
index c57e7d4d8..9140d7fc0 100644
--- a/frontend/src/pages/WorkspaceSettings/VectorDatabase/VectorDBIdentifier/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/VectorDBIdentifier/index.jsx
@@ -1,13 +1,9 @@
 export default function VectorDBIdentifier({ workspace }) {
   return (
     <div>
-      <h3 className="text-white text-sm font-semibold">
-        Vector database identifier
-      </h3>
-      <p className="text-white text-opacity-60 text-xs font-medium py-1"> </p>
-      <p className="text-white text-opacity-60 text-sm font-medium">
-        {workspace?.slug}
-      </p>
+      <h3 className="input-label">Vector database identifier</h3>
+      <p className="text-white/60 text-xs font-medium py-1"> </p>
+      <p className="text-white/60 text-sm">{workspace?.slug}</p>
     </div>
   );
 }
diff --git a/server/endpoints/chat.js b/server/endpoints/chat.js
index 756944bea..a08b36e24 100644
--- a/server/endpoints/chat.js
+++ b/server/endpoints/chat.js
@@ -1,14 +1,10 @@
 const { v4: uuidv4 } = require("uuid");
 const { reqBody, userFromSession, multiUserMode } = require("../utils/http");
-const { Workspace } = require("../models/workspace");
 const { validatedRequest } = require("../utils/middleware/validatedRequest");
 const { WorkspaceChats } = require("../models/workspaceChats");
 const { SystemSettings } = require("../models/systemSettings");
 const { Telemetry } = require("../models/telemetry");
-const {
-  streamChatWithWorkspace,
-  VALID_CHAT_MODE,
-} = require("../utils/chats/stream");
+const { streamChatWithWorkspace } = require("../utils/chats/stream");
 const {
   ROLES,
   flexUserRoleValid,
@@ -16,6 +12,7 @@ const {
 const { EventLogs } = require("../models/eventLogs");
 const {
   validWorkspaceAndThreadSlug,
+  validWorkspaceSlug,
 } = require("../utils/middleware/validWorkspace");
 const { writeResponseChunk } = require("../utils/helpers/chat/responses");
 
@@ -24,32 +21,21 @@ function chatEndpoints(app) {
 
   app.post(
     "/workspace/:slug/stream-chat",
-    [validatedRequest, flexUserRoleValid([ROLES.all])],
+    [validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug],
     async (request, response) => {
       try {
         const user = await userFromSession(request, response);
-        const { slug } = request.params;
-        const { message, mode = "query" } = reqBody(request);
+        const { message } = reqBody(request);
+        const workspace = response.locals.workspace;
 
-        const workspace = multiUserMode(response)
-          ? await Workspace.getWithUser(user, { slug })
-          : await Workspace.get({ slug });
-
-        if (!workspace) {
-          response.sendStatus(400).end();
-          return;
-        }
-
-        if (!message?.length || !VALID_CHAT_MODE.includes(mode)) {
+        if (!message?.length) {
           response.status(400).json({
             id: uuidv4(),
             type: "abort",
             textResponse: null,
             sources: [],
             close: true,
-            error: !message?.length
-              ? "Message is empty."
-              : `${mode} is not a valid mode.`,
+            error: !message?.length ? "Message is empty." : null,
           });
           return;
         }
@@ -95,7 +81,13 @@ function chatEndpoints(app) {
           }
         }
 
-        await streamChatWithWorkspace(response, workspace, message, mode, user);
+        await streamChatWithWorkspace(
+          response,
+          workspace,
+          message,
+          workspace?.chatMode,
+          user
+        );
         await Telemetry.sendTelemetry("sent_chat", {
           multiUserMode: multiUserMode(response),
           LLMSelection: process.env.LLM_PROVIDER || "openai",
@@ -137,20 +129,18 @@ function chatEndpoints(app) {
     async (request, response) => {
       try {
         const user = await userFromSession(request, response);
-        const { message, mode = "query" } = reqBody(request);
+        const { message } = reqBody(request);
         const workspace = response.locals.workspace;
         const thread = response.locals.thread;
 
-        if (!message?.length || !VALID_CHAT_MODE.includes(mode)) {
+        if (!message?.length) {
           response.status(400).json({
             id: uuidv4(),
             type: "abort",
             textResponse: null,
             sources: [],
             close: true,
-            error: !message?.length
-              ? "Message is empty."
-              : `${mode} is not a valid mode.`,
+            error: !message?.length ? "Message is empty." : null,
           });
           return;
         }
@@ -202,7 +192,7 @@ function chatEndpoints(app) {
           response,
           workspace,
           message,
-          mode,
+          workspace?.chatMode,
           user,
           thread
         );
diff --git a/server/models/workspace.js b/server/models/workspace.js
index 9cc142e74..92c2f9e36 100644
--- a/server/models/workspace.js
+++ b/server/models/workspace.js
@@ -18,6 +18,7 @@ const Workspace = {
     "similarityThreshold",
     "chatModel",
     "topN",
+    "chatMode",
   ],
 
   new: async function (name = null, creatorId = null) {
@@ -59,7 +60,7 @@ const Workspace = {
     try {
       const workspace = await prisma.workspaces.update({
         where: { id },
-        data,
+        data, // TODO: strict validation on writables here.
       });
       return { workspace, message: null };
     } catch (error) {
diff --git a/server/prisma/migrations/20240216214639_init/migration.sql b/server/prisma/migrations/20240216214639_init/migration.sql
new file mode 100644
index 000000000..368782bc9
--- /dev/null
+++ b/server/prisma/migrations/20240216214639_init/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "workspaces" ADD COLUMN "chatMode" TEXT DEFAULT 'chat';
diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma
index 55b469cfc..77b25c8de 100644
--- a/server/prisma/schema.prisma
+++ b/server/prisma/schema.prisma
@@ -98,6 +98,7 @@ model workspaces {
   similarityThreshold          Float?                         @default(0.25)
   chatModel                    String?
   topN                         Int?                           @default(4)
+  chatMode                     String?                        @default("chat")
   workspace_users              workspace_users[]
   documents                    workspace_documents[]
   workspace_suggested_messages workspace_suggested_messages[]