这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions frontend/src/components/Modals/Settings/VectorDbs/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import React, { useState } from "react";
import System from "../../../../models/system";
import ChromaLogo from "../../../../media/vectordbs/chroma.png";
import PineconeLogo from "../../../../media/vectordbs/pinecone.png";
import LanceDbLogo from "../../../../media/vectordbs/lancedb.png";

const noop = () => false;
export default function VectorDBSelection({
hideModal = noop,
user,
settings = {},
}) {
const [hasChanges, setHasChanges] = useState(false);
const [vectorDB, setVectorDB] = useState(settings?.VectorDB || "lancedb");
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
const canDebug = settings.MultiUserMode
? settings?.CanDebug && user?.role === "admin"
: settings?.CanDebug;

function updateVectorChoice(selection) {
if (!canDebug || selection === vectorDB) return false;
setHasChanges(true);
setVectorDB(selection);
}

const handleSubmit = async (e) => {
e.preventDefault();
setSaving(true);
setError(null);
const data = {};
const form = new FormData(e.target);
for (var [key, value] of form.entries()) data[key] = value;
const { error } = await System.updateSystem(data);
setError(error);
setSaving(false);
setHasChanges(!!error ? true : false);
};
return (
<div className="relative w-full max-w-2xl max-h-full">
<div className="relative bg-white rounded-lg shadow dark:bg-stone-700">
<div className="flex items-start justify-between px-6 py-4">
<p className="text-gray-800 dark:text-stone-200 text-base ">
These are the credentials and settings for how your AnythingLLM
instance will function. Its important these keys are current and
correct.
</p>
</div>

{!!error && (
<div className="mb-8 bg-red-700 dark:bg-orange-800 bg-opacity-30 border border-red-800 dark:border-orange-600 p-4 rounded-lg w-[90%] flex mx-auto">
<p className="text-red-800 dark:text-orange-300 text-sm">{error}</p>
</div>
)}

<form onSubmit={handleSubmit} onChange={() => setHasChanges(true)}>
<div className="px-6 space-y-6 flex h-full w-full">
<div className="w-full flex flex-col gap-y-4">
<p className="block text-sm font-medium text-gray-800 dark:text-slate-200">
Vector database provider
</p>
<div className="w-full flex overflow-x-scroll gap-x-4 no-scroll">
<input hidden={true} name="VectorDB" value={vectorDB} />
<VectorDBOption
name="Chroma"
value="chroma"
link="trychroma.com"
description="Open source vector database you can host yourself or on the cloud."
checked={vectorDB === "chroma"}
image={ChromaLogo}
onClick={updateVectorChoice}
/>
<VectorDBOption
name="Pinecone"
value="pinecone"
link="pinecone.io"
description="100% cloud-based vector database for enterprise use cases."
checked={vectorDB === "pinecone"}
image={PineconeLogo}
onClick={updateVectorChoice}
/>
<VectorDBOption
name="LanceDB"
value="lancedb"
link="lancedb.com"
description="100% local vector DB that runs on the same instance as AnythingLLM."
checked={vectorDB === "lancedb"}
image={LanceDbLogo}
onClick={updateVectorChoice}
/>
</div>
{vectorDB === "pinecone" && (
<>
<div>
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
Pinecone DB API Key
</label>
<input
type="text"
name="PineConeKey"
disabled={!canDebug}
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
placeholder="Pinecone API Key"
defaultValue={settings?.PineConeKey ? "*".repeat(20) : ""}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>

<div>
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
Pinecone Index Environment
</label>
<input
type="text"
name="PineConeEnvironment"
disabled={!canDebug}
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
placeholder="us-gcp-west-1"
defaultValue={settings?.PineConeEnvironment}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>

<div>
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
Pinecone Index Name
</label>
<input
type="text"
name="PineConeIndex"
disabled={!canDebug}
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
placeholder="my-index"
defaultValue={settings?.PineConeIndex}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>
</>
)}

{vectorDB === "chroma" && (
<>
<div>
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
Chroma Endpoint
</label>
<input
type="url"
name="ChromaEndpoint"
disabled={!canDebug}
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
placeholder="http://localhost:8000"
defaultValue={settings?.ChromaEndpoint}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>
</>
)}
{vectorDB === "lancedb" && (
<div className="w-full h-40 items-center justify-center flex">
<p className="text-gray-800 dark:text-slate-400">
There is no configuration needed for LanceDB.
</p>
</div>
)}
</div>
</div>
<div className="w-full p-4">
<button
hidden={!hasChanges}
disabled={saving}
type="submit"
className="w-full text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
{saving ? "Saving..." : "Save changes"}
</button>
</div>
</form>
<div className="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
<button
onClick={hideModal}
type="button"
className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Close
</button>
</div>
</div>
</div>
);
}

const VectorDBOption = ({
name,
link,
description,
value,
image,
checked = false,
onClick,
}) => {
return (
<div onClick={() => onClick(value)}>
<input
type="checkbox"
value={value}
className="peer hidden"
checked={checked}
readOnly={true}
formNoValidate={true}
/>
<label className="transition-all duration-300 inline-flex h-full w-60 cursor-pointer items-center justify-between rounded-lg border border-gray-200 bg-white p-5 text-gray-500 hover:bg-gray-50 hover:text-gray-600 peer-checked:border-blue-600 peer-checked:bg-blue-50 peer-checked:dark:bg-stone-800 peer-checked:text-gray-600 dark:border-slate-200 dark:bg-stone-800 dark:text-slate-400 dark:hover:bg-stone-700 dark:hover:text-slate-300 dark:peer-checked:text-slate-300">
<div className="block">
<img src={image} alt={name} className="mb-2 h-10 w-10 rounded-full" />
<div className="w-full text-lg font-semibold">{name}</div>
<div className="flex w-full flex-col gap-y-1 text-sm">
<p className="text-xs text-slate-400">{link}</p>
{description}
</div>
</div>
</label>
</div>
);
};
39 changes: 13 additions & 26 deletions frontend/src/components/Modals/Settings/index.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import React, { useEffect, useState } from "react";
import { Archive, Lock, Key, X, Users, LogOut } from "react-feather";
import { Archive, Lock, Key, X, Users, Database } from "react-feather";
import SystemKeys from "./Keys";
import ExportOrImportData from "./ExportImport";
import PasswordProtection from "./PasswordProtection";
import System from "../../../models/system";
import MultiUserMode from "./MultiUserMode";
import { AUTH_TOKEN, AUTH_USER } from "../../../utils/constants";
import paths from "../../../utils/paths";
import useUser from "../../../hooks/useUser";
import VectorDBSelection from "./VectorDbs";

const TABS = {
keys: SystemKeys,
exportimport: ExportOrImportData,
password: PasswordProtection,
multiuser: MultiUserMode,
vectordb: VectorDBSelection,
};

const noop = () => false;
export default function SystemSettingsModal({ hideModal = noop }) {
const { user } = useUser();
const [loading, setLoading] = useState(true);
const [selectedTab, setSelectedTab] = useState("keys");
// const [selectedTab, setSelectedTab] = useState("keys");
const [selectedTab, setSelectedTab] = useState("vectordb");
const [settings, setSettings] = useState(null);
const Component = TABS[selectedTab || "keys"];

Expand Down Expand Up @@ -93,14 +94,21 @@ function SettingTabs({ selectedTab, changeTab, settings, user }) {
icon={<Key className="h-4 w-4 flex-shrink-0" />}
onClick={changeTab}
/>
<SettingTab
active={selectedTab === "vectordb"}
displayName="Vector Database"
tabName="vectordb"
icon={<Database className="h-4 w-4 flex-shrink-0" />}
onClick={changeTab}
/>
<SettingTab
active={selectedTab === "exportimport"}
displayName="Export or Import"
tabName="exportimport"
icon={<Archive className="h-4 w-4 flex-shrink-0" />}
onClick={changeTab}
/>
{!settings?.MultiUserMode ? (
{!settings?.MultiUserMode && (
<>
<SettingTab
active={selectedTab === "multiuser"}
Expand All @@ -117,8 +125,6 @@ function SettingTabs({ selectedTab, changeTab, settings, user }) {
onClick={changeTab}
/>
</>
) : (
<LogoutTab user={user} />
)}
</ul>
);
Expand Down Expand Up @@ -150,25 +156,6 @@ function SettingTab({
);
}

function LogoutTab({ user }) {
if (!user) return null;

return (
<li className="mr-2">
<button
onClick={() => {
window.localStorage.removeItem(AUTH_USER);
window.localStorage.removeItem(AUTH_TOKEN);
window.location.replace(paths.home());
}}
className="flex items-center gap-x-1 p-4 border-b-2 rounded-t-lg group whitespace-nowrap border-transparent hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300"
>
<LogOut className="h-4 w-4 flex-shrink-0" /> Log out of {user.username}
</button>
</li>
);
}

export function useSystemSettingsModal() {
const [showing, setShowing] = useState(false);
const showModal = () => {
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Briefcase,
Cpu,
GitHub,
LogOut,
Menu,
Plus,
Shield,
Expand All @@ -21,6 +22,8 @@ import ActiveWorkspaces from "./ActiveWorkspaces";
import paths from "../../utils/paths";
import Discord from "../Icons/Discord";
import useUser from "../../hooks/useUser";
import { userFromStorage } from "../../utils/request";
import { AUTH_TOKEN, AUTH_USER } from "../../utils/constants";

export default function Sidebar() {
const sidebarRef = useRef(null);
Expand Down Expand Up @@ -103,6 +106,7 @@ export default function Sidebar() {
Enterprise Installation
</p>
</a>
<LogoutButton />
</div>

{/* Footer */}
Expand Down Expand Up @@ -269,6 +273,7 @@ export function SidebarMobileHeader() {
Enterprise Installation
</p>
</a>
<LogoutButton />
</div>

{/* Footer */}
Expand Down Expand Up @@ -325,3 +330,25 @@ function AdminHome() {
</a>
);
}

function LogoutButton() {
if (!window.localStorage.getItem(AUTH_USER)) return null;
const user = userFromStorage();
if (!user.username) return null;

return (
<button
onClick={() => {
window.localStorage.removeItem(AUTH_USER);
window.localStorage.removeItem(AUTH_TOKEN);
window.location.replace(paths.home());
}}
className="flex flex-grow w-[100%] h-[36px] gap-x-2 py-[5px] px-4 border border-slate-400 dark:border-transparent rounded-lg text-slate-800 dark:text-slate-200 justify-center items-center hover:bg-slate-100 dark:bg-stone-800 dark:hover:bg-stone-900"
>
<LogOut className="h-4 w-4" />
<p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold">
Log out of {user.username}
</p>
</button>
);
}
Binary file added frontend/src/media/vectordbs/chroma.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/src/media/vectordbs/lancedb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/src/media/vectordbs/pinecone.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions server/utils/helpers/updateENV.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ function validChromaURL(input = "") {
function updateENV(newENVs = {}) {
let error = "";
const validKeys = Object.keys(KEY_MAPPING);
const ENV_KEYS = Object.keys(newENVs).filter((key) =>
validKeys.includes(key)
const ENV_KEYS = Object.keys(newENVs).filter(
(key) => validKeys.includes(key) && !newENVs[key].includes("******") // strip out answers where the value is all asterisks
);
const newValues = {};

Expand Down