From 8962e2d39cf3ea2367068fe30642b745777ea124 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Fri, 12 Jan 2024 17:48:07 -0800 Subject: [PATCH 01/13] WIP model selection per workspace (migrations and openai saves properly --- .../LLMSelection/OpenAiOptions/index.jsx | 62 ++++--- .../Modals/MangeWorkspace/Settings/index.jsx | 154 +++++++++++++++++- .../Modals/MangeWorkspace/index.jsx | 16 +- server/models/workspace.js | 1 + .../20240113013409_init/migration.sql | 2 + server/prisma/schema.prisma | 1 + 6 files changed, 206 insertions(+), 30 deletions(-) create mode 100644 server/prisma/migrations/20240113013409_init/migration.sql diff --git a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx index cbd83edb99a..52ceca0cc27 100644 --- a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx @@ -1,35 +1,41 @@ import { useState, useEffect } from "react"; import System from "@/models/system"; -export default function OpenAiOptions({ settings }) { +export default function OpenAiOptions({ settings, isWorkspace = false }) { const [inputValue, setInputValue] = useState(settings?.OpenAiKey); const [openAIKey, setOpenAIKey] = useState(settings?.OpenAiKey); return (
-
- - setInputValue(e.target.value)} - onBlur={() => setOpenAIKey(inputValue)} - /> -
- + {!isWorkspace && ( +
+ + setInputValue(e.target.value)} + onBlur={() => setOpenAIKey(inputValue)} + /> +
+ )} +
); } -function OpenAIModelSelection({ apiKey, settings }) { +function OpenAIModelSelection({ apiKey, settings, isWorkspace = false }) { const [customModels, setCustomModels] = useState([]); const [loading, setLoading] = useState(true); @@ -54,9 +60,11 @@ function OpenAIModelSelection({ apiKey, settings }) { if (loading) { return (
- + {!isWorkspace && ( + + )} { setSaving(true); @@ -38,6 +50,8 @@ export default function WorkspaceSettings({ active, workspace }) { const data = {}; const form = new FormData(formEl.current); for (var [key, value] of form.entries()) data[key] = castToType(key, value); + + console.log(data); const { workspace: updatedWorkspace, message } = await Workspace.update( workspace.slug, data @@ -64,6 +78,80 @@ export default function WorkspaceSettings({ active, workspace }) { : window.location.reload(); }; + // const LLMS = [ + // { + // name: "OpenAI", + // value: "openai", + // options: , + // description: "The standard option for most non-commercial use.", + // }, + // { + // name: "Azure OpenAI", + // value: "azure", + // options: , + // description: "The enterprise option of OpenAI hosted on Azure services.", + // }, + // { + // name: "Anthropic", + // value: "anthropic", + // options: , + // description: "A friendly AI Assistant hosted by Anthropic.", + // }, + // { + // name: "Gemini", + // value: "gemini", + // options: , + // description: "Google's largest and most capable AI model", + // }, + // { + // name: "Ollama", + // value: "ollama", + // options: , + // description: "Run LLMs locally on your own machine.", + // }, + // { + // name: "LM Studio", + // value: "lmstudio", + // options: , + // description: + // "Discover, download, and run thousands of cutting edge LLMs in a few clicks.", + // }, + // { + // name: "Local AI", + // value: "localai", + // options: , + // description: "Run LLMs locally on your own machine.", + // }, + // { + // name: "Together AI", + // value: "togetherai", + // options: , + // description: "Run open source models from Together AI.", + // }, + // { + // name: "Native", + // value: "native", + // options: , + // description: + // "Use a downloaded custom Llama model for chatting on this AnythingLLM instance.", + // }, + // ]; + + // const selectedLLMOptions = LLMS.find( + // (llm) => llm.value === settings?.LLMProvider + // )?.options; + + // const llmName = LLMS.find((llm) => llm.value === settings?.LLMProvider).name; + + useEffect(() => { + async function getCustomModels() { + const customModels = await System.customModels(settings?.LLMProvider); + setCustomModels(customModels?.models || []); + } + + getCustomModels(); + }, [settings?.LLMProvider]); + return (
@@ -99,6 +187,70 @@ export default function WorkspaceSettings({ active, workspace }) {
+ {/* Chat model */} +
+
+ +

+ The specific chat model that will be used for this + workspace. +

+
+ +
+
diff --git a/server/models/workspace.js b/server/models/workspace.js index 9139c25e9d7..793f7a6be5c 100644 --- a/server/models/workspace.js +++ b/server/models/workspace.js @@ -14,6 +14,7 @@ const Workspace = { "lastUpdatedAt", "openAiPrompt", "similarityThreshold", + "chatModel", ], new: async function (name = null, creatorId = null) { diff --git a/server/prisma/migrations/20240113013409_init/migration.sql b/server/prisma/migrations/20240113013409_init/migration.sql new file mode 100644 index 00000000000..09b9448ec84 --- /dev/null +++ b/server/prisma/migrations/20240113013409_init/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "workspaces" ADD COLUMN "chatModel" TEXT; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index e9aa8a8a51d..2f632a46ab8 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -93,6 +93,7 @@ model workspaces { lastUpdatedAt DateTime @default(now()) openAiPrompt String? similarityThreshold Float? @default(0.25) + chatModel String? workspace_users workspace_users[] documents workspace_documents[] } From eea080369b2aa1fc13d44ee319b32b907bab85cc Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Fri, 12 Jan 2024 17:57:42 -0800 Subject: [PATCH 02/13] revert OpenAiOption --- .../LLMSelection/OpenAiOptions/index.jsx | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx index 52ceca0cc27..cbd83edb99a 100644 --- a/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OpenAiOptions/index.jsx @@ -1,41 +1,35 @@ import { useState, useEffect } from "react"; import System from "@/models/system"; -export default function OpenAiOptions({ settings, isWorkspace = false }) { +export default function OpenAiOptions({ settings }) { const [inputValue, setInputValue] = useState(settings?.OpenAiKey); const [openAIKey, setOpenAIKey] = useState(settings?.OpenAiKey); return (
- {!isWorkspace && ( -
- - setInputValue(e.target.value)} - onBlur={() => setOpenAIKey(inputValue)} - /> -
- )} - +
+ + setInputValue(e.target.value)} + onBlur={() => setOpenAIKey(inputValue)} + /> +
+
); } -function OpenAIModelSelection({ apiKey, settings, isWorkspace = false }) { +function OpenAIModelSelection({ apiKey, settings }) { const [customModels, setCustomModels] = useState([]); const [loading, setLoading] = useState(true); @@ -60,11 +54,9 @@ function OpenAIModelSelection({ apiKey, settings, isWorkspace = false }) { if (loading) { return (
- {!isWorkspace && ( - - )} + Date: Mon, 15 Jan 2024 20:08:00 -0800 Subject: [PATCH 03/13] add support for models per workspace for anthropic, localAi, ollama, openAi, and togetherAi --- .../Settings/ChatModelSelection.jsx | 330 ++++++++++++++++++ .../Modals/MangeWorkspace/Settings/index.jsx | 224 +++++------- frontend/src/models/system.js | 11 + .../GeneralSettings/LLMPreference/index.jsx | 5 +- server/endpoints/system.js | 14 + server/models/workspace.js | 14 + server/utils/AiProviders/anthropic/index.js | 24 +- server/utils/AiProviders/localAi/index.js | 54 ++- server/utils/AiProviders/ollama/index.js | 54 ++- server/utils/AiProviders/openAi/index.js | 54 ++- server/utils/AiProviders/togetherAi/index.js | 54 ++- server/utils/chats/stream.js | 8 +- 12 files changed, 630 insertions(+), 216 deletions(-) create mode 100644 frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelSelection.jsx diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelSelection.jsx b/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelSelection.jsx new file mode 100644 index 00000000000..f18c45187f6 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/Settings/ChatModelSelection.jsx @@ -0,0 +1,330 @@ +import { useEffect, useState } from "react"; + +export default function ChatModelSelection({ + settings, + workspace, + setHasChanges, + customModels, + loadingModels, +}) { + const [groupedModels, setGroupedModels] = useState({}); + + useEffect(() => { + if (settings?.LLMProvider === "togetherai") { + if (customModels?.length > 0) { + const modelsByOrganization = customModels.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + + setGroupedModels(modelsByOrganization); + } + } + }, [customModels, settings]); + + if (loadingModels) { + return ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ +
+ ); + } + + return ( + <> + {settings?.LLMProvider === "openai" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ + +
+ )} + + {settings?.LLMProvider === "anthropic" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ +
+ )} + + {settings?.LLMProvider === "localai" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ + +
+ )} + + {settings?.LLMProvider === "ollama" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ +
+ )} + + {settings?.LLMProvider === "togetherai" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ +
+ )} + + {/* Disabling for now due to having to reload the model into memory */} + {/* {settings?.LLMProvider === "native" && ( +
+
+ +

+ The specific chat model that will be used for this workspace. +

+
+ +
+ )} */} + + ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx index 55ed11bf9e2..6547ca4c74b 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx @@ -6,16 +6,7 @@ import System from "../../../../models/system"; import PreLoader from "../../../Preloader"; import { useParams } from "react-router-dom"; import showToast from "../../../../utils/toast"; - -import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; -import AzureAiOptions from "@/components/LLMSelection/AzureAiOptions"; -import AnthropicAiOptions from "@/components/LLMSelection/AnthropicAiOptions"; -import GeminiLLMOptions from "@/components/LLMSelection/GeminiLLMOptions"; -import OllamaLLMOptions from "@/components/LLMSelection/OllamaLLMOptions"; -import LMStudioOptions from "@/components/LLMSelection/LMStudioOptions"; -import LocalAiOptions from "@/components/LLMSelection/LocalAiOptions"; -import TogetherAiOptions from "@/components/LLMSelection/TogetherAiOptions"; -import NativeLLMOptions from "@/components/LLMSelection/NativeLLMOptions"; +import ChatModelSelection from "./ChatModelSelection"; // Ensure that a type is correct before sending the body // to the backend. @@ -37,12 +28,12 @@ function castToType(key, value) { } export default function WorkspaceSettings({ active, workspace, settings }) { - console.log("Workspace: ", workspace); const { slug } = useParams(); const formEl = useRef(null); const [saving, setSaving] = useState(false); const [hasChanges, setHasChanges] = useState(false); const [customModels, setCustomModels] = useState([]); + const [loadingModels, setLoadingModels] = useState(true); const handleUpdate = async (e) => { setSaving(true); @@ -50,8 +41,6 @@ export default function WorkspaceSettings({ active, workspace, settings }) { const data = {}; const form = new FormData(formEl.current); for (var [key, value] of form.entries()) data[key] = castToType(key, value); - - console.log(data); const { workspace: updatedWorkspace, message } = await Workspace.update( workspace.slug, data @@ -78,79 +67,87 @@ export default function WorkspaceSettings({ active, workspace, settings }) { : window.location.reload(); }; - // const LLMS = [ - // { - // name: "OpenAI", - // value: "openai", - // options: , - // description: "The standard option for most non-commercial use.", - // }, - // { - // name: "Azure OpenAI", - // value: "azure", - // options: , - // description: "The enterprise option of OpenAI hosted on Azure services.", - // }, - // { - // name: "Anthropic", - // value: "anthropic", - // options: , - // description: "A friendly AI Assistant hosted by Anthropic.", - // }, - // { - // name: "Gemini", - // value: "gemini", - // options: , - // description: "Google's largest and most capable AI model", - // }, - // { - // name: "Ollama", - // value: "ollama", - // options: , - // description: "Run LLMs locally on your own machine.", - // }, - // { - // name: "LM Studio", - // value: "lmstudio", - // options: , - // description: - // "Discover, download, and run thousands of cutting edge LLMs in a few clicks.", - // }, - // { - // name: "Local AI", - // value: "localai", - // options: , - // description: "Run LLMs locally on your own machine.", - // }, - // { - // name: "Together AI", - // value: "togetherai", - // options: , - // description: "Run open source models from Together AI.", - // }, - // { - // name: "Native", - // value: "native", - // options: , - // description: - // "Use a downloaded custom Llama model for chatting on this AnythingLLM instance.", - // }, - // ]; + // useEffect(() => { + // async function getCustomModels() { + // let customModels; + + // if (settings?.LLMProvider === "openai") { + // customModels = await System.customModels(settings?.LLMProvider); + // } + + // if (settings?.LLMProvider === "localai") { + // customModels = await System.customModels( + // "localai", + // null, + // settings?.LocalAiBasePath + // ); + + // } + + // if (settings?.LLMProvider === "ollama") { + // customModels = await System.customModels( + // "ollama", + // null, + // settings?.OllamaLLMBasePath + // ); + // } + + // if (settings?.LLMProvider === "togetherai") { + // customModels = await System.customModels("togetherai"); + // } - // const selectedLLMOptions = LLMS.find( - // (llm) => llm.value === settings?.LLMProvider - // )?.options; + // if (settings?.LLMProvider === "native") { + // customModels = await System.customModels("native-llm", null, null); + // } - // const llmName = LLMS.find((llm) => llm.value === settings?.LLMProvider).name; + // setLoadingModels(false); + // setCustomModels(customModels?.models || []); + // } + + // getCustomModels(); + // }, [settings?.LLMProvider]); useEffect(() => { async function getCustomModels() { - const customModels = await System.customModels(settings?.LLMProvider); - setCustomModels(customModels?.models || []); + let provider = settings?.LLMProvider; + let basePath = null; + + if (provider) { + switch (provider) { + case "localai": + basePath = settings?.LocalAiBasePath; + break; + case "ollama": + basePath = settings?.OllamaLLMBasePath; + break; + case "native": + provider = "native-llm"; + break; + default: + break; + } + + try { + const customModels = await System.customModels( + provider, + null, + basePath + ); + setCustomModels(customModels?.models || []); + } catch (error) { + console.error("Error fetching custom models:", error); + } + } + + setLoadingModels(false); } getCustomModels(); - }, [settings?.LLMProvider]); + }, [ + settings?.LLMProvider, + settings?.LocalAiBasePath, + settings?.OllamaLLMBasePath, + ]); return ( @@ -187,70 +184,13 @@ export default function WorkspaceSettings({ active, workspace, settings }) {
- {/* Chat model */} -
-
- -

- The specific chat model that will be used for this - workspace. -

-
- -
- +