From 287a1d22139fbc7578236cbd3c7e184f3c992d0f Mon Sep 17 00:00:00 2001 From: Timothy Carambat Date: Fri, 5 Apr 2024 10:14:37 -0700 Subject: [PATCH 01/11] patch aria attrib outside element (#1047) --- frontend/src/components/Sidebar/index.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Sidebar/index.jsx b/frontend/src/components/Sidebar/index.jsx index 92bbbba6b..a32c0a3b6 100644 --- a/frontend/src/components/Sidebar/index.jsx +++ b/frontend/src/components/Sidebar/index.jsx @@ -99,13 +99,15 @@ export function SidebarMobileHeader() { return ( <> -
+
Date: Fri, 5 Apr 2024 10:17:20 -0700 Subject: [PATCH 02/11] [FIX] Workspace UI bug fix (#1043) fix workspaces list ui --- frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx index e379fd0b1..0a867e889 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx @@ -72,7 +72,7 @@ export default function ActiveWorkspaces() { } return ( -
+
{workspaces.map((workspace) => { const isActive = workspace.slug === slug; const isHovered = hoverStates[workspace.id]; From 94b58249a37a21b1c08deaa2d1edfdecbb6deb18 Mon Sep 17 00:00:00 2001 From: Timothy Carambat Date: Fri, 5 Apr 2024 10:58:36 -0700 Subject: [PATCH 03/11] Enable per-workspace provider/model combination (#1042) * Enable per-workspace provider/model combination * cleanup * remove resetWorkspaceChatModels and wipeWorkspaceModelPreference to prevent workspace from resetting model * add space --------- Co-authored-by: shatfield4 --- .../LLMSelection/AnthropicAiOptions/index.jsx | 76 +++--- .../LLMSelection/GeminiLLMOptions/index.jsx | 40 +-- .../LLMSelection/GroqAiOptions/index.jsx | 40 +-- .../LLMSelection/LMStudioOptions/index.jsx | 38 +-- .../LLMSelection/LocalAiOptions/index.jsx | 46 ++-- .../LLMSelection/MistralOptions/index.jsx | 4 +- .../LLMSelection/OllamaLLMOptions/index.jsx | 38 +-- .../LLMSelection/OpenAiOptions/index.jsx | 4 +- .../LLMSelection/OpenRouterOptions/index.jsx | 6 +- .../LLMSelection/PerplexityOptions/index.jsx | 4 +- .../LLMSelection/TogetherAiOptions/index.jsx | 6 +- frontend/src/hooks/useGetProvidersModels.js | 2 +- .../GeneralSettings/LLMPreference/index.jsx | 241 ++++++++++-------- .../ChatSettings/ChatModelSelection/index.jsx | 17 +- .../WorkspaceLLMItem/index.jsx | 151 +++++++++++ .../WorkspaceLLMSelection/index.jsx | 159 ++++++++++++ .../WorkspaceSettings/ChatSettings/index.jsx | 72 +++--- server/endpoints/workspaces.js | 4 +- server/models/systemSettings.js | 196 +++++++------- server/models/workspace.js | 91 ++++--- .../20240405015034_init/migration.sql | 2 + server/prisma/schema.prisma | 1 + server/utils/chats/embed.js | 4 +- server/utils/chats/index.js | 5 +- server/utils/chats/stream.js | 5 +- server/utils/helpers/index.js | 35 +-- server/utils/helpers/updateENV.js | 10 - 27 files changed, 836 insertions(+), 461 deletions(-) create mode 100644 frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/WorkspaceLLMItem/index.jsx create mode 100644 frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx create mode 100644 server/prisma/migrations/20240405015034_init/migration.sql diff --git a/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx b/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx index e8c288d60..9fe283ffe 100644 --- a/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/AnthropicAiOptions/index.jsx @@ -1,26 +1,6 @@ -import { Info } from "@phosphor-icons/react"; -import paths from "@/utils/paths"; - -export default function AnthropicAiOptions({ settings, showAlert = false }) { +export default function AnthropicAiOptions({ settings }) { return (
- {showAlert && ( -
-
- -

- Anthropic as your LLM requires you to set an embedding service to - use. -

-
- - Manage embedding → - -
- )}
-
- - -
+ {!settings?.credentialsOnly && ( +
+ + +
+ )}
); diff --git a/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx index 3b53ccc1e..a46e51329 100644 --- a/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx +++ b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx @@ -18,25 +18,27 @@ export default function GeminiLLMOptions({ settings }) { />
-
- - -
+ {!settings?.credentialsOnly && ( +
+ + +
+ )}
); diff --git a/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx b/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx index cc6fbbcc0..c85f0f1e0 100644 --- a/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/GroqAiOptions/index.jsx @@ -17,25 +17,27 @@ export default function GroqAiOptions({ settings }) { />
-
- - -
+ {!settings?.credentialsOnly && ( +
+ + +
+ )}
); } diff --git a/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx index 200c77a6e..c94a99d78 100644 --- a/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx +++ b/frontend/src/components/LLMSelection/LMStudioOptions/index.jsx @@ -46,23 +46,27 @@ export default function LMStudioOptions({ settings, showAlert = false }) { onBlur={() => setBasePath(basePathValue)} /> - -
- - e.target.blur()} - defaultValue={settings?.LMStudioTokenLimit} - required={true} - autoComplete="off" - /> -
+ {!settings?.credentialsOnly && ( + <> + +
+ + e.target.blur()} + defaultValue={settings?.LMStudioTokenLimit} + required={true} + autoComplete="off" + /> +
+ + )} ); diff --git a/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx b/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx index 91e386702..36b2f2588 100644 --- a/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/LocalAiOptions/index.jsx @@ -46,27 +46,31 @@ export default function LocalAiOptions({ settings, showAlert = false }) { onBlur={() => setBasePath(basePathValue)} /> - -
- - e.target.blur()} - defaultValue={settings?.LocalAiTokenLimit} - required={true} - autoComplete="off" - /> -
+ {!settings?.credentialsOnly && ( + <> + +
+ + e.target.blur()} + defaultValue={settings?.LocalAiTokenLimit} + required={true} + autoComplete="off" + /> +
+ + )}
diff --git a/frontend/src/components/LLMSelection/MistralOptions/index.jsx b/frontend/src/components/LLMSelection/MistralOptions/index.jsx index a143436ee..4daadcff1 100644 --- a/frontend/src/components/LLMSelection/MistralOptions/index.jsx +++ b/frontend/src/components/LLMSelection/MistralOptions/index.jsx @@ -24,7 +24,9 @@ export default function MistralOptions({ settings }) { onBlur={() => setMistralKey(inputValue)} />
- + {!settings?.credentialsOnly && ( + + )}
); } diff --git a/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx b/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx index ddfd7a81b..b08f29447 100644 --- a/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OllamaLLMOptions/index.jsx @@ -27,23 +27,27 @@ export default function OllamaLLMOptions({ settings }) { onBlur={() => setBasePath(basePathValue)} /> - -
- - e.target.blur()} - defaultValue={settings?.OllamaLLMTokenLimit} - required={true} - autoComplete="off" - /> -
+ {!settings?.credentialsOnly && ( + <> + +
+ + e.target.blur()} + defaultValue={settings?.OllamaLLMTokenLimit} + required={true} + autoComplete="off" + /> +
+ + )} ); diff --git a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx index 1e3493096..c5ec337d0 100644 --- a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx @@ -24,7 +24,9 @@ export default function OpenAiOptions({ settings }) { onBlur={() => setOpenAIKey(inputValue)} /> - + {!settings?.credentialsOnly && ( + + )} ); } diff --git a/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx index ff2a1d8f0..94ae320a2 100644 --- a/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx @@ -19,7 +19,9 @@ export default function OpenRouterOptions({ settings }) { spellCheck={false} /> - + {!settings?.credentialsOnly && ( + + )} ); } @@ -84,7 +86,7 @@ function OpenRouterModelSelection({ settings }) { diff --git a/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx b/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx index 6c4522495..9b53cd191 100644 --- a/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx +++ b/frontend/src/components/LLMSelection/PerplexityOptions/index.jsx @@ -19,7 +19,9 @@ export default function PerplexityOptions({ settings }) { spellCheck={false} /> - + {!settings?.credentialsOnly && ( + + )} ); } diff --git a/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx b/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx index 2c816339f..a0eefc83a 100644 --- a/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/TogetherAiOptions/index.jsx @@ -19,7 +19,9 @@ export default function TogetherAiOptions({ settings }) { spellCheck={false} /> - + {!settings?.credentialsOnly && ( + + )} ); } @@ -84,7 +86,7 @@ function TogetherAiModelSelection({ settings }) { diff --git a/frontend/src/hooks/useGetProvidersModels.js b/frontend/src/hooks/useGetProvidersModels.js index f578c929f..95df82a3a 100644 --- a/frontend/src/hooks/useGetProvidersModels.js +++ b/frontend/src/hooks/useGetProvidersModels.js @@ -2,7 +2,7 @@ import System from "@/models/system"; import { useEffect, useState } from "react"; // Providers which cannot use this feature for workspace<>model selection -export const DISABLED_PROVIDERS = ["azure", "lmstudio"]; +export const DISABLED_PROVIDERS = ["azure", "lmstudio", "native"]; const PROVIDER_DEFAULT_MODELS = { openai: [ "gpt-3.5-turbo", diff --git a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx index b9525c925..ccc6508ba 100644 --- a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx @@ -36,6 +36,130 @@ import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; +export const AVAILABLE_LLM_PROVIDERS = [ + { + name: "OpenAI", + value: "openai", + logo: OpenAiLogo, + options: (settings) => , + description: "The standard option for most non-commercial use.", + requiredConfig: ["OpenAiKey"], + }, + { + name: "Azure OpenAI", + value: "azure", + logo: AzureOpenAiLogo, + options: (settings) => , + description: "The enterprise option of OpenAI hosted on Azure services.", + requiredConfig: ["AzureOpenAiEndpoint"], + }, + { + name: "Anthropic", + value: "anthropic", + logo: AnthropicLogo, + options: (settings) => , + description: "A friendly AI Assistant hosted by Anthropic.", + requiredConfig: ["AnthropicApiKey"], + }, + { + name: "Gemini", + value: "gemini", + logo: GeminiLogo, + options: (settings) => , + description: "Google's largest and most capable AI model", + requiredConfig: ["GeminiLLMApiKey"], + }, + { + name: "HuggingFace", + value: "huggingface", + logo: HuggingFaceLogo, + options: (settings) => , + description: + "Access 150,000+ open-source LLMs and the world's AI community", + requiredConfig: [ + "HuggingFaceLLMEndpoint", + "HuggingFaceLLMAccessToken", + "HuggingFaceLLMTokenLimit", + ], + }, + { + name: "Ollama", + value: "ollama", + logo: OllamaLogo, + options: (settings) => , + description: "Run LLMs locally on your own machine.", + requiredConfig: ["OllamaLLMBasePath"], + }, + { + name: "LM Studio", + value: "lmstudio", + logo: LMStudioLogo, + options: (settings) => , + description: + "Discover, download, and run thousands of cutting edge LLMs in a few clicks.", + requiredConfig: ["LMStudioBasePath"], + }, + { + name: "Local AI", + value: "localai", + logo: LocalAiLogo, + options: (settings) => , + description: "Run LLMs locally on your own machine.", + requiredConfig: ["LocalAiApiKey", "LocalAiBasePath", "LocalAiTokenLimit"], + }, + { + name: "Together AI", + value: "togetherai", + logo: TogetherAILogo, + options: (settings) => , + description: "Run open source models from Together AI.", + requiredConfig: ["TogetherAiApiKey"], + }, + { + name: "Mistral", + value: "mistral", + logo: MistralLogo, + options: (settings) => , + description: "Run open source models from Mistral AI.", + requiredConfig: ["MistralApiKey"], + }, + { + name: "Perplexity AI", + value: "perplexity", + logo: PerplexityLogo, + options: (settings) => , + description: + "Run powerful and internet-connected models hosted by Perplexity AI.", + requiredConfig: ["PerplexityApiKey"], + }, + { + name: "OpenRouter", + value: "openrouter", + logo: OpenRouterLogo, + options: (settings) => , + description: "A unified interface for LLMs.", + requiredConfig: ["OpenRouterApiKey"], + }, + { + name: "Groq", + value: "groq", + logo: GroqLogo, + options: (settings) => , + description: + "The fastest LLM inferencing available for real-time AI applications.", + requiredConfig: ["GroqApiKey"], + }, + { + name: "Native", + value: "native", + logo: AnythingLLMIcon, + options: (settings) => , + description: + "Use a downloaded custom Llama model for chatting on this AnythingLLM instance.", + requiredConfig: [], + }, +]; + export default function GeneralLLMPreference() { const [saving, setSaving] = useState(false); const [hasChanges, setHasChanges] = useState(false); @@ -94,120 +218,15 @@ export default function GeneralLLMPreference() { }, []); useEffect(() => { - const filtered = LLMS.filter((llm) => + const filtered = AVAILABLE_LLM_PROVIDERS.filter((llm) => llm.name.toLowerCase().includes(searchQuery.toLowerCase()) ); setFilteredLLMs(filtered); }, [searchQuery, selectedLLM]); - const LLMS = [ - { - name: "OpenAI", - value: "openai", - logo: OpenAiLogo, - options: , - description: "The standard option for most non-commercial use.", - }, - { - name: "Azure OpenAI", - value: "azure", - logo: AzureOpenAiLogo, - options: , - description: "The enterprise option of OpenAI hosted on Azure services.", - }, - { - name: "Anthropic", - value: "anthropic", - logo: AnthropicLogo, - options: , - description: "A friendly AI Assistant hosted by Anthropic.", - }, - { - name: "Gemini", - value: "gemini", - logo: GeminiLogo, - options: , - description: "Google's largest and most capable AI model", - }, - { - name: "HuggingFace", - value: "huggingface", - logo: HuggingFaceLogo, - options: , - description: - "Access 150,000+ open-source LLMs and the world's AI community", - }, - { - name: "Ollama", - value: "ollama", - logo: OllamaLogo, - options: , - description: "Run LLMs locally on your own machine.", - }, - { - name: "LM Studio", - value: "lmstudio", - logo: LMStudioLogo, - options: , - description: - "Discover, download, and run thousands of cutting edge LLMs in a few clicks.", - }, - { - name: "Local AI", - value: "localai", - logo: LocalAiLogo, - options: , - description: "Run LLMs locally on your own machine.", - }, - { - name: "Together AI", - value: "togetherai", - logo: TogetherAILogo, - options: , - description: "Run open source models from Together AI.", - }, - { - name: "Mistral", - value: "mistral", - logo: MistralLogo, - options: , - description: "Run open source models from Mistral AI.", - }, - { - name: "Perplexity AI", - value: "perplexity", - logo: PerplexityLogo, - options: , - description: - "Run powerful and internet-connected models hosted by Perplexity AI.", - }, - { - name: "OpenRouter", - value: "openrouter", - logo: OpenRouterLogo, - options: , - description: "A unified interface for LLMs.", - }, - { - name: "Groq", - value: "groq", - logo: GroqLogo, - options: , - description: - "The fastest LLM inferencing available for real-time AI applications.", - }, - { - name: "Native", - value: "native", - logo: AnythingLLMIcon, - options: , - description: - "Use a downloaded custom Llama model for chatting on this AnythingLLM instance.", - }, - ]; - - const selectedLLMObject = LLMS.find((llm) => llm.value === selectedLLM); - + const selectedLLMObject = AVAILABLE_LLM_PROVIDERS.find( + (llm) => llm.value === selectedLLM + ); return (
@@ -339,7 +358,9 @@ export default function GeneralLLMPreference() { className="mt-4 flex flex-col gap-y-1" > {selectedLLM && - LLMS.find((llm) => llm.value === selectedLLM)?.options} + AVAILABLE_LLM_PROVIDERS.find( + (llm) => llm.value === selectedLLM + )?.options?.(settings)}
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx index 3ef7bb7ac..9ed424294 100644 --- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatModelSelection/index.jsx @@ -3,21 +3,20 @@ import useGetProviderModels, { } from "@/hooks/useGetProvidersModels"; export default function ChatModelSelection({ - settings, + provider, workspace, setHasChanges, }) { - const { defaultModels, customModels, loading } = useGetProviderModels( - settings?.LLMProvider - ); - if (DISABLED_PROVIDERS.includes(settings?.LLMProvider)) return null; + const { defaultModels, customModels, loading } = + useGetProviderModels(provider); + if (DISABLED_PROVIDERS.includes(provider)) return null; if (loading) { return (

The specific chat model that will be used for this workspace. If @@ -42,8 +41,7 @@ export default function ChatModelSelection({

The specific chat model that will be used for this workspace. If @@ -59,9 +57,6 @@ export default function ChatModelSelection({ }} className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" > - {defaultModels.length > 0 && ( {defaultModels.map((model) => { diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/WorkspaceLLMItem/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/WorkspaceLLMItem/index.jsx new file mode 100644 index 000000000..872d2a422 --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/WorkspaceLLMItem/index.jsx @@ -0,0 +1,151 @@ +// This component differs from the main LLMItem in that it shows if a provider is +// "ready for use" and if not - will then highjack the click handler to show a modal +// of the provider options that must be saved to continue. +import { createPortal } from "react-dom"; +import ModalWrapper from "@/components/ModalWrapper"; +import { useModal } from "@/hooks/useModal"; +import { X } from "@phosphor-icons/react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; + +export default function WorkspaceLLM({ + llm, + availableLLMs, + settings, + checked, + onClick, +}) { + const { isOpen, openModal, closeModal } = useModal(); + const { name, value, logo, description } = llm; + + function handleProviderSelection() { + // Determine if provider needs additional setup because its minimum required keys are + // not yet set in settings. + const requiresAdditionalSetup = (llm.requiredConfig || []).some( + (key) => !settings[key] + ); + if (requiresAdditionalSetup) { + openModal(); + return; + } + onClick(value); + } + + return ( + <> +

+ +
+ {`${name} +
+
{name}
+
{description}
+
+
+
+ + + ); +} + +function SetupProvider({ + availableLLMs, + isOpen, + provider, + closeModal, + postSubmit, +}) { + if (!isOpen) return null; + const LLMOption = availableLLMs.find((llm) => llm.value === provider); + if (!LLMOption) return null; + + async function handleUpdate(e) { + e.preventDefault(); + e.stopPropagation(); + const data = {}; + const form = new FormData(e.target); + for (var [key, value] of form.entries()) data[key] = value; + const { error } = await System.updateSystem(data); + if (error) { + showToast(`Failed to save ${LLMOption.name} settings: ${error}`, "error"); + return; + } + + closeModal(); + postSubmit(); + return false; + } + + // Cannot do nested forms, it will cause all sorts of issues, so we portal this out + // to the parent container form so we don't have nested forms. + return createPortal( + +
+
+
+

+ Setup {LLMOption.name} +

+ +
+ +
+
+

+ To use {LLMOption.name} as this workspace's LLM you need to set + it up first. +

+
{LLMOption.options({ credentialsOnly: true })}
+
+
+ + +
+
+
+
+
, + document.getElementById("workspace-chat-settings-container") + ); +} diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx new file mode 100644 index 000000000..07e355962 --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx @@ -0,0 +1,159 @@ +import React, { useEffect, useRef, useState } from "react"; +import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png"; +import WorkspaceLLMItem from "./WorkspaceLLMItem"; +import { AVAILABLE_LLM_PROVIDERS } from "@/pages/GeneralSettings/LLMPreference"; +import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; +import ChatModelSelection from "../ChatModelSelection"; + +const DISABLED_PROVIDERS = ["azure", "lmstudio", "native"]; +const LLM_DEFAULT = { + name: "System default", + value: "default", + logo: AnythingLLMIcon, + options: () => , + description: "Use the system LLM preference for this workspace.", + requiredConfig: [], +}; + +export default function WorkspaceLLMSelection({ + settings, + workspace, + setHasChanges, +}) { + const [filteredLLMs, setFilteredLLMs] = useState([]); + const [selectedLLM, setSelectedLLM] = useState( + workspace?.chatProvider ?? "default" + ); + const [searchQuery, setSearchQuery] = useState(""); + const [searchMenuOpen, setSearchMenuOpen] = useState(false); + const searchInputRef = useRef(null); + const LLMS = [LLM_DEFAULT, ...AVAILABLE_LLM_PROVIDERS].filter( + (llm) => !DISABLED_PROVIDERS.includes(llm.value) + ); + + function updateLLMChoice(selection) { + console.log({ selection }); + setSearchQuery(""); + setSelectedLLM(selection); + setSearchMenuOpen(false); + setHasChanges(true); + } + + function handleXButton() { + if (searchQuery.length > 0) { + setSearchQuery(""); + if (searchInputRef.current) searchInputRef.current.value = ""; + } else { + setSearchMenuOpen(!searchMenuOpen); + } + } + + useEffect(() => { + const filtered = LLMS.filter((llm) => + llm.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + setFilteredLLMs(filtered); + }, [LLMS, searchQuery, selectedLLM]); + + const selectedLLMObject = LLMS.find((llm) => llm.value === selectedLLM); + return ( +
+
+ +

+ The specific LLM provider & model that will be used for this + workspace. By default, it uses the system LLM provider and settings. +

+
+ +
+ + {searchMenuOpen && ( +
setSearchMenuOpen(false)} + /> + )} + {searchMenuOpen ? ( +
+
+
+ + setSearchQuery(e.target.value)} + ref={searchInputRef} + onKeyDown={(e) => { + if (e.key === "Enter") e.preventDefault(); + }} + /> + +
+
+ {filteredLLMs.map((llm) => { + return ( + updateLLMChoice(llm.value)} + /> + ); + })} +
+
+
+ ) : ( + + )} +
+ {selectedLLM !== "default" && ( +
+ +
+ )} +
+ ); +} diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx index 3004b871c..a6bab2c37 100644 --- a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx @@ -3,11 +3,11 @@ import Workspace from "@/models/workspace"; import showToast from "@/utils/toast"; import { castToType } from "@/utils/types"; import { useEffect, useRef, useState } from "react"; -import ChatModelSelection from "./ChatModelSelection"; import ChatHistorySettings from "./ChatHistorySettings"; import ChatPromptSettings from "./ChatPromptSettings"; import ChatTemperatureSettings from "./ChatTemperatureSettings"; import ChatModeSelection from "./ChatModeSelection"; +import WorkspaceLLMSelection from "./WorkspaceLLMSelection"; export default function ChatSettings({ workspace }) { const [settings, setSettings] = useState({}); @@ -44,35 +44,45 @@ export default function ChatSettings({ workspace }) { if (!workspace) return null; return ( -
- - - - - - {hasChanges && ( - - )} - +
+
+ + + + + + {hasChanges && ( + + )} + +
); } diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index da9e2ad94..1c87dc366 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -508,7 +508,7 @@ function workspaceEndpoints(app) { if (fs.existsSync(oldPfpPath)) fs.unlinkSync(oldPfpPath); } - const { workspace, message } = await Workspace.update( + const { workspace, message } = await Workspace._update( workspaceRecord.id, { pfpFilename: uploadedFileName, @@ -547,7 +547,7 @@ function workspaceEndpoints(app) { if (fs.existsSync(oldPfpPath)) fs.unlinkSync(oldPfpPath); } - const { workspace, message } = await Workspace.update( + const { workspace, message } = await Workspace._update( workspaceRecord.id, { pfpFilename: null, diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index c4529ad91..080a01f08 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -57,103 +57,13 @@ const SystemSettings = { // VectorDB Provider Selection Settings & Configs // -------------------------------------------------------- VectorDB: vectorDB, - // Pinecone DB Keys - PineConeKey: !!process.env.PINECONE_API_KEY, - PineConeIndex: process.env.PINECONE_INDEX, - - // Chroma DB Keys - ChromaEndpoint: process.env.CHROMA_ENDPOINT, - ChromaApiHeader: process.env.CHROMA_API_HEADER, - ChromaApiKey: !!process.env.CHROMA_API_KEY, - - // Weaviate DB Keys - WeaviateEndpoint: process.env.WEAVIATE_ENDPOINT, - WeaviateApiKey: process.env.WEAVIATE_API_KEY, - - // QDrant DB Keys - QdrantEndpoint: process.env.QDRANT_ENDPOINT, - QdrantApiKey: process.env.QDRANT_API_KEY, - - // Milvus DB Keys - MilvusAddress: process.env.MILVUS_ADDRESS, - MilvusUsername: process.env.MILVUS_USERNAME, - MilvusPassword: !!process.env.MILVUS_PASSWORD, - - // Zilliz DB Keys - ZillizEndpoint: process.env.ZILLIZ_ENDPOINT, - ZillizApiToken: process.env.ZILLIZ_API_TOKEN, - - // AstraDB Keys - AstraDBApplicationToken: process?.env?.ASTRA_DB_APPLICATION_TOKEN, - AstraDBEndpoint: process?.env?.ASTRA_DB_ENDPOINT, + ...this.vectorDBPreferenceKeys(), // -------------------------------------------------------- // LLM Provider Selection Settings & Configs // -------------------------------------------------------- LLMProvider: llmProvider, - // OpenAI Keys - OpenAiKey: !!process.env.OPEN_AI_KEY, - OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo", - - // Azure + OpenAI Keys - AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT, - AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY, - AzureOpenAiModelPref: process.env.OPEN_MODEL_PREF, - AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF, - AzureOpenAiTokenLimit: process.env.AZURE_OPENAI_TOKEN_LIMIT || 4096, - - // Anthropic Keys - AnthropicApiKey: !!process.env.ANTHROPIC_API_KEY, - AnthropicModelPref: process.env.ANTHROPIC_MODEL_PREF || "claude-2", - - // Gemini Keys - GeminiLLMApiKey: !!process.env.GEMINI_API_KEY, - GeminiLLMModelPref: process.env.GEMINI_LLM_MODEL_PREF || "gemini-pro", - - // LMStudio Keys - LMStudioBasePath: process.env.LMSTUDIO_BASE_PATH, - LMStudioTokenLimit: process.env.LMSTUDIO_MODEL_TOKEN_LIMIT, - LMStudioModelPref: process.env.LMSTUDIO_MODEL_PREF, - - // LocalAI Keys - LocalAiApiKey: !!process.env.LOCAL_AI_API_KEY, - LocalAiBasePath: process.env.LOCAL_AI_BASE_PATH, - LocalAiModelPref: process.env.LOCAL_AI_MODEL_PREF, - LocalAiTokenLimit: process.env.LOCAL_AI_MODEL_TOKEN_LIMIT, - - // Ollama LLM Keys - OllamaLLMBasePath: process.env.OLLAMA_BASE_PATH, - OllamaLLMModelPref: process.env.OLLAMA_MODEL_PREF, - OllamaLLMTokenLimit: process.env.OLLAMA_MODEL_TOKEN_LIMIT, - - // TogetherAI Keys - TogetherAiApiKey: !!process.env.TOGETHER_AI_API_KEY, - TogetherAiModelPref: process.env.TOGETHER_AI_MODEL_PREF, - - // Perplexity AI Keys - PerplexityApiKey: !!process.env.PERPLEXITY_API_KEY, - PerplexityModelPref: process.env.PERPLEXITY_MODEL_PREF, - - // OpenRouter Keys - OpenRouterApiKey: !!process.env.OPENROUTER_API_KEY, - OpenRouterModelPref: process.env.OPENROUTER_MODEL_PREF, - - // Mistral AI (API) Keys - MistralApiKey: !!process.env.MISTRAL_API_KEY, - MistralModelPref: process.env.MISTRAL_MODEL_PREF, - - // Groq AI API Keys - GroqApiKey: !!process.env.GROQ_API_KEY, - GroqModelPref: process.env.GROQ_MODEL_PREF, - - // Native LLM Keys - NativeLLMModelPref: process.env.NATIVE_LLM_MODEL_PREF, - NativeLLMTokenLimit: process.env.NATIVE_LLM_MODEL_TOKEN_LIMIT, - - // HuggingFace Dedicated Inference - HuggingFaceLLMEndpoint: process.env.HUGGING_FACE_LLM_ENDPOINT, - HuggingFaceLLMAccessToken: !!process.env.HUGGING_FACE_LLM_API_KEY, - HuggingFaceLLMTokenLimit: process.env.HUGGING_FACE_LLM_TOKEN_LIMIT, + ...this.llmPreferenceKeys(), // -------------------------------------------------------- // Whisper (Audio transcription) Selection Settings & Configs @@ -273,6 +183,108 @@ const SystemSettings = { return false; } }, + + vectorDBPreferenceKeys: function () { + return { + // Pinecone DB Keys + PineConeKey: !!process.env.PINECONE_API_KEY, + PineConeIndex: process.env.PINECONE_INDEX, + + // Chroma DB Keys + ChromaEndpoint: process.env.CHROMA_ENDPOINT, + ChromaApiHeader: process.env.CHROMA_API_HEADER, + ChromaApiKey: !!process.env.CHROMA_API_KEY, + + // Weaviate DB Keys + WeaviateEndpoint: process.env.WEAVIATE_ENDPOINT, + WeaviateApiKey: process.env.WEAVIATE_API_KEY, + + // QDrant DB Keys + QdrantEndpoint: process.env.QDRANT_ENDPOINT, + QdrantApiKey: process.env.QDRANT_API_KEY, + + // Milvus DB Keys + MilvusAddress: process.env.MILVUS_ADDRESS, + MilvusUsername: process.env.MILVUS_USERNAME, + MilvusPassword: !!process.env.MILVUS_PASSWORD, + + // Zilliz DB Keys + ZillizEndpoint: process.env.ZILLIZ_ENDPOINT, + ZillizApiToken: process.env.ZILLIZ_API_TOKEN, + + // AstraDB Keys + AstraDBApplicationToken: process?.env?.ASTRA_DB_APPLICATION_TOKEN, + AstraDBEndpoint: process?.env?.ASTRA_DB_ENDPOINT, + }; + }, + + llmPreferenceKeys: function () { + return { + // OpenAI Keys + OpenAiKey: !!process.env.OPEN_AI_KEY, + OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo", + + // Azure + OpenAI Keys + AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT, + AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY, + AzureOpenAiModelPref: process.env.OPEN_MODEL_PREF, + AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF, + AzureOpenAiTokenLimit: process.env.AZURE_OPENAI_TOKEN_LIMIT || 4096, + + // Anthropic Keys + AnthropicApiKey: !!process.env.ANTHROPIC_API_KEY, + AnthropicModelPref: process.env.ANTHROPIC_MODEL_PREF || "claude-2", + + // Gemini Keys + GeminiLLMApiKey: !!process.env.GEMINI_API_KEY, + GeminiLLMModelPref: process.env.GEMINI_LLM_MODEL_PREF || "gemini-pro", + + // LMStudio Keys + LMStudioBasePath: process.env.LMSTUDIO_BASE_PATH, + LMStudioTokenLimit: process.env.LMSTUDIO_MODEL_TOKEN_LIMIT, + LMStudioModelPref: process.env.LMSTUDIO_MODEL_PREF, + + // LocalAI Keys + LocalAiApiKey: !!process.env.LOCAL_AI_API_KEY, + LocalAiBasePath: process.env.LOCAL_AI_BASE_PATH, + LocalAiModelPref: process.env.LOCAL_AI_MODEL_PREF, + LocalAiTokenLimit: process.env.LOCAL_AI_MODEL_TOKEN_LIMIT, + + // Ollama LLM Keys + OllamaLLMBasePath: process.env.OLLAMA_BASE_PATH, + OllamaLLMModelPref: process.env.OLLAMA_MODEL_PREF, + OllamaLLMTokenLimit: process.env.OLLAMA_MODEL_TOKEN_LIMIT, + + // TogetherAI Keys + TogetherAiApiKey: !!process.env.TOGETHER_AI_API_KEY, + TogetherAiModelPref: process.env.TOGETHER_AI_MODEL_PREF, + + // Perplexity AI Keys + PerplexityApiKey: !!process.env.PERPLEXITY_API_KEY, + PerplexityModelPref: process.env.PERPLEXITY_MODEL_PREF, + + // OpenRouter Keys + OpenRouterApiKey: !!process.env.OPENROUTER_API_KEY, + OpenRouterModelPref: process.env.OPENROUTER_MODEL_PREF, + + // Mistral AI (API) Keys + MistralApiKey: !!process.env.MISTRAL_API_KEY, + MistralModelPref: process.env.MISTRAL_MODEL_PREF, + + // Groq AI API Keys + GroqApiKey: !!process.env.GROQ_API_KEY, + GroqModelPref: process.env.GROQ_MODEL_PREF, + + // Native LLM Keys + NativeLLMModelPref: process.env.NATIVE_LLM_MODEL_PREF, + NativeLLMTokenLimit: process.env.NATIVE_LLM_MODEL_TOKEN_LIMIT, + + // HuggingFace Dedicated Inference + HuggingFaceLLMEndpoint: process.env.HUGGING_FACE_LLM_ENDPOINT, + HuggingFaceLLMAccessToken: !!process.env.HUGGING_FACE_LLM_API_KEY, + HuggingFaceLLMTokenLimit: process.env.HUGGING_FACE_LLM_TOKEN_LIMIT, + }; + }, }; module.exports.SystemSettings = SystemSettings; diff --git a/server/models/workspace.js b/server/models/workspace.js index f061ca206..b905c199c 100644 --- a/server/models/workspace.js +++ b/server/models/workspace.js @@ -19,6 +19,7 @@ const Workspace = { "lastUpdatedAt", "openAiPrompt", "similarityThreshold", + "chatProvider", "chatModel", "topN", "chatMode", @@ -52,19 +53,42 @@ const Workspace = { } }, - update: async function (id = null, data = {}) { + update: async function (id = null, updates = {}) { if (!id) throw new Error("No workspace id provided for update"); - const validKeys = Object.keys(data).filter((key) => + const validFields = Object.keys(updates).filter((key) => this.writable.includes(key) ); - if (validKeys.length === 0) + + Object.entries(updates).forEach(([key]) => { + if (validFields.includes(key)) return; + delete updates[key]; + }); + + if (Object.keys(updates).length === 0) return { workspace: { id }, message: "No valid fields to update!" }; + // If the user unset the chatProvider we will need + // to then clear the chatModel as well to prevent confusion during + // LLM loading. + if (updates?.chatProvider === "default") { + updates.chatProvider = null; + updates.chatModel = null; + } + + return this._update(id, updates); + }, + + // Explicit update of settings + key validations. + // Only use this method when directly setting a key value + // that takes no user input for the keys being modified. + _update: async function (id = null, data = {}) { + if (!id) throw new Error("No workspace id provided for update"); + try { const workspace = await prisma.workspaces.update({ where: { id }, - data, // TODO: strict validation on writables here. + data, }); return { workspace, message: null }; } catch (error) { @@ -229,47 +253,40 @@ const Workspace = { } }, - resetWorkspaceChatModels: async () => { - try { - await prisma.workspaces.updateMany({ - data: { - chatModel: null, - }, - }); - return { success: true, error: null }; - } catch (error) { - console.error("Error resetting workspace chat models:", error.message); - return { success: false, error: error.message }; - } - }, - trackChange: async function (prevData, newData, user) { try { - const { Telemetry } = require("./telemetry"); - const { EventLogs } = require("./eventLogs"); - if ( - !newData?.openAiPrompt || - newData?.openAiPrompt === this.defaultPrompt || - newData?.openAiPrompt === prevData?.openAiPrompt - ) - return; - - await Telemetry.sendTelemetry("workspace_prompt_changed"); - await EventLogs.logEvent( - "workspace_prompt_changed", - { - workspaceName: prevData?.name, - prevSystemPrompt: prevData?.openAiPrompt || this.defaultPrompt, - newSystemPrompt: newData?.openAiPrompt, - }, - user?.id - ); + await this._trackWorkspacePromptChange(prevData, newData, user); return; } catch (error) { console.error("Error tracking workspace change:", error.message); return; } }, + + // We are only tracking this change to determine the need to a prompt library or + // prompt assistant feature. If this is something you would like to see - tell us on GitHub! + _trackWorkspacePromptChange: async function (prevData, newData, user) { + const { Telemetry } = require("./telemetry"); + const { EventLogs } = require("./eventLogs"); + if ( + !newData?.openAiPrompt || + newData?.openAiPrompt === this.defaultPrompt || + newData?.openAiPrompt === prevData?.openAiPrompt + ) + return; + + await Telemetry.sendTelemetry("workspace_prompt_changed"); + await EventLogs.logEvent( + "workspace_prompt_changed", + { + workspaceName: prevData?.name, + prevSystemPrompt: prevData?.openAiPrompt || this.defaultPrompt, + newSystemPrompt: newData?.openAiPrompt, + }, + user?.id + ); + return; + }, }; module.exports = { Workspace }; diff --git a/server/prisma/migrations/20240405015034_init/migration.sql b/server/prisma/migrations/20240405015034_init/migration.sql new file mode 100644 index 000000000..54a39d940 --- /dev/null +++ b/server/prisma/migrations/20240405015034_init/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "workspaces" ADD COLUMN "chatProvider" TEXT; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index fbb5f61d4..1e589b0f1 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -98,6 +98,7 @@ model workspaces { lastUpdatedAt DateTime @default(now()) openAiPrompt String? similarityThreshold Float? @default(0.25) + chatProvider String? chatModel String? topN Int? @default(4) chatMode String? @default("chat") diff --git a/server/utils/chats/embed.js b/server/utils/chats/embed.js index f748a3a5d..497b2c8e4 100644 --- a/server/utils/chats/embed.js +++ b/server/utils/chats/embed.js @@ -28,7 +28,9 @@ async function streamChatWithForEmbed( embed.workspace.openAiTemp = parseFloat(temperatureOverride); const uuid = uuidv4(); - const LLMConnector = getLLMProvider(chatModel ?? embed.workspace?.chatModel); + const LLMConnector = getLLMProvider({ + model: chatModel ?? embed.workspace?.chatModel, + }); const VectorDb = getVectorDbClass(); const { safe, reasons = [] } = await LLMConnector.isSafe(message); if (!safe) { diff --git a/server/utils/chats/index.js b/server/utils/chats/index.js index 10df9983f..7e40b9a8b 100644 --- a/server/utils/chats/index.js +++ b/server/utils/chats/index.js @@ -37,7 +37,10 @@ async function chatWithWorkspace( return await VALID_COMMANDS[command](workspace, message, uuid, user); } - const LLMConnector = getLLMProvider(workspace?.chatModel); + const LLMConnector = getLLMProvider({ + provider: workspace?.chatProvider, + model: workspace?.chatModel, + }); const VectorDb = getVectorDbClass(); const { safe, reasons = [] } = await LLMConnector.isSafe(message); if (!safe) { diff --git a/server/utils/chats/stream.js b/server/utils/chats/stream.js index f1a335bc8..0ec969eba 100644 --- a/server/utils/chats/stream.js +++ b/server/utils/chats/stream.js @@ -35,7 +35,10 @@ async function streamChatWithWorkspace( return; } - const LLMConnector = getLLMProvider(workspace?.chatModel); + const LLMConnector = getLLMProvider({ + provider: workspace?.chatProvider, + model: workspace?.chatModel, + }); const VectorDb = getVectorDbClass(); const { safe, reasons = [] } = await LLMConnector.isSafe(message); if (!safe) { diff --git a/server/utils/helpers/index.js b/server/utils/helpers/index.js index 783609725..a441bf82f 100644 --- a/server/utils/helpers/index.js +++ b/server/utils/helpers/index.js @@ -30,52 +30,53 @@ function getVectorDbClass() { } } -function getLLMProvider(modelPreference = null) { - const vectorSelection = process.env.LLM_PROVIDER || "openai"; +function getLLMProvider({ provider = null, model = null } = {}) { + const LLMSelection = provider ?? process.env.LLM_PROVIDER ?? "openai"; const embedder = getEmbeddingEngineSelection(); - switch (vectorSelection) { + + switch (LLMSelection) { case "openai": const { OpenAiLLM } = require("../AiProviders/openAi"); - return new OpenAiLLM(embedder, modelPreference); + return new OpenAiLLM(embedder, model); case "azure": const { AzureOpenAiLLM } = require("../AiProviders/azureOpenAi"); - return new AzureOpenAiLLM(embedder, modelPreference); + return new AzureOpenAiLLM(embedder, model); case "anthropic": const { AnthropicLLM } = require("../AiProviders/anthropic"); - return new AnthropicLLM(embedder, modelPreference); + return new AnthropicLLM(embedder, model); case "gemini": const { GeminiLLM } = require("../AiProviders/gemini"); - return new GeminiLLM(embedder, modelPreference); + return new GeminiLLM(embedder, model); case "lmstudio": const { LMStudioLLM } = require("../AiProviders/lmStudio"); - return new LMStudioLLM(embedder, modelPreference); + return new LMStudioLLM(embedder, model); case "localai": const { LocalAiLLM } = require("../AiProviders/localAi"); - return new LocalAiLLM(embedder, modelPreference); + return new LocalAiLLM(embedder, model); case "ollama": const { OllamaAILLM } = require("../AiProviders/ollama"); - return new OllamaAILLM(embedder, modelPreference); + return new OllamaAILLM(embedder, model); case "togetherai": const { TogetherAiLLM } = require("../AiProviders/togetherAi"); - return new TogetherAiLLM(embedder, modelPreference); + return new TogetherAiLLM(embedder, model); case "perplexity": const { PerplexityLLM } = require("../AiProviders/perplexity"); - return new PerplexityLLM(embedder, modelPreference); + return new PerplexityLLM(embedder, model); case "openrouter": const { OpenRouterLLM } = require("../AiProviders/openRouter"); - return new OpenRouterLLM(embedder, modelPreference); + return new OpenRouterLLM(embedder, model); case "mistral": const { MistralLLM } = require("../AiProviders/mistral"); - return new MistralLLM(embedder, modelPreference); + return new MistralLLM(embedder, model); case "native": const { NativeLLM } = require("../AiProviders/native"); - return new NativeLLM(embedder, modelPreference); + return new NativeLLM(embedder, model); case "huggingface": const { HuggingFaceLLM } = require("../AiProviders/huggingface"); - return new HuggingFaceLLM(embedder, modelPreference); + return new HuggingFaceLLM(embedder, model); case "groq": const { GroqLLM } = require("../AiProviders/groq"); - return new GroqLLM(embedder, modelPreference); + return new GroqLLM(embedder, model); default: throw new Error("ENV: No LLM_PROVIDER value found in environment!"); } diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index 12c45af26..a026fe338 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -2,7 +2,6 @@ const KEY_MAPPING = { LLMProvider: { envKey: "LLM_PROVIDER", checks: [isNotEmpty, supportedLLM], - postUpdate: [wipeWorkspaceModelPreference], }, // OpenAI Settings OpenAiKey: { @@ -493,15 +492,6 @@ function validHuggingFaceEndpoint(input = "") { : null; } -// If the LLMProvider has changed we need to reset all workspace model preferences to -// null since the provider<>model name combination will be invalid for whatever the new -// provider is. -async function wipeWorkspaceModelPreference(key, prev, next) { - if (prev === next) return; - const { Workspace } = require("../../models/workspace"); - await Workspace.resetWorkspaceChatModels(); -} - // This will force update .env variables which for any which reason were not able to be parsed or // read from an ENV file as this seems to be a complicating step for many so allowing people to write // to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks From a2923e35e8c6596cab446a2772df288b9e5a774b Mon Sep 17 00:00:00 2001 From: timothycarambat Date: Fri, 5 Apr 2024 11:54:38 -0700 Subject: [PATCH 04/11] remove console --- .../ChatSettings/WorkspaceLLMSelection/index.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx index 07e355962..16eefa05f 100644 --- a/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/WorkspaceLLMSelection/index.jsx @@ -32,7 +32,6 @@ export default function WorkspaceLLMSelection({ ); function updateLLMChoice(selection) { - console.log({ selection }); setSearchQuery(""); setSelectedLLM(selection); setSearchMenuOpen(false); From 657be7ecfccce87670167c57f82be883fca6f67b Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Fri, 5 Apr 2024 13:54:12 -0700 Subject: [PATCH 05/11] [FEAT] Normalize UI for CTA buttons/UX improvements (#1021) * members workspace settings menu and admin users UI updates * implement CTAButton in all general/admin settings + ui tweaks * move CTAButton to components/lib * fix UI for security page to match rest of all settings pages * UX improvements * add CTAButton component * prevent components folder from being ignored * patch some UI fixes --------- Co-authored-by: timothycarambat --- frontend/.gitignore | 1 + .../src/components/lib/CTAButton/index.jsx | 16 +++++ .../src/pages/Admin/Invitations/index.jsx | 16 ++--- frontend/src/pages/Admin/Logging/index.jsx | 17 +++-- frontend/src/pages/Admin/System/index.jsx | 25 +++---- frontend/src/pages/Admin/Users/index.jsx | 14 ++-- frontend/src/pages/Admin/Workspaces/index.jsx | 14 ++-- .../pages/GeneralSettings/ApiKeys/index.jsx | 15 +++-- .../src/pages/GeneralSettings/Chats/index.jsx | 2 +- .../GeneralSettings/EmbedConfigs/index.jsx | 14 ++-- .../EmbeddingPreference/index.jsx | 22 ++++--- .../GeneralSettings/LLMPreference/index.jsx | 22 ++++--- .../GeneralSettings/PrivacyAndData/index.jsx | 8 +-- .../pages/GeneralSettings/Security/index.jsx | 66 ++++++++++--------- .../TranscriptionPreference/index.jsx | 22 ++++--- .../GeneralSettings/VectorDatabase/index.jsx | 22 ++++--- .../Members/AddMemberModal/index.jsx | 20 +++--- .../pages/WorkspaceSettings/Members/index.jsx | 10 +-- 18 files changed, 176 insertions(+), 150 deletions(-) create mode 100644 frontend/src/components/lib/CTAButton/index.jsx diff --git a/frontend/.gitignore b/frontend/.gitignore index 196c8f691..787206034 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -12,6 +12,7 @@ dist lib dist-ssr *.local +!frontend/components/lib # Editor directories and files .vscode/* diff --git a/frontend/src/components/lib/CTAButton/index.jsx b/frontend/src/components/lib/CTAButton/index.jsx new file mode 100644 index 000000000..93427f0f9 --- /dev/null +++ b/frontend/src/components/lib/CTAButton/index.jsx @@ -0,0 +1,16 @@ +export default function CTAButton({ + children, + disabled = false, + onClick, + className = "", +}) { + return ( + + ); +} diff --git a/frontend/src/pages/Admin/Invitations/index.jsx b/frontend/src/pages/Admin/Invitations/index.jsx index f81ccae52..599271ad9 100644 --- a/frontend/src/pages/Admin/Invitations/index.jsx +++ b/frontend/src/pages/Admin/Invitations/index.jsx @@ -10,6 +10,7 @@ import InviteRow from "./InviteRow"; import NewInviteModal from "./NewInviteModal"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminInvites() { const { isOpen, openModal, closeModal } = useModal(); @@ -21,25 +22,24 @@ export default function AdminInvites() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

Invitations

-

Create invitation links for people in your organization to accept and sign up with. Invitations can only be used by a single user.

+
+ + Create Invite + Link + +
diff --git a/frontend/src/pages/Admin/Logging/index.jsx b/frontend/src/pages/Admin/Logging/index.jsx index 5d8a90acb..69a81ab56 100644 --- a/frontend/src/pages/Admin/Logging/index.jsx +++ b/frontend/src/pages/Admin/Logging/index.jsx @@ -6,6 +6,7 @@ import { isMobile } from "react-device-detect"; import * as Skeleton from "react-loading-skeleton"; import LogRow from "./LogRow"; import showToast from "@/utils/toast"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminLogs() { const handleResetLogs = async () => { @@ -32,24 +33,26 @@ export default function AdminLogs() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

Event Logs

-

View all actions and events happening on this instance for monitoring.

+
+ + Clear Event Logs + +
diff --git a/frontend/src/pages/Admin/System/index.jsx b/frontend/src/pages/Admin/System/index.jsx index 867276b0e..bdab765a5 100644 --- a/frontend/src/pages/Admin/System/index.jsx +++ b/frontend/src/pages/Admin/System/index.jsx @@ -3,6 +3,7 @@ import Sidebar from "@/components/SettingsSidebar"; import { isMobile } from "react-device-detect"; import Admin from "@/models/admin"; import showToast from "@/utils/toast"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminSystem() { const [saving, setSaving] = useState(false); @@ -49,7 +50,7 @@ export default function AdminSystem() {
setHasChanges(true)} - className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16" + className="flex flex-col w-full px-1 md:pl-6 md:pr-[50px] md:py-6 py-16" >
@@ -62,8 +63,14 @@ export default function AdminSystem() { instance.

- -
+ {hasChanges && ( +
+ + {saving ? "Saving..." : "Save changes"} + +
+ )} +

Users can delete workspaces @@ -141,18 +148,6 @@ export default function AdminSystem() {

)}
- - {hasChanges && ( -
- -
- )}
diff --git a/frontend/src/pages/Admin/Users/index.jsx b/frontend/src/pages/Admin/Users/index.jsx index 6824ee219..408e794aa 100644 --- a/frontend/src/pages/Admin/Users/index.jsx +++ b/frontend/src/pages/Admin/Users/index.jsx @@ -10,6 +10,7 @@ import useUser from "@/hooks/useUser"; import NewUserModal from "./NewUserModal"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminUsers() { const { isOpen, openModal, closeModal } = useModal(); @@ -21,16 +22,10 @@ export default function AdminUsers() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

Users

-

These are all the accounts which have an account on this instance. @@ -38,6 +33,11 @@ export default function AdminUsers() { instance.

+
+ + Add user + +
diff --git a/frontend/src/pages/Admin/Workspaces/index.jsx b/frontend/src/pages/Admin/Workspaces/index.jsx index 63b9fb346..5a7b128d2 100644 --- a/frontend/src/pages/Admin/Workspaces/index.jsx +++ b/frontend/src/pages/Admin/Workspaces/index.jsx @@ -9,6 +9,7 @@ import WorkspaceRow from "./WorkspaceRow"; import NewWorkspaceModal from "./NewWorkspaceModal"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminWorkspaces() { const { isOpen, openModal, closeModal } = useModal(); @@ -20,24 +21,23 @@ export default function AdminWorkspaces() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

Instance Workspaces

-

These are all the workspaces that exist on this instance. Removing a workspace will delete all of it's associated chats and settings.

+
+ + New Workspace + +
diff --git a/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx b/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx index 6df1d09dd..4cf3fa397 100644 --- a/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx +++ b/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx @@ -12,6 +12,7 @@ import { userFromStorage } from "@/utils/request"; import System from "@/models/system"; import ModalWrapper from "@/components/ModalWrapper"; import { useModal } from "@/hooks/useModal"; +import CTAButton from "@/components/lib/CTAButton"; export default function AdminApiKeys() { const { isOpen, openModal, closeModal } = useModal(); @@ -23,16 +24,10 @@ export default function AdminApiKeys() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

API Keys

-

API keys allow the holder to programmatically access and manage @@ -47,6 +42,12 @@ export default function AdminApiKeys() { Read the API documentation →

+
+ + Generate New API + Key + +
diff --git a/frontend/src/pages/GeneralSettings/Chats/index.jsx b/frontend/src/pages/GeneralSettings/Chats/index.jsx index e5da1a888..d5b3d5093 100644 --- a/frontend/src/pages/GeneralSettings/Chats/index.jsx +++ b/frontend/src/pages/GeneralSettings/Chats/index.jsx @@ -90,7 +90,7 @@ export default function WorkspaceChats() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

diff --git a/frontend/src/pages/GeneralSettings/EmbedConfigs/index.jsx b/frontend/src/pages/GeneralSettings/EmbedConfigs/index.jsx index a46a9d5e0..4d65e0d03 100644 --- a/frontend/src/pages/GeneralSettings/EmbedConfigs/index.jsx +++ b/frontend/src/pages/GeneralSettings/EmbedConfigs/index.jsx @@ -9,6 +9,7 @@ import NewEmbedModal from "./NewEmbedModal"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; import Embed from "@/models/embed"; +import CTAButton from "@/components/lib/CTAButton"; export default function EmbedConfigs() { const { isOpen, openModal, closeModal } = useModal(); @@ -20,18 +21,12 @@ export default function EmbedConfigs() { style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -

+

Embeddable Chat Widgets

-

Embeddable chat widgets are public facing chat interfaces that are @@ -39,6 +34,11 @@ export default function EmbedConfigs() { that then you can publish to the world.

+
+ + Create embed + +
diff --git a/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx b/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx index d2b265560..ad7c08d35 100644 --- a/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx @@ -19,6 +19,7 @@ import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; +import CTAButton from "@/components/lib/CTAButton"; export default function GeneralEmbeddingPreference() { const [saving, setSaving] = useState(false); @@ -165,21 +166,12 @@ export default function GeneralEmbeddingPreference() { onSubmit={handleSubmit} className="flex w-full" > -
+

Embedding Preference

- {hasChanges && ( - - )}

When using an LLM that does not natively support an embedding @@ -191,6 +183,16 @@ export default function GeneralEmbeddingPreference() { format which AnythingLLM can use to process.

+
+ {hasChanges && ( + handleSubmit()} + className="mt-3 mr-0 -mb-14 z-10" + > + {saving ? "Saving..." : "Save changes"} + + )} +
Embedding Provider
diff --git a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx index ccc6508ba..5fbc826ca 100644 --- a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx @@ -35,6 +35,7 @@ import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; +import CTAButton from "@/components/lib/CTAButton"; export const AVAILABLE_LLM_PROVIDERS = [ { @@ -245,21 +246,12 @@ export default function GeneralLLMPreference() { className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" >
-
+

LLM Preference

- {hasChanges && ( - - )}

These are the credentials and settings for your preferred LLM @@ -268,6 +260,16 @@ export default function GeneralLLMPreference() { properly.

+
+ {hasChanges && ( + handleSubmit()} + className="mt-3 mr-0 -mb-14 z-10" + > + {saving ? "Saving..." : "Save changes"} + + )} +
LLM Provider
diff --git a/frontend/src/pages/GeneralSettings/PrivacyAndData/index.jsx b/frontend/src/pages/GeneralSettings/PrivacyAndData/index.jsx index dfc4b29f9..c0cd2b199 100644 --- a/frontend/src/pages/GeneralSettings/PrivacyAndData/index.jsx +++ b/frontend/src/pages/GeneralSettings/PrivacyAndData/index.jsx @@ -29,16 +29,16 @@ export default function PrivacyAndDataHandling() {
-
+
-

+

Privacy & Data-Handling

-

+

This is your configuration for how connected third party providers and AnythingLLM handle your data.

diff --git a/frontend/src/pages/GeneralSettings/Security/index.jsx b/frontend/src/pages/GeneralSettings/Security/index.jsx index 298a81ad7..7d60aadad 100644 --- a/frontend/src/pages/GeneralSettings/Security/index.jsx +++ b/frontend/src/pages/GeneralSettings/Security/index.jsx @@ -6,6 +6,7 @@ import System from "@/models/system"; import paths from "@/utils/paths"; import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants"; import PreLoader from "@/components/Preloader"; +import CTAButton from "@/components/lib/CTAButton"; export default function GeneralSecurity() { return ( @@ -13,7 +14,7 @@ export default function GeneralSecurity() {
@@ -32,7 +33,7 @@ function MultiUserMode() { const handleSubmit = async (e) => { e.preventDefault(); setSaving(true); - + setHasChanges(false); if (useMultiUserMode) { const form = new FormData(e.target); const data = { @@ -83,27 +84,30 @@ function MultiUserMode() { setHasChanges(true)} - className="flex w-full" + className="flex flex-col w-full px-1 md:pl-6 md:pr-[50px] md:py-6 py-16" > -
-
+
+
-

Multi-User Mode

- {hasChanges && ( - - )} +

+ Multi-User Mode +

-

+

Set up your instance to support your team by activating Multi-User Mode.

+ {hasChanges && ( +
+ handleSubmit()} + className="mt-3 mr-0 -mb-20 z-10" + > + {saving ? "Saving..." : "Save changes"} + +
+ )}
@@ -198,6 +202,7 @@ function PasswordProtection() { if (multiUserModeEnabled) return false; setSaving(true); + setHasChanges(false); const form = new FormData(e.target); const data = { usePassword, @@ -248,29 +253,30 @@ function PasswordProtection() { setHasChanges(true)} - className="flex w-full" + className="flex flex-col w-full px-1 md:pl-6 md:pr-[50px] md:py-6 py-16" > -
-
+
+
-

+

Password Protection

- {hasChanges && ( - - )}
-

+

Protect your AnythingLLM instance with a password. If you forget this there is no recovery method so ensure you save this password.

+ {hasChanges && ( +
+ handleSubmit()} + className="mt-3 mr-0 -mb-20 z-10" + > + {saving ? "Saving..." : "Save changes"} + +
+ )}
diff --git a/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx b/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx index c4d20ef4b..5fbd196c3 100644 --- a/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx @@ -10,6 +10,7 @@ import OpenAiWhisperOptions from "@/components/TranscriptionSelection/OpenAiOpti import NativeTranscriptionOptions from "@/components/TranscriptionSelection/NativeTranscriptionOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; +import CTAButton from "@/components/lib/CTAButton"; export default function TranscriptionModelPreference() { const [saving, setSaving] = useState(false); @@ -114,21 +115,12 @@ export default function TranscriptionModelPreference() { className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll" > -
+

Transcription Model Preference

- {hasChanges && ( - - )}

These are the credentials and settings for your preferred @@ -137,6 +129,16 @@ export default function TranscriptionModelPreference() { transcribe.

+
+ {hasChanges && ( + handleSubmit()} + className="mt-3 mr-0 -mb-14 z-10" + > + {saving ? "Saving..." : "Save changes"} + + )} +
Transcription Provider
diff --git a/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx b/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx index df0307d11..a782410e5 100644 --- a/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx +++ b/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx @@ -25,6 +25,7 @@ import ZillizCloudOptions from "@/components/VectorDBSelection/ZillizCloudOption import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; import AstraDBOptions from "@/components/VectorDBSelection/AstraDBOptions"; +import CTAButton from "@/components/lib/CTAButton"; export default function GeneralVectorDatabase() { const [saving, setSaving] = useState(false); @@ -189,21 +190,12 @@ export default function GeneralVectorDatabase() { onSubmit={handleSubmit} className="flex w-full" > -
+

Vector Database

- {hasChanges && ( - - )}

These are the credentials and settings for how your @@ -211,6 +203,16 @@ export default function GeneralVectorDatabase() { are current and correct.

+
+ {hasChanges && ( + handleSubmit()} + className="mt-3 mr-0 -mb-14 z-10" + > + {saving ? "Saving..." : "Save changes"} + + )} +
Vector Database Provider
diff --git a/frontend/src/pages/WorkspaceSettings/Members/AddMemberModal/index.jsx b/frontend/src/pages/WorkspaceSettings/Members/AddMemberModal/index.jsx index 0799e5486..e5410f403 100644 --- a/frontend/src/pages/WorkspaceSettings/Members/AddMemberModal/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/Members/AddMemberModal/index.jsx @@ -138,15 +138,17 @@ export default function AddMemberModal({ closeModal, workspace, users }) {

Select All

- + {selectedUsers.length > 0 && ( + + )}
- + Manage Users Date: Fri, 5 Apr 2024 14:25:41 -0700 Subject: [PATCH 06/11] [FEAT] Implement new data connectors UI (#1034) * WIP data connector redesign * new UI for data connectors complete * remove old data connector page/cleanup imports * cleanup of UI and imports --------- Co-authored-by: timothycarambat --- frontend/src/App.jsx | 15 - .../components/DataConnectorOption/index.jsx | 22 -- .../DataConnectorOption/media/github.png | Bin 22064 -> 0 bytes .../DataConnectorOption/media/github.svg | 4 + .../DataConnectorOption/media/index.js | 4 +- .../DataConnectorOption/media/youtube.png | Bin 5412 -> 0 bytes .../DataConnectorOption/media/youtube.svg | 10 + .../DataConnectors/ConnectorOption/index.jsx | 25 ++ .../Connectors/Github/index.jsx | 271 ++++++++++++++++ .../Connectors/Youtube/index.jsx | 91 ++++++ .../MangeWorkspace/DataConnectors/index.jsx | 77 +++++ .../Modals/MangeWorkspace/index.jsx | 47 ++- .../src/components/SettingsSidebar/index.jsx | 24 +- .../Connectors/Github/index.jsx | 293 ------------------ .../Connectors/Youtube/index.jsx | 113 ------- .../DataConnectors/Connectors/index.jsx | 21 -- .../GeneralSettings/DataConnectors/index.jsx | 43 --- frontend/src/utils/paths.js | 11 - 18 files changed, 531 insertions(+), 540 deletions(-) delete mode 100644 frontend/src/components/DataConnectorOption/media/github.png create mode 100644 frontend/src/components/DataConnectorOption/media/github.svg delete mode 100644 frontend/src/components/DataConnectorOption/media/youtube.png create mode 100644 frontend/src/components/DataConnectorOption/media/youtube.svg create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/Github/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/Youtube/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/index.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9ef160e72..dbd61623d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -39,12 +39,6 @@ const GeneralVectorDatabase = lazy( () => import("@/pages/GeneralSettings/VectorDatabase") ); const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security")); -const DataConnectors = lazy( - () => import("@/pages/GeneralSettings/DataConnectors") -); -const DataConnectorSetup = lazy( - () => import("@/pages/GeneralSettings/DataConnectors/Connectors") -); const WorkspaceSettings = lazy(() => import("@/pages/WorkspaceSettings")); const EmbedConfigSetup = lazy( () => import("@/pages/GeneralSettings/EmbedConfigs") @@ -145,15 +139,6 @@ export default function App() { path="/settings/workspaces" element={} /> - } - /> - } - /> - {/* Onboarding Flow */} } /> } /> diff --git a/frontend/src/components/DataConnectorOption/index.jsx b/frontend/src/components/DataConnectorOption/index.jsx index df7fad0f6..038624acf 100644 --- a/frontend/src/components/DataConnectorOption/index.jsx +++ b/frontend/src/components/DataConnectorOption/index.jsx @@ -1,6 +1,3 @@ -import paths from "@/utils/paths"; -import ConnectorImages from "./media"; - export default function DataConnectorOption({ slug }) { if (!DATA_CONNECTORS.hasOwnProperty(slug)) return null; const { path, image, name, description, link } = DATA_CONNECTORS[slug]; @@ -26,22 +23,3 @@ export default function DataConnectorOption({ slug }) { ); } - -export const DATA_CONNECTORS = { - github: { - name: "GitHub Repo", - path: paths.settings.dataConnectors.github(), - image: ConnectorImages.github, - description: - "Import an entire public or private Github repository in a single click.", - link: "https://github.com", - }, - "youtube-transcript": { - name: "YouTube Transcript", - path: paths.settings.dataConnectors.youtubeTranscript(), - image: ConnectorImages.youtube, - description: - "Import the transcription of an entire YouTube video from a link.", - link: "https://youtube.com", - }, -}; diff --git a/frontend/src/components/DataConnectorOption/media/github.png b/frontend/src/components/DataConnectorOption/media/github.png deleted file mode 100644 index 835221bab5ec4ca19b03d0b9c25f4948b84a0cd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22064 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 diff --git a/frontend/src/components/DataConnectorOption/media/github.svg b/frontend/src/components/DataConnectorOption/media/github.svg new file mode 100644 index 000000000..e01722151 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/github.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/DataConnectorOption/media/index.js b/frontend/src/components/DataConnectorOption/media/index.js index b3bacc1de..543bed5f7 100644 --- a/frontend/src/components/DataConnectorOption/media/index.js +++ b/frontend/src/components/DataConnectorOption/media/index.js @@ -1,5 +1,5 @@ -import Github from "./github.png"; -import YouTube from "./youtube.png"; +import Github from "./github.svg"; +import YouTube from "./youtube.svg"; const ConnectorImages = { github: Github, diff --git a/frontend/src/components/DataConnectorOption/media/youtube.png b/frontend/src/components/DataConnectorOption/media/youtube.png deleted file mode 100644 index aed2b0475cafadf03769b671baeeeaa58581a68f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5412 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 + + + + + + + + + diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx new file mode 100644 index 000000000..e0b10e050 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx @@ -0,0 +1,25 @@ +export default function ConnectorOption({ + slug, + selectedConnector, + setSelectedConnector, + image, + name, + description, +}) { + return ( + + ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx new file mode 100644 index 000000000..de6ed77e1 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -0,0 +1,271 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; +import { TagsInput } from "react-tag-input-component"; +import { Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +const DEFAULT_BRANCHES = ["main", "master"]; +export default function GithubOptions() { + const [loading, setLoading] = useState(false); + const [repo, setRepo] = useState(null); + const [accessToken, setAccessToken] = useState(null); + const [ignores, setIgnores] = useState([]); + + const [settings, setSettings] = useState({ + repo: null, + accessToken: null, + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all files for repo - this may take a while.", + "info", + { clear: true, autoClose: false } + ); + const { data, error } = await System.dataConnectors.github.collect({ + repo: form.get("repo"), + accessToken: form.get("accessToken"), + branch: form.get("branch"), + ignorePaths: ignores, + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.files} ${pluralize("file", data.files)} collected from ${ + data.author + }/${data.repo}:${data.branch}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +

+
+ +
+
+
+
+ +

+ Url of the GitHub repo you wish to collect. +

+
+ setRepo(e.target.value)} + onBlur={() => setSettings({ ...settings, repo })} + spellCheck={false} + /> +
+
+
+ +

+ Access Token to prevent rate limiting. +

+
+ setAccessToken(e.target.value)} + onBlur={() => setSettings({ ...settings, accessToken })} + /> +
+ +
+ +
+
+ +

+ List in .gitignore format to ignore specific files during + collection. Press enter after each entry you want to save. +

+
+ +
+
+ +
+ + {loading && ( +

+ Once complete, all files will be available for embedding into + workspaces in the document picker. +

+ )} +
+ +
+
+ ); +} + +function GitHubBranchSelection({ repo, accessToken }) { + const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchAllBranches() { + if (!repo) { + setAllBranches(DEFAULT_BRANCHES); + setLoading(false); + return; + } + + setLoading(true); + const { branches } = await System.dataConnectors.github.branches({ + repo, + accessToken, + }); + setAllBranches(branches.length > 0 ? branches : DEFAULT_BRANCHES); + setLoading(false); + } + fetchAllBranches(); + }, [repo, accessToken]); + + if (loading) { + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); + } + + return ( +
+
+ +

+ Branch you wish to collect files from. +

+
+ +
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx new file mode 100644 index 000000000..ed18dcd42 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -0,0 +1,91 @@ +import React, { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; + +export default function YoutubeOptions() { + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast("Fetching transcript for YouTube video.", "info", { + clear: true, + autoClose: false, + }); + + const { data, error } = await System.dataConnectors.youtube.transcribe({ + url: form.get("url"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.title} by ${data.author} transcription completed. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +

+ URL of the YouTube video you wish to transcribe. +

+
+ +
+
+
+ +
+ + {loading && ( +

+ Once complete, the transcription will be available for embedding + into workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx new file mode 100644 index 000000000..419fc1fc9 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -0,0 +1,77 @@ +import ConnectorImages from "@/components/DataConnectorOption/media"; +import { MagnifyingGlass } from "@phosphor-icons/react"; +import GithubOptions from "./Connectors/Github"; +import YoutubeOptions from "./Connectors/Youtube"; +import { useState } from "react"; +import ConnectorOption from "./ConnectorOption"; + +export const DATA_CONNECTORS = { + github: { + name: "GitHub Repo", + image: ConnectorImages.github, + description: + "Import an entire public or private Github repository in a single click.", + options: , + }, + "youtube-transcript": { + name: "YouTube Transcript", + image: ConnectorImages.youtube, + description: + "Import the transcription of an entire YouTube video from a link.", + options: , + }, +}; + +export default function DataConnectors() { + const [selectedConnector, setSelectedConnector] = useState("github"); + const [searchQuery, setSearchQuery] = useState(""); + + const filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => + DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ {filteredConnectors.length > 0 ? ( + filteredConnectors.map((slug, index) => ( + + )) + ) : ( +
+ No data connectors found. +
+ )} +
+
+
+
+ {DATA_CONNECTORS[selectedConnector].options} +
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/index.jsx b/frontend/src/components/Modals/MangeWorkspace/index.jsx index 4898f531f..2c6e658b0 100644 --- a/frontend/src/components/Modals/MangeWorkspace/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/index.jsx @@ -6,12 +6,15 @@ import System from "../../../models/system"; import { isMobile } from "react-device-detect"; import useUser from "../../../hooks/useUser"; import DocumentSettings from "./Documents"; +import DataConnectors from "./DataConnectors"; const noop = () => {}; const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { const { slug } = useParams(); + const { user } = useUser(); const [workspace, setWorkspace] = useState(null); const [settings, setSettings] = useState({}); + const [selectedTab, setSelectedTab] = useState("documents"); useEffect(() => { async function getSettings() { @@ -67,7 +70,6 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {
-
- + + {user?.role !== "default" && ( + + )} + + {selectedTab === "documents" ? ( + + ) : ( + + )}
@@ -84,6 +98,35 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { }; export default memo(ManageWorkspace); + +const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => { + return ( +
+
+ + +
+
+ ); +}; export function useManageWorkspaceModal() { const { user } = useUser(); const [showing, setShowing] = useState(false); diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index 66f881ff6..15e53aa5e 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -15,7 +15,6 @@ import { House, List, FileCode, - Plugs, Notepad, CodeBlock, Barcode, @@ -75,11 +74,10 @@ export default function SettingsSidebar() { className={`z-99 fixed top-0 left-0 transition-all duration-500 w-[100vw] h-[100vh]`} >
setShowSidebar(false)} />
{React.cloneElement(icon, { weight: isActive ? "fill" : "regular" })} @@ -207,9 +204,8 @@ const Option = ({
{!!subOptions && (isActive || hasActiveChild) && (
{subOptions}
@@ -304,14 +300,6 @@ const SidebarOptions = ({ user = null }) => ( flex={true} allowedRole={["admin"]} /> -
Manage embedding → diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index 40450d4e1..67797d266 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -20,6 +20,7 @@ import { Barcode, ClosedCaptioning, EyeSlash, + SplitVertical, } from "@phosphor-icons/react"; import useUser from "@/hooks/useUser"; import { USER_BACKGROUND_COLOR } from "@/utils/constants"; @@ -288,12 +289,25 @@ const SidebarOptions = ({ user = null }) => ( allowedRole={["admin"]} />