diff --git a/frontend/src/models/workspace.js b/frontend/src/models/workspace.js index ae2cd5590a8ea329189386fbb5284c9364960930..7bc95ef8c81bc5680a273b96d80b10d997214d50 100644 --- a/frontend/src/models/workspace.js +++ b/frontend/src/models/workspace.js @@ -173,6 +173,14 @@ const Workspace = { return result; }, + wipeVectorDb: async function (slug) { + return await fetch(`${API_BASE}/workspace/${slug}/reset-vector-db`, { + method: "DELETE", + headers: baseHeaders(), + }) + .then((res) => res.ok) + .catch(() => false); + }, uploadFile: async function (slug, formData) { const response = await fetch(`${API_BASE}/workspace/${slug}/upload`, { method: "POST", diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/ResetDatabase/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/ResetDatabase/index.jsx new file mode 100644 index 0000000000000000000000000000000000000000..e3e2730cdb94f843f3a951d253d7559846a8544f --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/ResetDatabase/index.jsx @@ -0,0 +1,42 @@ +import { useState } from "react"; +import Workspace from "@/models/workspace"; +import showToast from "@/utils/toast"; + +export default function ResetDatabase({ workspace }) { + const [deleting, setDeleting] = useState(false); + + const resetVectorDatabase = async () => { + if ( + !window.confirm( + `You are about to reset this workspace's vector database. This will remove all vector embeddings currently embedded.\n\nThe original source files will remain untouched. This action is irreversible.` + ) + ) + return false; + + setDeleting(true); + const success = await Workspace.wipeVectorDb(workspace.slug); + if (!success) { + showToast("Workspace vector database could not be reset!", "error", { + clear: true, + }); + setDeleting(false); + return; + } + + showToast("Workspace vector database was reset!", "success", { + clear: true, + }); + setDeleting(false); + }; + + return ( + <button + disabled={deleting} + onClick={resetVectorDatabase} + type="button" + className="border-none w-fit transition-all duration-300 border border-transparent rounded-lg whitespace-nowrap text-sm px-5 py-2.5 focus:z-10 bg-red-500/25 text-red-200 hover:text-white hover:bg-red-600 disabled:bg-red-600 disabled:text-red-200 disabled:animate-pulse" + > + {deleting ? "Clearing vectors..." : "Reset Workspace Vector Database"} + </button> + ); +} diff --git a/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx b/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx index ca8de006b813a26d083fd4bcf72f19d4a5212e3b..0a9a0e87ae1e30dbe373db710df12e5d9291a14a 100644 --- a/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/VectorDatabase/index.jsx @@ -5,6 +5,7 @@ import { useRef, useState } from "react"; import VectorDBIdentifier from "./VectorDBIdentifier"; import MaxContextSnippets from "./MaxContextSnippets"; import DocumentSimilarityThreshold from "./DocumentSimilarityThreshold"; +import ResetDatabase from "./ResetDatabase"; export default function VectorDatabase({ workspace }) { const [hasChanges, setHasChanges] = useState(false); @@ -43,6 +44,7 @@ export default function VectorDatabase({ workspace }) { workspace={workspace} setHasChanges={setHasChanges} /> + <ResetDatabase workspace={workspace} /> {hasChanges && ( <button type="submit" diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index 1c87dc366965a0dfe20b7afdf0e1edc67a7858b4..e9df2613c9d534513e3df4660043bcb31f684b27 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -266,6 +266,47 @@ function workspaceEndpoints(app) { } ); + app.delete( + "/workspace/:slug/reset-vector-db", + [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], + async (request, response) => { + try { + const { slug = "" } = request.params; + const user = await userFromSession(request, response); + const VectorDb = getVectorDbClass(); + const workspace = multiUserMode(response) + ? await Workspace.getWithUser(user, { slug }) + : await Workspace.get({ slug }); + + if (!workspace) { + response.sendStatus(400).end(); + return; + } + + await DocumentVectors.deleteForWorkspace(workspace.id); + await Document.delete({ workspaceId: Number(workspace.id) }); + + await EventLogs.logEvent( + "workspace_vectors_reset", + { + workspaceName: workspace?.name || "Unknown Workspace", + }, + response.locals?.user?.id + ); + + try { + await VectorDb["delete-namespace"]({ namespace: slug }); + } catch (e) { + console.error(e.message); + } + response.sendStatus(200).end(); + } catch (e) { + console.log(e.message, e); + response.sendStatus(500).end(); + } + } + ); + app.get( "/workspaces", [validatedRequest, flexUserRoleValid([ROLES.all])],