mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 20:50:09 +01:00
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render
This commit is contained in:
commit
fde4e5400f
@ -13,7 +13,9 @@ class RepoLoader {
|
|||||||
|
|
||||||
#validGithubUrl() {
|
#validGithubUrl() {
|
||||||
const UrlPattern = require("url-pattern");
|
const UrlPattern = require("url-pattern");
|
||||||
const pattern = new UrlPattern("https\\://github.com/(:author)/(:project)");
|
const pattern = new UrlPattern(
|
||||||
|
"https\\://github.com/(:author)/(:project(*))"
|
||||||
|
);
|
||||||
const match = pattern.match(this.repo);
|
const match = pattern.match(this.repo);
|
||||||
if (!match) return false;
|
if (!match) return false;
|
||||||
|
|
||||||
|
@ -21,7 +21,21 @@ class MimeDetector {
|
|||||||
// which has had this extension far before TS was invented. So need to force re-map this MIME map.
|
// which has had this extension far before TS was invented. So need to force re-map this MIME map.
|
||||||
this.lib.define(
|
this.lib.define(
|
||||||
{
|
{
|
||||||
"text/plain": ["ts", "py", "opts", "lock", "jsonl", "qml", "sh"],
|
"text/plain": [
|
||||||
|
"ts",
|
||||||
|
"py",
|
||||||
|
"opts",
|
||||||
|
"lock",
|
||||||
|
"jsonl",
|
||||||
|
"qml",
|
||||||
|
"sh",
|
||||||
|
"c",
|
||||||
|
"cs",
|
||||||
|
"h",
|
||||||
|
"js",
|
||||||
|
"lua",
|
||||||
|
"pas",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@ This folder of AnythingLLM contains the source code for how the embedded version
|
|||||||
The AnythingLLM Embedded chat widget allows you to expose a workspace and its embedded knowledge base as a chat bubble via a `<script>` or `<iframe>` element that you can embed in a website or HTML.
|
The AnythingLLM Embedded chat widget allows you to expose a workspace and its embedded knowledge base as a chat bubble via a `<script>` or `<iframe>` element that you can embed in a website or HTML.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- Users will _not_ be able to view or read context snippets like they can in the core AnythingLLM application
|
- Users will _not_ be able to view or read context snippets like they can in the core AnythingLLM application
|
||||||
- Users are assigned a random session ID that they use to persist a chat session.
|
- Users are assigned a random session ID that they use to persist a chat session.
|
||||||
- **Recommended** You can limit both the number of chats an embedding can process **and** per-session.
|
- **Recommended** You can limit both the number of chats an embedding can process **and** per-session.
|
||||||
@ -21,6 +22,7 @@ The AnythingLLM Embedded chat widget allows you to expose a workspace and its em
|
|||||||
_by using the AnythingLLM embedded chat widget you are responsible for securing and configuration of the embed as to not allow excessive chat model abuse of your instance_
|
_by using the AnythingLLM embedded chat widget you are responsible for securing and configuration of the embed as to not allow excessive chat model abuse of your instance_
|
||||||
|
|
||||||
### Developer Setup
|
### Developer Setup
|
||||||
|
|
||||||
- `cd embed` from the root of the repo
|
- `cd embed` from the root of the repo
|
||||||
- `yarn` to install all dev and script dependencies
|
- `yarn` to install all dev and script dependencies
|
||||||
- `yarn dev` to boot up an example HTML page to use the chat embed widget.
|
- `yarn dev` to boot up an example HTML page to use the chat embed widget.
|
||||||
@ -34,6 +36,7 @@ While in development mode (`yarn dev`) the script will rebuild on any changes to
|
|||||||
### `<script>` tag HTML embed
|
### `<script>` tag HTML embed
|
||||||
|
|
||||||
The primary way of embedding a workspace as a chat widget is via a simple `<script>`
|
The primary way of embedding a workspace as a chat widget is via a simple `<script>`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--
|
<!--
|
||||||
An example of a script tag embed
|
An example of a script tag embed
|
||||||
@ -44,13 +47,14 @@ REQUIRED data attributes:
|
|||||||
<script
|
<script
|
||||||
data-embed-id="5fc05aaf-2f2c-4c84-87a3-367a4692c1ee"
|
data-embed-id="5fc05aaf-2f2c-4c84-87a3-367a4692c1ee"
|
||||||
data-base-api-url="http://localhost:3001/api/embed"
|
data-base-api-url="http://localhost:3001/api/embed"
|
||||||
src="http://localhost:3000/embed/anythingllm-chat-widget.min.js">
|
src="http://localhost:3000/embed/anythingllm-chat-widget.min.js"
|
||||||
</script>
|
></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### `<script>` Customization Options
|
### `<script>` Customization Options
|
||||||
|
|
||||||
**LLM Overrides**
|
**LLM Overrides**
|
||||||
|
|
||||||
- `data-prompt` — Override the chat window with a custom system prompt. This is not visible to the user. If undefined it will use the embeds attached workspace system prompt.
|
- `data-prompt` — Override the chat window with a custom system prompt. This is not visible to the user. If undefined it will use the embeds attached workspace system prompt.
|
||||||
|
|
||||||
- `data-model` — Override the chat model used for responses. This must be a valid model string for your AnythingLLM LLM provider. If unset it will use the embeds attached workspace model selection or the system setting.
|
- `data-model` — Override the chat model used for responses. This must be a valid model string for your AnythingLLM LLM provider. If unset it will use the embeds attached workspace model selection or the system setting.
|
||||||
@ -58,6 +62,7 @@ REQUIRED data attributes:
|
|||||||
- `data-temperature` — Override the chat model temperature. This must be a valid value for your AnythingLLM LLM provider. If unset it will use the embeds attached workspace model temperature or the system setting.
|
- `data-temperature` — Override the chat model temperature. This must be a valid value for your AnythingLLM LLM provider. If unset it will use the embeds attached workspace model temperature or the system setting.
|
||||||
|
|
||||||
**Style Overrides**
|
**Style Overrides**
|
||||||
|
|
||||||
- `data-chat-icon` — The chat bubble icon show when chat is closed. Options are `plus`, `chatCircle`, `support`, `search2`, `search`, `magic`.
|
- `data-chat-icon` — The chat bubble icon show when chat is closed. Options are `plus`, `chatCircle`, `support`, `search2`, `search`, `magic`.
|
||||||
|
|
||||||
- `data-button-color` — The chat bubble background color shown when chat is closed. Value must be hex color code.
|
- `data-button-color` — The chat bubble background color shown when chat is closed. Value must be hex color code.
|
||||||
@ -78,15 +83,20 @@ REQUIRED data attributes:
|
|||||||
|
|
||||||
- `data-position` - Adjust the positioning of the embed chat widget and open chat button. Default `bottom-right`. Options are `bottom-right`, `bottom-left`, `top-right`, `top-left`.
|
- `data-position` - Adjust the positioning of the embed chat widget and open chat button. Default `bottom-right`. Options are `bottom-right`, `bottom-left`, `top-right`, `top-left`.
|
||||||
|
|
||||||
|
- `data-assistant-name` - Set the chat assistant name that appears above each chat message. Default `AnythingLLM Chat Assistant`
|
||||||
|
|
||||||
|
- `data-assistant-icon` - Set the icon of the chat assistant.
|
||||||
|
|
||||||
**Behavior Overrides**
|
**Behavior Overrides**
|
||||||
|
|
||||||
- `data-open-on-load` — Once loaded, open the chat as default. It can still be closed by the user.
|
- `data-open-on-load` — Once loaded, open the chat as default. It can still be closed by the user.
|
||||||
|
|
||||||
- `data-support-email` — Shows a support email that the user can used to draft an email via the "three dot" menu in the top right. Option will not appear if it is not set.
|
- `data-support-email` — Shows a support email that the user can used to draft an email via the "three dot" menu in the top right. Option will not appear if it is not set.
|
||||||
|
|
||||||
|
|
||||||
### `<iframe>` tag HTML embed
|
### `<iframe>` tag HTML embed
|
||||||
|
|
||||||
_work in progress_
|
_work in progress_
|
||||||
|
|
||||||
### `<iframe>` Customization Options
|
### `<iframe>` Customization Options
|
||||||
_work in progress_
|
|
||||||
|
_work in progress_
|
||||||
|
@ -19,7 +19,8 @@ const HistoricalMessage = forwardRef(
|
|||||||
<div
|
<div
|
||||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
||||||
>
|
>
|
||||||
AnythingLLM Chat Assistant
|
{embedderSettings.settings.assistantName ||
|
||||||
|
"Anything LLM Chat Assistant"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
@ -31,7 +32,7 @@ const HistoricalMessage = forwardRef(
|
|||||||
>
|
>
|
||||||
{role === "assistant" && (
|
{role === "assistant" && (
|
||||||
<img
|
<img
|
||||||
src={AnythingLLMIcon}
|
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||||
alt="Anything LLM Icon"
|
alt="Anything LLM Icon"
|
||||||
className="w-9 h-9 flex-shrink-0 ml-2 mt-2"
|
className="w-9 h-9 flex-shrink-0 ml-2 mt-2"
|
||||||
id="anything-llm-icon"
|
id="anything-llm-icon"
|
||||||
|
@ -13,7 +13,7 @@ const PromptReply = forwardRef(
|
|||||||
return (
|
return (
|
||||||
<div className={`flex items-start w-full h-fit justify-start`}>
|
<div className={`flex items-start w-full h-fit justify-start`}>
|
||||||
<img
|
<img
|
||||||
src={AnythingLLMIcon}
|
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||||
alt="Anything LLM Icon"
|
alt="Anything LLM Icon"
|
||||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||||
/>
|
/>
|
||||||
@ -33,7 +33,7 @@ const PromptReply = forwardRef(
|
|||||||
return (
|
return (
|
||||||
<div className={`flex items-end w-full h-fit justify-start`}>
|
<div className={`flex items-end w-full h-fit justify-start`}>
|
||||||
<img
|
<img
|
||||||
src={AnythingLLMIcon}
|
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||||
alt="Anything LLM Icon"
|
alt="Anything LLM Icon"
|
||||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||||
/>
|
/>
|
||||||
@ -60,7 +60,8 @@ const PromptReply = forwardRef(
|
|||||||
<div
|
<div
|
||||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
||||||
>
|
>
|
||||||
AnythingLLM Chat Assistant
|
{embedderSettings.settings.assistantName ||
|
||||||
|
"Anything LLM Chat Assistant"}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
key={uuid}
|
key={uuid}
|
||||||
@ -68,7 +69,7 @@ const PromptReply = forwardRef(
|
|||||||
className={`flex items-start w-full h-fit justify-start`}
|
className={`flex items-start w-full h-fit justify-start`}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={AnythingLLMIcon}
|
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||||
alt="Anything LLM Icon"
|
alt="Anything LLM Icon"
|
||||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||||
/>
|
/>
|
||||||
|
@ -21,6 +21,8 @@ const DEFAULT_SETTINGS = {
|
|||||||
sponsorText: "Powered by AnythingLLM", // default sponsor text
|
sponsorText: "Powered by AnythingLLM", // default sponsor text
|
||||||
sponsorLink: "https://useanything.com", // default sponsor link
|
sponsorLink: "https://useanything.com", // default sponsor link
|
||||||
position: "bottom-right", // position of chat button/window
|
position: "bottom-right", // position of chat button/window
|
||||||
|
assistantName: "AnythingLLM Chat Assistant", // default assistant name
|
||||||
|
assistantIcon: null, // default assistant icon
|
||||||
|
|
||||||
// behaviors
|
// behaviors
|
||||||
openOnLoad: "off", // or "on"
|
openOnLoad: "off", // or "on"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -173,6 +173,14 @@ const Workspace = {
|
|||||||
|
|
||||||
return result;
|
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) {
|
uploadFile: async function (slug, formData) {
|
||||||
const response = await fetch(`${API_BASE}/workspace/${slug}/upload`, {
|
const response = await fetch(`${API_BASE}/workspace/${slug}/upload`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -8,7 +8,11 @@ const TITLE = "Welcome to AnythingLLM";
|
|||||||
const DESCRIPTION = "Help us make AnythingLLM built for your needs. Optional.";
|
const DESCRIPTION = "Help us make AnythingLLM built for your needs. Optional.";
|
||||||
|
|
||||||
async function sendQuestionnaire({ email, useCase, comment }) {
|
async function sendQuestionnaire({ email, useCase, comment }) {
|
||||||
if (import.meta.env.DEV) return;
|
if (import.meta.env.DEV) {
|
||||||
|
console.log("sendQuestionnaire", { email, useCase, comment });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return fetch(`https://onboarding-wxich7363q-uc.a.run.app`, {
|
return fetch(`https://onboarding-wxich7363q-uc.a.run.app`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -38,9 +42,25 @@ export default function Survey({ setHeader, setForwardBtn, setBackBtn }) {
|
|||||||
navigate(paths.onboarding.createWorkspace());
|
navigate(paths.onboarding.createWorkspace());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (submitRef.current) {
|
|
||||||
submitRef.current.click();
|
if (!formRef.current) {
|
||||||
|
skipSurvey();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if any inputs are not empty. If that is the case, trigger form validation.
|
||||||
|
// via the requestSubmit() handler
|
||||||
|
const formData = new FormData(formRef.current);
|
||||||
|
if (
|
||||||
|
!!formData.get("email") ||
|
||||||
|
!!formData.get("use_case") ||
|
||||||
|
!!formData.get("comment")
|
||||||
|
) {
|
||||||
|
formRef.current.requestSubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipSurvey();
|
||||||
}
|
}
|
||||||
|
|
||||||
function skipSurvey() {
|
function skipSurvey() {
|
||||||
@ -116,26 +136,24 @@ export default function Survey({ setHeader, setForwardBtn, setBackBtn }) {
|
|||||||
<div className="mt-2 gap-y-3 flex flex-col">
|
<div className="mt-2 gap-y-3 flex flex-col">
|
||||||
<label
|
<label
|
||||||
className={`transition-all duration-300 w-full h-11 p-2.5 bg-white/10 rounded-lg flex justify-start items-center gap-2.5 cursor-pointer border border-transparent ${
|
className={`transition-all duration-300 w-full h-11 p-2.5 bg-white/10 rounded-lg flex justify-start items-center gap-2.5 cursor-pointer border border-transparent ${
|
||||||
selectedOption === "business"
|
selectedOption === "job" ? "border-white border-opacity-40" : ""
|
||||||
? "border-white border-opacity-40"
|
|
||||||
: ""
|
|
||||||
} hover:border-white/60`}
|
} hover:border-white/60`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="use_case"
|
name="use_case"
|
||||||
value={"business"}
|
value={"job"}
|
||||||
checked={selectedOption === "business"}
|
checked={selectedOption === "job"}
|
||||||
onChange={(e) => setSelectedOption(e.target.value)}
|
onChange={(e) => setSelectedOption(e.target.value)}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`w-4 h-4 rounded-full border-2 border-white mr-2 ${
|
className={`w-4 h-4 rounded-full border-2 border-white mr-2 ${
|
||||||
selectedOption === "business" ? "bg-white" : ""
|
selectedOption === "job" ? "bg-white" : ""
|
||||||
}`}
|
}`}
|
||||||
></div>
|
></div>
|
||||||
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
||||||
For my business
|
For work
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
@ -159,77 +177,7 @@ export default function Survey({ setHeader, setForwardBtn, setBackBtn }) {
|
|||||||
}`}
|
}`}
|
||||||
></div>
|
></div>
|
||||||
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
||||||
For personal use
|
For my personal use
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
className={`transition-all duration-300 w-full h-11 p-2.5 bg-white/10 rounded-lg flex justify-start items-center gap-2.5 cursor-pointer border border-transparent ${
|
|
||||||
selectedOption === "education"
|
|
||||||
? "border-white border-opacity-40"
|
|
||||||
: ""
|
|
||||||
} hover:border-white/60`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="use_case"
|
|
||||||
value={"education"}
|
|
||||||
checked={selectedOption === "education"}
|
|
||||||
onChange={(e) => setSelectedOption(e.target.value)}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={`w-4 h-4 rounded-full border-2 border-white mr-2 ${
|
|
||||||
selectedOption === "education" ? "bg-white" : ""
|
|
||||||
}`}
|
|
||||||
></div>
|
|
||||||
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
|
||||||
For my education
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
className={`transition-all duration-300 w-full h-11 p-2.5 bg-white/10 rounded-lg flex justify-start items-center gap-2.5 cursor-pointer border border-transparent ${
|
|
||||||
selectedOption === "side_hustle"
|
|
||||||
? "border-white border-opacity-40"
|
|
||||||
: ""
|
|
||||||
} hover:border-white/60`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="use_case"
|
|
||||||
value={"side_hustle"}
|
|
||||||
checked={selectedOption === "side_hustle"}
|
|
||||||
onChange={(e) => setSelectedOption(e.target.value)}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={`w-4 h-4 rounded-full border-2 border-white mr-2 ${
|
|
||||||
selectedOption === "side_hustle" ? "bg-white" : ""
|
|
||||||
}`}
|
|
||||||
></div>
|
|
||||||
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
|
||||||
For my side-hustle
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
className={`transition-all duration-300 w-full h-11 p-2.5 bg-white/10 rounded-lg flex justify-start items-center gap-2.5 cursor-pointer border border-transparent ${
|
|
||||||
selectedOption === "job" ? "border-white border-opacity-40" : ""
|
|
||||||
} hover:border-white/60`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="use_case"
|
|
||||||
value={"job"}
|
|
||||||
checked={selectedOption === "job"}
|
|
||||||
onChange={(e) => setSelectedOption(e.target.value)}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={`w-4 h-4 rounded-full border-2 border-white mr-2 ${
|
|
||||||
selectedOption === "job" ? "bg-white" : ""
|
|
||||||
}`}
|
|
||||||
></div>
|
|
||||||
<div className="text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight">
|
|
||||||
For my job
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
|
@ -15,6 +15,10 @@ const LLM_DEFAULT = {
|
|||||||
requiredConfig: [],
|
requiredConfig: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LLMS = [LLM_DEFAULT, ...AVAILABLE_LLM_PROVIDERS].filter(
|
||||||
|
(llm) => !DISABLED_PROVIDERS.includes(llm.value)
|
||||||
|
);
|
||||||
|
|
||||||
export default function WorkspaceLLMSelection({
|
export default function WorkspaceLLMSelection({
|
||||||
settings,
|
settings,
|
||||||
workspace,
|
workspace,
|
||||||
@ -27,9 +31,6 @@ export default function WorkspaceLLMSelection({
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
||||||
const searchInputRef = useRef(null);
|
const searchInputRef = useRef(null);
|
||||||
const LLMS = [LLM_DEFAULT, ...AVAILABLE_LLM_PROVIDERS].filter(
|
|
||||||
(llm) => !DISABLED_PROVIDERS.includes(llm.value)
|
|
||||||
);
|
|
||||||
|
|
||||||
function updateLLMChoice(selection) {
|
function updateLLMChoice(selection) {
|
||||||
setSearchQuery("");
|
setSearchQuery("");
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { useRef, useState } from "react";
|
|||||||
import VectorDBIdentifier from "./VectorDBIdentifier";
|
import VectorDBIdentifier from "./VectorDBIdentifier";
|
||||||
import MaxContextSnippets from "./MaxContextSnippets";
|
import MaxContextSnippets from "./MaxContextSnippets";
|
||||||
import DocumentSimilarityThreshold from "./DocumentSimilarityThreshold";
|
import DocumentSimilarityThreshold from "./DocumentSimilarityThreshold";
|
||||||
|
import ResetDatabase from "./ResetDatabase";
|
||||||
|
|
||||||
export default function VectorDatabase({ workspace }) {
|
export default function VectorDatabase({ workspace }) {
|
||||||
const [hasChanges, setHasChanges] = useState(false);
|
const [hasChanges, setHasChanges] = useState(false);
|
||||||
@ -43,6 +44,7 @@ export default function VectorDatabase({ workspace }) {
|
|||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
setHasChanges={setHasChanges}
|
setHasChanges={setHasChanges}
|
||||||
/>
|
/>
|
||||||
|
<ResetDatabase workspace={workspace} />
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -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(
|
app.get(
|
||||||
"/workspaces",
|
"/workspaces",
|
||||||
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||||
|
@ -425,8 +425,8 @@ function validChromaURL(input = "") {
|
|||||||
function validAzureURL(input = "") {
|
function validAzureURL(input = "") {
|
||||||
try {
|
try {
|
||||||
new URL(input);
|
new URL(input);
|
||||||
if (!input.includes("openai.azure.com"))
|
if (!input.includes("openai.azure.com") && !input.includes("microsoft.com"))
|
||||||
return "URL must include openai.azure.com";
|
return "Valid Azure endpoints must contain openai.azure.com OR microsoft.com";
|
||||||
return null;
|
return null;
|
||||||
} catch {
|
} catch {
|
||||||
return "Not a valid URL";
|
return "Not a valid URL";
|
||||||
|
Loading…
Reference in New Issue
Block a user