mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-04-23 13:08:11 +00:00
[FEAT] create custom prompt suggestions per workspace (#664)
* create custom suggested chat messages per workspace * update how suggestedChats are passed to chat window * update mobile styles * update edit change handler --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
2bc11d3f1a
commit
608f28d745
12 changed files with 483 additions and 32 deletions
|
@ -41,6 +41,7 @@ const DataConnectors = lazy(
|
||||||
const DataConnectorSetup = lazy(
|
const DataConnectorSetup = lazy(
|
||||||
() => import("@/pages/GeneralSettings/DataConnectors/Connectors")
|
() => import("@/pages/GeneralSettings/DataConnectors/Connectors")
|
||||||
);
|
);
|
||||||
|
const WorkspaceSettings = lazy(() => import("@/pages/WorkspaceSettings"));
|
||||||
const EmbedConfigSetup = lazy(
|
const EmbedConfigSetup = lazy(
|
||||||
() => import("@/pages/GeneralSettings/EmbedConfigs")
|
() => import("@/pages/GeneralSettings/EmbedConfigs")
|
||||||
);
|
);
|
||||||
|
@ -62,6 +63,10 @@ export default function App() {
|
||||||
<Route path="/accept-invite/:code" element={<InvitePage />} />
|
<Route path="/accept-invite/:code" element={<InvitePage />} />
|
||||||
|
|
||||||
{/* Admin */}
|
{/* Admin */}
|
||||||
|
<Route
|
||||||
|
path="/workspace/:slug/settings"
|
||||||
|
element={<PrivateRoute Component={WorkspaceSettings} />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/settings/llm-preference"
|
path="/settings/llm-preference"
|
||||||
element={<AdminRoute Component={GeneralLLMPreference} />}
|
element={<AdminRoute Component={GeneralLLMPreference} />}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import PreLoader from "../../../Preloader";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import showToast from "../../../../utils/toast";
|
import showToast from "../../../../utils/toast";
|
||||||
import ChatModelPreference from "./ChatModelPreference";
|
import ChatModelPreference from "./ChatModelPreference";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
// Ensure that a type is correct before sending the body
|
// Ensure that a type is correct before sending the body
|
||||||
// to the backend.
|
// to the backend.
|
||||||
|
@ -313,6 +314,13 @@ export default function WorkspaceSettings({ active, workspace, settings }) {
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-4 w-full flex justify-start">
|
||||||
|
<Link to={paths.workspace.additionalSettings(workspace.slug)}>
|
||||||
|
<a className="underline text-white/60 text-sm font-medium hover:text-sky-600">
|
||||||
|
View additional settings
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ManageWorkspace from "../../../Modals/MangeWorkspace";
|
||||||
import { ArrowDown } from "@phosphor-icons/react";
|
import { ArrowDown } from "@phosphor-icons/react";
|
||||||
import debounce from "lodash.debounce";
|
import debounce from "lodash.debounce";
|
||||||
|
|
||||||
export default function ChatHistory({ history = [], workspace }) {
|
export default function ChatHistory({ history = [], workspace, sendCommand }) {
|
||||||
const replyRef = useRef(null);
|
const replyRef = useRef(null);
|
||||||
const { showing, showModal, hideModal } = useManageWorkspaceModal();
|
const { showing, showModal, hideModal } = useManageWorkspaceModal();
|
||||||
const [isAtBottom, setIsAtBottom] = useState(true);
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||||
|
@ -46,25 +46,31 @@ export default function ChatHistory({ history = [], workspace }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSendSuggestedMessage = (heading, message) => {
|
||||||
|
sendCommand(`${heading} ${message}`, true);
|
||||||
|
};
|
||||||
|
|
||||||
if (history.length === 0) {
|
if (history.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full md:mt-0 pb-48 w-full justify-end items-center">
|
<div className="flex flex-col h-full md:mt-0 pb-44 md:pb-40 w-full justify-end items-center">
|
||||||
<div className="flex flex-col items-start">
|
<div className="flex flex-col items-center md:items-start md:max-w-[600px] w-full px-4">
|
||||||
<p className="text-white/60 text-lg font-base py-4">
|
<p className="text-white/60 text-lg font-base py-4">
|
||||||
Welcome to your new workspace.
|
Welcome to your new workspace.
|
||||||
</p>
|
</p>
|
||||||
<div className="w-full text-center">
|
<p className="w-full items-center text-white/60 text-lg font-base flex flex-col md:flex-row gap-x-1">
|
||||||
<p className="text-white/60 text-lg font-base inline-grid md:inline-flex items-center gap-x-2">
|
To get started either{" "}
|
||||||
To get started either{" "}
|
<span
|
||||||
<span
|
className="underline font-medium cursor-pointer"
|
||||||
className="underline font-medium cursor-pointer"
|
onClick={showModal}
|
||||||
onClick={showModal}
|
>
|
||||||
>
|
upload a document
|
||||||
upload a document
|
</span>
|
||||||
</span>
|
or <b className="font-medium italic">send a chat.</b>
|
||||||
or <b className="font-medium italic">send a chat.</b>
|
</p>
|
||||||
</p>
|
<WorkspaceChatSuggestions
|
||||||
</div>
|
suggestions={workspace?.suggestedMessages ?? []}
|
||||||
|
sendSuggestion={handleSendSuggestedMessage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showing && (
|
{showing && (
|
||||||
<ManageWorkspace
|
<ManageWorkspace
|
||||||
|
@ -134,3 +140,21 @@ export default function ChatHistory({ history = [], workspace }) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function WorkspaceChatSuggestions({ suggestions = [], sendSuggestion }) {
|
||||||
|
if (suggestions.length === 0) return null;
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-white/60 text-xs mt-10 w-full justify-center">
|
||||||
|
{suggestions.map((suggestion, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
className="text-left p-2.5 border rounded-xl border-white/20 bg-sidebar hover:bg-workspace-item-selected-gradient"
|
||||||
|
onClick={() => sendSuggestion(suggestion.heading, suggestion.message)}
|
||||||
|
>
|
||||||
|
<p className="font-semibold">{suggestion.heading}</p>
|
||||||
|
<p>{suggestion.message}</p>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -97,7 +97,11 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
||||||
>
|
>
|
||||||
{isMobile && <SidebarMobileHeader />}
|
{isMobile && <SidebarMobileHeader />}
|
||||||
<div className="flex flex-col h-full w-full md:mt-0 mt-[40px]">
|
<div className="flex flex-col h-full w-full md:mt-0 mt-[40px]">
|
||||||
<ChatHistory history={chatHistory} workspace={workspace} />
|
<ChatHistory
|
||||||
|
history={chatHistory}
|
||||||
|
workspace={workspace}
|
||||||
|
sendCommand={sendCommand}
|
||||||
|
/>
|
||||||
<PromptInput
|
<PromptInput
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
message={message}
|
message={message}
|
||||||
|
|
|
@ -168,6 +168,42 @@ const Workspace = {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return { response, data };
|
return { response, data };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getSuggestedMessages: async function (slug) {
|
||||||
|
return await fetch(`${API_BASE}/workspace/${slug}/suggested-messages`, {
|
||||||
|
method: "GET",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not fetch suggested messages.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => res.suggestedMessages)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setSuggestedMessages: async function (slug, messages) {
|
||||||
|
return fetch(`${API_BASE}/workspace/${slug}/suggested-messages`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
body: JSON.stringify({ messages }),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(
|
||||||
|
res.statusText || "Error setting suggested messages."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { success: true, ...res.json() };
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return { success: false, error: e.message };
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Workspace;
|
export default Workspace;
|
||||||
|
|
|
@ -27,7 +27,11 @@ function ShowWorkspaceChat() {
|
||||||
async function getWorkspace() {
|
async function getWorkspace() {
|
||||||
if (!slug) return;
|
if (!slug) return;
|
||||||
const _workspace = await Workspace.bySlug(slug);
|
const _workspace = await Workspace.bySlug(slug);
|
||||||
setWorkspace(_workspace);
|
const suggestedMessages = await Workspace.getSuggestedMessages(slug);
|
||||||
|
setWorkspace({
|
||||||
|
..._workspace,
|
||||||
|
suggestedMessages,
|
||||||
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
getWorkspace();
|
getWorkspace();
|
||||||
|
|
208
frontend/src/pages/WorkspaceSettings/index.jsx
Normal file
208
frontend/src/pages/WorkspaceSettings/index.jsx
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { isMobile } from "react-device-detect";
|
||||||
|
import showToast from "@/utils/toast";
|
||||||
|
import { ArrowUUpLeft, Plus, X } from "@phosphor-icons/react";
|
||||||
|
import Workspace from "@/models/workspace";
|
||||||
|
import paths from "@/utils/paths";
|
||||||
|
|
||||||
|
export default function WorkspaceSettings() {
|
||||||
|
const [hasChanges, setHasChanges] = useState(false);
|
||||||
|
const [workspace, setWorkspace] = useState(null);
|
||||||
|
const [suggestedMessages, setSuggestedMessages] = useState([]);
|
||||||
|
const [editingIndex, setEditingIndex] = useState(-1);
|
||||||
|
const [newMessage, setNewMessage] = useState({ heading: "", message: "" });
|
||||||
|
const { slug } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchWorkspace() {
|
||||||
|
if (!slug) return;
|
||||||
|
const workspace = await Workspace.bySlug(slug);
|
||||||
|
const suggestedMessages = await Workspace.getSuggestedMessages(slug);
|
||||||
|
setWorkspace(workspace);
|
||||||
|
setSuggestedMessages(suggestedMessages);
|
||||||
|
}
|
||||||
|
fetchWorkspace();
|
||||||
|
}, [slug]);
|
||||||
|
|
||||||
|
const handleSaveSuggestedMessages = async () => {
|
||||||
|
const validMessages = suggestedMessages.filter(
|
||||||
|
(msg) =>
|
||||||
|
msg?.heading?.trim()?.length > 0 || msg?.message?.trim()?.length > 0
|
||||||
|
);
|
||||||
|
const { success, error } = await Workspace.setSuggestedMessages(
|
||||||
|
slug,
|
||||||
|
validMessages
|
||||||
|
);
|
||||||
|
if (!success) {
|
||||||
|
showToast(`Failed to update welcome messages: ${error}`, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showToast("Successfully updated welcome messages.", "success");
|
||||||
|
setHasChanges(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addMessage = () => {
|
||||||
|
setEditingIndex(-1);
|
||||||
|
if (suggestedMessages.length >= 4) {
|
||||||
|
showToast("Maximum of 4 messages allowed.", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultMessage = {
|
||||||
|
heading: "Explain to me",
|
||||||
|
message: "the benefits of AnythingLLM",
|
||||||
|
};
|
||||||
|
setNewMessage(defaultMessage);
|
||||||
|
setSuggestedMessages([...suggestedMessages, { ...defaultMessage }]);
|
||||||
|
setHasChanges(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeMessage = (index) => {
|
||||||
|
const messages = [...suggestedMessages];
|
||||||
|
messages.splice(index, 1);
|
||||||
|
setSuggestedMessages(messages);
|
||||||
|
setHasChanges(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startEditing = (index) => {
|
||||||
|
setEditingIndex(index);
|
||||||
|
setNewMessage({ ...suggestedMessages[index] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveMessage = (index) => {
|
||||||
|
removeMessage(index);
|
||||||
|
setEditingIndex(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEditChange = (e) => {
|
||||||
|
const updatedNewMessage = {
|
||||||
|
...newMessage,
|
||||||
|
[e.target.name]: e.target.value,
|
||||||
|
};
|
||||||
|
setNewMessage(updatedNewMessage);
|
||||||
|
const updatedMessages = suggestedMessages.map((message, index) => {
|
||||||
|
if (index === editingIndex) {
|
||||||
|
return { ...message, [e.target.name]: e.target.value };
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
});
|
||||||
|
|
||||||
|
setSuggestedMessages(updatedMessages);
|
||||||
|
setHasChanges(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
|
<a
|
||||||
|
href={paths.workspace.chat(slug)}
|
||||||
|
className="absolute top-2 left-2 md:top-16 md:left-10 transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border z-10"
|
||||||
|
>
|
||||||
|
<ArrowUUpLeft className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
|
className="transition-all duration-500 relative md:ml-[16px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
||||||
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
|
<div className="items-center flex gap-x-4">
|
||||||
|
<p className="text-2xl font-semibold text-white">
|
||||||
|
Workspace Settings ({workspace?.name})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-base text-white text-opacity-60">
|
||||||
|
Customize your workspace.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="my-6">
|
||||||
|
<div className="flex flex-col gap-y-2">
|
||||||
|
<h2 className="leading-tight font-medium text-white">
|
||||||
|
Suggested Chat Messages
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm font-base text-white/60">
|
||||||
|
Customize the messages that will be suggested to your workspace
|
||||||
|
users.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-white/60 text-xs mt-6 w-full justify-center max-w-[600px]">
|
||||||
|
{suggestedMessages.map((suggestion, index) => (
|
||||||
|
<div key={index} className="relative w-full">
|
||||||
|
<button
|
||||||
|
className="transition-all duration-300 absolute z-10 text-neutral-700 bg-white rounded-full hover:bg-zinc-600 hover:border-zinc-600 hover:text-white border-transparent border shadow-lg ml-2"
|
||||||
|
style={{
|
||||||
|
top: -8,
|
||||||
|
left: 265,
|
||||||
|
}}
|
||||||
|
onClick={() => handleRemoveMessage(index)}
|
||||||
|
>
|
||||||
|
<X className="m-[1px]" size={20} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => startEditing(index)}
|
||||||
|
className={`text-left p-2.5 border rounded-xl w-full border-white/20 bg-sidebar hover:bg-workspace-item-selected-gradient ${
|
||||||
|
editingIndex === index ? "border-sky-400" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<p className="font-semibold">{suggestion.heading}</p>
|
||||||
|
<p>{suggestion.message}</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{editingIndex >= 0 && (
|
||||||
|
<div className="flex flex-col gap-y-4 mr-2 mt-8">
|
||||||
|
<div className="w-1/2">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-2">
|
||||||
|
Heading
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
placeholder="Message heading"
|
||||||
|
className=" bg-sidebar text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||||
|
value={newMessage.heading}
|
||||||
|
name="heading"
|
||||||
|
onChange={onEditChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-1/2">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-2">
|
||||||
|
Message
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
placeholder="Message"
|
||||||
|
className="bg-sidebar text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||||
|
value={newMessage.message}
|
||||||
|
name="message"
|
||||||
|
onChange={onEditChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{suggestedMessages.length < 4 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addMessage}
|
||||||
|
className="flex gap-x-2 items-center justify-center mt-6 text-white text-sm hover:text-sky-400 transition-all duration-300"
|
||||||
|
>
|
||||||
|
Add new message <Plus className="" size={24} weight="fill" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasChanges && (
|
||||||
|
<div className="flex justify-center py-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
||||||
|
onClick={handleSaveSuggestedMessages}
|
||||||
|
>
|
||||||
|
Save Messages
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -55,6 +55,9 @@ export default {
|
||||||
chat: (slug) => {
|
chat: (slug) => {
|
||||||
return `/workspace/${slug}`;
|
return `/workspace/${slug}`;
|
||||||
},
|
},
|
||||||
|
additionalSettings: (slug) => {
|
||||||
|
return `/workspace/${slug}/settings`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
apiDocs: () => {
|
apiDocs: () => {
|
||||||
return `${API_BASE}/docs`;
|
return `${API_BASE}/docs`;
|
||||||
|
|
|
@ -17,6 +17,9 @@ const {
|
||||||
flexUserRoleValid,
|
flexUserRoleValid,
|
||||||
ROLES,
|
ROLES,
|
||||||
} = require("../utils/middleware/multiUserProtected");
|
} = require("../utils/middleware/multiUserProtected");
|
||||||
|
const {
|
||||||
|
WorkspaceSuggestedMessages,
|
||||||
|
} = require("../models/workspacesSuggestedMessages");
|
||||||
const { handleUploads } = setupMulter();
|
const { handleUploads } = setupMulter();
|
||||||
|
|
||||||
function workspaceEndpoints(app) {
|
function workspaceEndpoints(app) {
|
||||||
|
@ -283,6 +286,53 @@ function workspaceEndpoints(app) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/workspace/:slug/suggested-messages",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||||
|
async function (request, response) {
|
||||||
|
try {
|
||||||
|
const { slug } = request.params;
|
||||||
|
const suggestedMessages =
|
||||||
|
await WorkspaceSuggestedMessages.getMessages(slug);
|
||||||
|
response.status(200).json({ success: true, suggestedMessages });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching suggested messages:", error);
|
||||||
|
response
|
||||||
|
.status(500)
|
||||||
|
.json({ success: false, message: "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
"/workspace/:slug/suggested-messages",
|
||||||
|
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
|
||||||
|
async (request, response) => {
|
||||||
|
try {
|
||||||
|
const { messages = [] } = reqBody(request);
|
||||||
|
const { slug } = request.params;
|
||||||
|
if (!Array.isArray(messages)) {
|
||||||
|
return response.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: "Invalid message format. Expected an array of messages.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await WorkspaceSuggestedMessages.saveAll(messages, slug);
|
||||||
|
return response.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: "Suggested messages saved successfully.",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing the suggested messages:", error);
|
||||||
|
response.status(500).json({
|
||||||
|
success: true,
|
||||||
|
message: "Error saving the suggested messages.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { workspaceEndpoints };
|
module.exports = { workspaceEndpoints };
|
||||||
|
|
83
server/models/workspacesSuggestedMessages.js
Normal file
83
server/models/workspacesSuggestedMessages.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
const prisma = require("../utils/prisma");
|
||||||
|
|
||||||
|
const WorkspaceSuggestedMessages = {
|
||||||
|
get: async function (clause = {}) {
|
||||||
|
try {
|
||||||
|
const message = await prisma.workspace_suggested_messages.findFirst({
|
||||||
|
where: clause,
|
||||||
|
});
|
||||||
|
return message || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
where: async function (clause = {}, limit) {
|
||||||
|
try {
|
||||||
|
const messages = await prisma.workspace_suggested_messages.findMany({
|
||||||
|
where: clause,
|
||||||
|
take: limit || undefined,
|
||||||
|
});
|
||||||
|
return messages;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveAll: async function (messages, workspaceSlug) {
|
||||||
|
try {
|
||||||
|
const workspace = await prisma.workspaces.findUnique({
|
||||||
|
where: { slug: workspaceSlug },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!workspace) throw new Error("Workspace not found");
|
||||||
|
|
||||||
|
// Delete all existing messages for the workspace
|
||||||
|
await prisma.workspace_suggested_messages.deleteMany({
|
||||||
|
where: { workspaceId: workspace.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create new messages
|
||||||
|
// We create each message individually because prisma
|
||||||
|
// with sqlite does not support createMany()
|
||||||
|
for (const message of messages) {
|
||||||
|
await prisma.workspace_suggested_messages.create({
|
||||||
|
data: {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
heading: message.heading,
|
||||||
|
message: message.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to save all messages", error.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getMessages: async function (workspaceSlug) {
|
||||||
|
try {
|
||||||
|
const workspace = await prisma.workspaces.findUnique({
|
||||||
|
where: { slug: workspaceSlug },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!workspace) throw new Error("Workspace not found");
|
||||||
|
|
||||||
|
const messages = await prisma.workspace_suggested_messages.findMany({
|
||||||
|
where: { workspaceId: workspace.id },
|
||||||
|
orderBy: { createdAt: "asc" },
|
||||||
|
});
|
||||||
|
|
||||||
|
return messages.map((msg) => ({
|
||||||
|
heading: msg.heading,
|
||||||
|
message: msg.message,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get all messages", error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.WorkspaceSuggestedMessages = WorkspaceSuggestedMessages;
|
13
server/prisma/migrations/20240206181106_init/migration.sql
Normal file
13
server/prisma/migrations/20240206181106_init/migration.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "workspace_suggested_messages" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"workspaceId" INTEGER NOT NULL,
|
||||||
|
"heading" TEXT NOT NULL,
|
||||||
|
"message" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT "workspace_suggested_messages_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "workspaces" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "workspace_suggested_messages_workspaceId_idx" ON "workspace_suggested_messages"("workspaceId");
|
|
@ -85,21 +85,34 @@ model welcome_messages {
|
||||||
}
|
}
|
||||||
|
|
||||||
model workspaces {
|
model workspaces {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
slug String @unique
|
slug String @unique
|
||||||
vectorTag String?
|
vectorTag String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
openAiTemp Float?
|
openAiTemp Float?
|
||||||
openAiHistory Int @default(20)
|
openAiHistory Int @default(20)
|
||||||
lastUpdatedAt DateTime @default(now())
|
lastUpdatedAt DateTime @default(now())
|
||||||
openAiPrompt String?
|
openAiPrompt String?
|
||||||
similarityThreshold Float? @default(0.25)
|
similarityThreshold Float? @default(0.25)
|
||||||
chatModel String?
|
chatModel String?
|
||||||
topN Int? @default(4)
|
topN Int? @default(4)
|
||||||
workspace_users workspace_users[]
|
workspace_users workspace_users[]
|
||||||
documents workspace_documents[]
|
documents workspace_documents[]
|
||||||
embed_configs embed_configs[]
|
workspace_suggested_messages workspace_suggested_messages[]
|
||||||
|
embed_configs embed_configs[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model workspace_suggested_messages {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
workspaceId Int
|
||||||
|
heading String
|
||||||
|
message String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastUpdatedAt DateTime @default(now())
|
||||||
|
workspace workspaces @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([workspaceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model workspace_chats {
|
model workspace_chats {
|
||||||
|
|
Loading…
Add table
Reference in a new issue