mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-04-13 16:18:11 +00:00
* wip remove all docs clear vector db on embedder/vector db change * purge all cached docs and remove docs from workspaces on vectordb/embedder change * lint * remove unneeded console log * remove reset vector stores endpoint and move to server side updateENV with postUpdate check * reset embed module * remove unused import * simplify deletion process rescoped document deletion to be more general for speed, everything needs to be reset anyway fixed issue where unembedded docs not in any workspaces, but cached, were not removed * add back missing readme file update warning text modals --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
1027 lines
25 KiB
JavaScript
1027 lines
25 KiB
JavaScript
const { resetAllVectorStores } = require("../vectorStore/resetAllVectorStores");
|
|
|
|
const KEY_MAPPING = {
|
|
LLMProvider: {
|
|
envKey: "LLM_PROVIDER",
|
|
checks: [isNotEmpty, supportedLLM],
|
|
},
|
|
// OpenAI Settings
|
|
OpenAiKey: {
|
|
envKey: "OPEN_AI_KEY",
|
|
checks: [isNotEmpty, validOpenAIKey],
|
|
},
|
|
OpenAiModelPref: {
|
|
envKey: "OPEN_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
// Azure OpenAI Settings
|
|
AzureOpenAiEndpoint: {
|
|
envKey: "AZURE_OPENAI_ENDPOINT",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AzureOpenAiTokenLimit: {
|
|
envKey: "AZURE_OPENAI_TOKEN_LIMIT",
|
|
checks: [validOpenAiTokenLimit],
|
|
},
|
|
AzureOpenAiKey: {
|
|
envKey: "AZURE_OPENAI_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AzureOpenAiModelPref: {
|
|
envKey: "OPEN_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AzureOpenAiEmbeddingModelPref: {
|
|
envKey: "EMBEDDING_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Anthropic Settings
|
|
AnthropicApiKey: {
|
|
envKey: "ANTHROPIC_API_KEY",
|
|
checks: [isNotEmpty, validAnthropicApiKey],
|
|
},
|
|
AnthropicModelPref: {
|
|
envKey: "ANTHROPIC_MODEL_PREF",
|
|
checks: [isNotEmpty, validAnthropicModel],
|
|
},
|
|
|
|
GeminiLLMApiKey: {
|
|
envKey: "GEMINI_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
GeminiLLMModelPref: {
|
|
envKey: "GEMINI_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty, validGeminiModel],
|
|
},
|
|
GeminiSafetySetting: {
|
|
envKey: "GEMINI_SAFETY_SETTING",
|
|
checks: [validGeminiSafetySetting],
|
|
},
|
|
|
|
// LMStudio Settings
|
|
LMStudioBasePath: {
|
|
envKey: "LMSTUDIO_BASE_PATH",
|
|
checks: [isNotEmpty, validLLMExternalBasePath, validDockerizedUrl],
|
|
},
|
|
LMStudioModelPref: {
|
|
envKey: "LMSTUDIO_MODEL_PREF",
|
|
checks: [],
|
|
},
|
|
LMStudioTokenLimit: {
|
|
envKey: "LMSTUDIO_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// LocalAI Settings
|
|
LocalAiBasePath: {
|
|
envKey: "LOCAL_AI_BASE_PATH",
|
|
checks: [isNotEmpty, validLLMExternalBasePath, validDockerizedUrl],
|
|
},
|
|
LocalAiModelPref: {
|
|
envKey: "LOCAL_AI_MODEL_PREF",
|
|
checks: [],
|
|
},
|
|
LocalAiTokenLimit: {
|
|
envKey: "LOCAL_AI_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
LocalAiApiKey: {
|
|
envKey: "LOCAL_AI_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
OllamaLLMBasePath: {
|
|
envKey: "OLLAMA_BASE_PATH",
|
|
checks: [isNotEmpty, validOllamaLLMBasePath, validDockerizedUrl],
|
|
},
|
|
OllamaLLMModelPref: {
|
|
envKey: "OLLAMA_MODEL_PREF",
|
|
checks: [],
|
|
},
|
|
OllamaLLMTokenLimit: {
|
|
envKey: "OLLAMA_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
OllamaLLMPerformanceMode: {
|
|
envKey: "OLLAMA_PERFORMANCE_MODE",
|
|
checks: [],
|
|
},
|
|
OllamaLLMKeepAliveSeconds: {
|
|
envKey: "OLLAMA_KEEP_ALIVE_TIMEOUT",
|
|
checks: [isInteger],
|
|
},
|
|
|
|
// Mistral AI API Settings
|
|
MistralApiKey: {
|
|
envKey: "MISTRAL_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
MistralModelPref: {
|
|
envKey: "MISTRAL_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Native LLM Settings
|
|
NativeLLMModelPref: {
|
|
envKey: "NATIVE_LLM_MODEL_PREF",
|
|
checks: [isDownloadedModel],
|
|
},
|
|
NativeLLMTokenLimit: {
|
|
envKey: "NATIVE_LLM_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// Hugging Face LLM Inference Settings
|
|
HuggingFaceLLMEndpoint: {
|
|
envKey: "HUGGING_FACE_LLM_ENDPOINT",
|
|
checks: [isNotEmpty, isValidURL, validHuggingFaceEndpoint],
|
|
},
|
|
HuggingFaceLLMAccessToken: {
|
|
envKey: "HUGGING_FACE_LLM_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
HuggingFaceLLMTokenLimit: {
|
|
envKey: "HUGGING_FACE_LLM_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// KoboldCPP Settings
|
|
KoboldCPPBasePath: {
|
|
envKey: "KOBOLD_CPP_BASE_PATH",
|
|
checks: [isNotEmpty, isValidURL],
|
|
},
|
|
KoboldCPPModelPref: {
|
|
envKey: "KOBOLD_CPP_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
KoboldCPPTokenLimit: {
|
|
envKey: "KOBOLD_CPP_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// Text Generation Web UI Settings
|
|
TextGenWebUIBasePath: {
|
|
envKey: "TEXT_GEN_WEB_UI_BASE_PATH",
|
|
checks: [isValidURL],
|
|
},
|
|
TextGenWebUITokenLimit: {
|
|
envKey: "TEXT_GEN_WEB_UI_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
TextGenWebUIAPIKey: {
|
|
envKey: "TEXT_GEN_WEB_UI_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
// LiteLLM Settings
|
|
LiteLLMModelPref: {
|
|
envKey: "LITE_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
LiteLLMTokenLimit: {
|
|
envKey: "LITE_LLM_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
LiteLLMBasePath: {
|
|
envKey: "LITE_LLM_BASE_PATH",
|
|
checks: [isValidURL],
|
|
},
|
|
LiteLLMApiKey: {
|
|
envKey: "LITE_LLM_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
// Generic OpenAI InferenceSettings
|
|
GenericOpenAiBasePath: {
|
|
envKey: "GENERIC_OPEN_AI_BASE_PATH",
|
|
checks: [isValidURL],
|
|
},
|
|
GenericOpenAiModelPref: {
|
|
envKey: "GENERIC_OPEN_AI_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
GenericOpenAiTokenLimit: {
|
|
envKey: "GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
GenericOpenAiKey: {
|
|
envKey: "GENERIC_OPEN_AI_API_KEY",
|
|
checks: [],
|
|
},
|
|
GenericOpenAiMaxTokens: {
|
|
envKey: "GENERIC_OPEN_AI_MAX_TOKENS",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// AWS Bedrock LLM InferenceSettings
|
|
AwsBedrockLLMConnectionMethod: {
|
|
envKey: "AWS_BEDROCK_LLM_CONNECTION_METHOD",
|
|
checks: [
|
|
(input) =>
|
|
["iam", "sessionToken"].includes(input) ? null : "Invalid value",
|
|
],
|
|
},
|
|
AwsBedrockLLMAccessKeyId: {
|
|
envKey: "AWS_BEDROCK_LLM_ACCESS_KEY_ID",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AwsBedrockLLMAccessKey: {
|
|
envKey: "AWS_BEDROCK_LLM_ACCESS_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AwsBedrockLLMSessionToken: {
|
|
envKey: "AWS_BEDROCK_LLM_SESSION_TOKEN",
|
|
checks: [],
|
|
},
|
|
AwsBedrockLLMRegion: {
|
|
envKey: "AWS_BEDROCK_LLM_REGION",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AwsBedrockLLMModel: {
|
|
envKey: "AWS_BEDROCK_LLM_MODEL_PREFERENCE",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AwsBedrockLLMTokenLimit: {
|
|
envKey: "AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
EmbeddingEngine: {
|
|
envKey: "EMBEDDING_ENGINE",
|
|
checks: [supportedEmbeddingModel],
|
|
postUpdate: [handleVectorStoreReset],
|
|
},
|
|
EmbeddingBasePath: {
|
|
envKey: "EMBEDDING_BASE_PATH",
|
|
checks: [isNotEmpty, validDockerizedUrl],
|
|
},
|
|
EmbeddingModelPref: {
|
|
envKey: "EMBEDDING_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
postUpdate: [handleVectorStoreReset],
|
|
},
|
|
EmbeddingModelMaxChunkLength: {
|
|
envKey: "EMBEDDING_MODEL_MAX_CHUNK_LENGTH",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// Generic OpenAI Embedding Settings
|
|
GenericOpenAiEmbeddingApiKey: {
|
|
envKey: "GENERIC_OPEN_AI_EMBEDDING_API_KEY",
|
|
checks: [],
|
|
},
|
|
GenericOpenAiEmbeddingMaxConcurrentChunks: {
|
|
envKey: "GENERIC_OPEN_AI_EMBEDDING_MAX_CONCURRENT_CHUNKS",
|
|
checks: [nonZero],
|
|
},
|
|
|
|
// Vector Database Selection Settings
|
|
VectorDB: {
|
|
envKey: "VECTOR_DB",
|
|
checks: [isNotEmpty, supportedVectorDB],
|
|
postUpdate: [handleVectorStoreReset],
|
|
},
|
|
|
|
// Chroma Options
|
|
ChromaEndpoint: {
|
|
envKey: "CHROMA_ENDPOINT",
|
|
checks: [isValidURL, validChromaURL, validDockerizedUrl],
|
|
},
|
|
ChromaApiHeader: {
|
|
envKey: "CHROMA_API_HEADER",
|
|
checks: [],
|
|
},
|
|
ChromaApiKey: {
|
|
envKey: "CHROMA_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
// Weaviate Options
|
|
WeaviateEndpoint: {
|
|
envKey: "WEAVIATE_ENDPOINT",
|
|
checks: [isValidURL, validDockerizedUrl],
|
|
},
|
|
WeaviateApiKey: {
|
|
envKey: "WEAVIATE_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
// QDrant Options
|
|
QdrantEndpoint: {
|
|
envKey: "QDRANT_ENDPOINT",
|
|
checks: [isValidURL, validDockerizedUrl],
|
|
},
|
|
QdrantApiKey: {
|
|
envKey: "QDRANT_API_KEY",
|
|
checks: [],
|
|
},
|
|
PineConeKey: {
|
|
envKey: "PINECONE_API_KEY",
|
|
checks: [],
|
|
},
|
|
PineConeIndex: {
|
|
envKey: "PINECONE_INDEX",
|
|
checks: [],
|
|
},
|
|
|
|
// Milvus Options
|
|
MilvusAddress: {
|
|
envKey: "MILVUS_ADDRESS",
|
|
checks: [isValidURL, validDockerizedUrl],
|
|
},
|
|
MilvusUsername: {
|
|
envKey: "MILVUS_USERNAME",
|
|
checks: [isNotEmpty],
|
|
},
|
|
MilvusPassword: {
|
|
envKey: "MILVUS_PASSWORD",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Zilliz Cloud Options
|
|
ZillizEndpoint: {
|
|
envKey: "ZILLIZ_ENDPOINT",
|
|
checks: [isValidURL],
|
|
},
|
|
ZillizApiToken: {
|
|
envKey: "ZILLIZ_API_TOKEN",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Astra DB Options
|
|
|
|
AstraDBApplicationToken: {
|
|
envKey: "ASTRA_DB_APPLICATION_TOKEN",
|
|
checks: [isNotEmpty],
|
|
},
|
|
AstraDBEndpoint: {
|
|
envKey: "ASTRA_DB_ENDPOINT",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Together Ai Options
|
|
TogetherAiApiKey: {
|
|
envKey: "TOGETHER_AI_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
TogetherAiModelPref: {
|
|
envKey: "TOGETHER_AI_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Fireworks AI Options
|
|
FireworksAiLLMApiKey: {
|
|
envKey: "FIREWORKS_AI_LLM_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
FireworksAiLLMModelPref: {
|
|
envKey: "FIREWORKS_AI_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Perplexity Options
|
|
PerplexityApiKey: {
|
|
envKey: "PERPLEXITY_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
PerplexityModelPref: {
|
|
envKey: "PERPLEXITY_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// OpenRouter Options
|
|
OpenRouterApiKey: {
|
|
envKey: "OPENROUTER_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
OpenRouterModelPref: {
|
|
envKey: "OPENROUTER_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
OpenRouterTimeout: {
|
|
envKey: "OPENROUTER_TIMEOUT_MS",
|
|
checks: [],
|
|
},
|
|
|
|
// Novita Options
|
|
NovitaLLMApiKey: {
|
|
envKey: "NOVITA_LLM_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
NovitaLLMModelPref: {
|
|
envKey: "NOVITA_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
NovitaLLMTimeout: {
|
|
envKey: "NOVITA_LLM_TIMEOUT_MS",
|
|
checks: [],
|
|
},
|
|
|
|
// Groq Options
|
|
GroqApiKey: {
|
|
envKey: "GROQ_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
GroqModelPref: {
|
|
envKey: "GROQ_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Cohere Options
|
|
CohereApiKey: {
|
|
envKey: "COHERE_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
CohereModelPref: {
|
|
envKey: "COHERE_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// VoyageAi Options
|
|
VoyageAiApiKey: {
|
|
envKey: "VOYAGEAI_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Whisper (transcription) providers
|
|
WhisperProvider: {
|
|
envKey: "WHISPER_PROVIDER",
|
|
checks: [isNotEmpty, supportedTranscriptionProvider],
|
|
postUpdate: [],
|
|
},
|
|
WhisperModelPref: {
|
|
envKey: "WHISPER_MODEL_PREF",
|
|
checks: [validLocalWhisper],
|
|
postUpdate: [],
|
|
},
|
|
|
|
// System Settings
|
|
AuthToken: {
|
|
envKey: "AUTH_TOKEN",
|
|
checks: [requiresForceMode, noRestrictedChars],
|
|
},
|
|
JWTSecret: {
|
|
envKey: "JWT_SECRET",
|
|
checks: [requiresForceMode],
|
|
},
|
|
DisableTelemetry: {
|
|
envKey: "DISABLE_TELEMETRY",
|
|
checks: [],
|
|
},
|
|
|
|
// Agent Integration ENVs
|
|
AgentGoogleSearchEngineId: {
|
|
envKey: "AGENT_GSE_CTX",
|
|
checks: [],
|
|
},
|
|
AgentGoogleSearchEngineKey: {
|
|
envKey: "AGENT_GSE_KEY",
|
|
checks: [],
|
|
},
|
|
AgentSearchApiKey: {
|
|
envKey: "AGENT_SEARCHAPI_API_KEY",
|
|
checks: [],
|
|
},
|
|
AgentSearchApiEngine: {
|
|
envKey: "AGENT_SEARCHAPI_ENGINE",
|
|
checks: [],
|
|
},
|
|
AgentSerperApiKey: {
|
|
envKey: "AGENT_SERPER_DEV_KEY",
|
|
checks: [],
|
|
},
|
|
AgentBingSearchApiKey: {
|
|
envKey: "AGENT_BING_SEARCH_API_KEY",
|
|
checks: [],
|
|
},
|
|
AgentSerplyApiKey: {
|
|
envKey: "AGENT_SERPLY_API_KEY",
|
|
checks: [],
|
|
},
|
|
AgentSearXNGApiUrl: {
|
|
envKey: "AGENT_SEARXNG_API_URL",
|
|
checks: [],
|
|
},
|
|
AgentTavilyApiKey: {
|
|
envKey: "AGENT_TAVILY_API_KEY",
|
|
checks: [],
|
|
},
|
|
|
|
// TTS/STT Integration ENVS
|
|
TextToSpeechProvider: {
|
|
envKey: "TTS_PROVIDER",
|
|
checks: [supportedTTSProvider],
|
|
},
|
|
|
|
// TTS OpenAI
|
|
TTSOpenAIKey: {
|
|
envKey: "TTS_OPEN_AI_KEY",
|
|
checks: [validOpenAIKey],
|
|
},
|
|
TTSOpenAIVoiceModel: {
|
|
envKey: "TTS_OPEN_AI_VOICE_MODEL",
|
|
checks: [],
|
|
},
|
|
|
|
// TTS ElevenLabs
|
|
TTSElevenLabsKey: {
|
|
envKey: "TTS_ELEVEN_LABS_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
TTSElevenLabsVoiceModel: {
|
|
envKey: "TTS_ELEVEN_LABS_VOICE_MODEL",
|
|
checks: [],
|
|
},
|
|
|
|
// PiperTTS Local
|
|
TTSPiperTTSVoiceModel: {
|
|
envKey: "TTS_PIPER_VOICE_MODEL",
|
|
checks: [],
|
|
},
|
|
|
|
// OpenAI Generic TTS
|
|
TTSOpenAICompatibleKey: {
|
|
envKey: "TTS_OPEN_AI_COMPATIBLE_KEY",
|
|
checks: [],
|
|
},
|
|
TTSOpenAICompatibleVoiceModel: {
|
|
envKey: "TTS_OPEN_AI_COMPATIBLE_VOICE_MODEL",
|
|
checks: [isNotEmpty],
|
|
},
|
|
TTSOpenAICompatibleEndpoint: {
|
|
envKey: "TTS_OPEN_AI_COMPATIBLE_ENDPOINT",
|
|
checks: [isValidURL],
|
|
},
|
|
|
|
// DeepSeek Options
|
|
DeepSeekApiKey: {
|
|
envKey: "DEEPSEEK_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
DeepSeekModelPref: {
|
|
envKey: "DEEPSEEK_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// APIPie Options
|
|
ApipieLLMApiKey: {
|
|
envKey: "APIPIE_LLM_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
ApipieLLMModelPref: {
|
|
envKey: "APIPIE_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// xAI Options
|
|
XAIApiKey: {
|
|
envKey: "XAI_LLM_API_KEY",
|
|
checks: [isNotEmpty],
|
|
},
|
|
XAIModelPref: {
|
|
envKey: "XAI_LLM_MODEL_PREF",
|
|
checks: [isNotEmpty],
|
|
},
|
|
|
|
// Nvidia NIM Options
|
|
NvidiaNimLLMBasePath: {
|
|
envKey: "NVIDIA_NIM_LLM_BASE_PATH",
|
|
checks: [isValidURL],
|
|
postUpdate: [
|
|
(_, __, nextValue) => {
|
|
const { parseNvidiaNimBasePath } = require("../AiProviders/nvidiaNim");
|
|
process.env.NVIDIA_NIM_LLM_BASE_PATH =
|
|
parseNvidiaNimBasePath(nextValue);
|
|
},
|
|
],
|
|
},
|
|
NvidiaNimLLMModelPref: {
|
|
envKey: "NVIDIA_NIM_LLM_MODEL_PREF",
|
|
checks: [],
|
|
postUpdate: [
|
|
async (_, __, nextValue) => {
|
|
const { NvidiaNimLLM } = require("../AiProviders/nvidiaNim");
|
|
await NvidiaNimLLM.setModelTokenLimit(nextValue);
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
function isNotEmpty(input = "") {
|
|
return !input || input.length === 0 ? "Value cannot be empty" : null;
|
|
}
|
|
|
|
function nonZero(input = "") {
|
|
if (isNaN(Number(input))) return "Value must be a number";
|
|
return Number(input) <= 0 ? "Value must be greater than zero" : null;
|
|
}
|
|
|
|
function isInteger(input = "") {
|
|
if (isNaN(Number(input))) return "Value must be a number";
|
|
return Number(input);
|
|
}
|
|
|
|
function isValidURL(input = "") {
|
|
try {
|
|
new URL(input);
|
|
return null;
|
|
} catch (e) {
|
|
return "URL is not a valid URL.";
|
|
}
|
|
}
|
|
|
|
function validOpenAIKey(input = "") {
|
|
return input.startsWith("sk-") ? null : "OpenAI Key must start with sk-";
|
|
}
|
|
|
|
function validAnthropicApiKey(input = "") {
|
|
return input.startsWith("sk-ant-")
|
|
? null
|
|
: "Anthropic Key must start with sk-ant-";
|
|
}
|
|
|
|
function validLLMExternalBasePath(input = "") {
|
|
try {
|
|
new URL(input);
|
|
if (!input.includes("v1")) return "URL must include /v1";
|
|
if (input.split("").slice(-1)?.[0] === "/")
|
|
return "URL cannot end with a slash";
|
|
return null;
|
|
} catch {
|
|
return "Not a valid URL";
|
|
}
|
|
}
|
|
|
|
function validOllamaLLMBasePath(input = "") {
|
|
try {
|
|
new URL(input);
|
|
if (input.split("").slice(-1)?.[0] === "/")
|
|
return "URL cannot end with a slash";
|
|
return null;
|
|
} catch {
|
|
return "Not a valid URL";
|
|
}
|
|
}
|
|
|
|
function supportedTTSProvider(input = "") {
|
|
const validSelection = [
|
|
"native",
|
|
"openai",
|
|
"elevenlabs",
|
|
"piper_local",
|
|
"generic-openai",
|
|
].includes(input);
|
|
return validSelection ? null : `${input} is not a valid TTS provider.`;
|
|
}
|
|
|
|
function validLocalWhisper(input = "") {
|
|
const validSelection = [
|
|
"Xenova/whisper-small",
|
|
"Xenova/whisper-large",
|
|
].includes(input);
|
|
return validSelection
|
|
? null
|
|
: `${input} is not a valid Whisper model selection.`;
|
|
}
|
|
|
|
function supportedLLM(input = "") {
|
|
const validSelection = [
|
|
"openai",
|
|
"azure",
|
|
"anthropic",
|
|
"gemini",
|
|
"lmstudio",
|
|
"localai",
|
|
"ollama",
|
|
"native",
|
|
"togetherai",
|
|
"fireworksai",
|
|
"mistral",
|
|
"huggingface",
|
|
"perplexity",
|
|
"openrouter",
|
|
"novita",
|
|
"groq",
|
|
"koboldcpp",
|
|
"textgenwebui",
|
|
"cohere",
|
|
"litellm",
|
|
"generic-openai",
|
|
"bedrock",
|
|
"deepseek",
|
|
"apipie",
|
|
"xai",
|
|
"nvidia-nim",
|
|
].includes(input);
|
|
return validSelection ? null : `${input} is not a valid LLM provider.`;
|
|
}
|
|
|
|
function supportedTranscriptionProvider(input = "") {
|
|
const validSelection = ["openai", "local"].includes(input);
|
|
return validSelection
|
|
? null
|
|
: `${input} is not a valid transcription model provider.`;
|
|
}
|
|
|
|
function validGeminiModel(input = "") {
|
|
const validModels = [
|
|
"gemini-pro",
|
|
"gemini-1.0-pro",
|
|
"gemini-1.5-pro-latest",
|
|
"gemini-1.5-flash-latest",
|
|
"gemini-1.5-pro-exp-0801",
|
|
"gemini-1.5-pro-exp-0827",
|
|
"gemini-1.5-flash-exp-0827",
|
|
"gemini-1.5-flash-8b-exp-0827",
|
|
"gemini-exp-1114",
|
|
"gemini-exp-1121",
|
|
"gemini-exp-1206",
|
|
"learnlm-1.5-pro-experimental",
|
|
"gemini-2.0-flash-exp",
|
|
];
|
|
return validModels.includes(input)
|
|
? null
|
|
: `Invalid Model type. Must be one of ${validModels.join(", ")}.`;
|
|
}
|
|
|
|
function validGeminiSafetySetting(input = "") {
|
|
const validModes = [
|
|
"BLOCK_NONE",
|
|
"BLOCK_ONLY_HIGH",
|
|
"BLOCK_MEDIUM_AND_ABOVE",
|
|
"BLOCK_LOW_AND_ABOVE",
|
|
];
|
|
return validModes.includes(input)
|
|
? null
|
|
: `Invalid Safety setting. Must be one of ${validModes.join(", ")}.`;
|
|
}
|
|
|
|
function validAnthropicModel(input = "") {
|
|
const validModels = [
|
|
"claude-instant-1.2",
|
|
"claude-2.0",
|
|
"claude-2.1",
|
|
"claude-3-haiku-20240307",
|
|
"claude-3-sonnet-20240229",
|
|
"claude-3-opus-latest",
|
|
"claude-3-5-haiku-latest",
|
|
"claude-3-5-haiku-20241022",
|
|
"claude-3-5-sonnet-latest",
|
|
"claude-3-5-sonnet-20241022",
|
|
"claude-3-5-sonnet-20240620",
|
|
];
|
|
return validModels.includes(input)
|
|
? null
|
|
: `Invalid Model type. Must be one of ${validModels.join(", ")}.`;
|
|
}
|
|
|
|
function supportedEmbeddingModel(input = "") {
|
|
const supported = [
|
|
"openai",
|
|
"azure",
|
|
"localai",
|
|
"native",
|
|
"ollama",
|
|
"lmstudio",
|
|
"cohere",
|
|
"voyageai",
|
|
"litellm",
|
|
"generic-openai",
|
|
"mistral",
|
|
];
|
|
return supported.includes(input)
|
|
? null
|
|
: `Invalid Embedding model type. Must be one of ${supported.join(", ")}.`;
|
|
}
|
|
|
|
function supportedVectorDB(input = "") {
|
|
const supported = [
|
|
"chroma",
|
|
"pinecone",
|
|
"lancedb",
|
|
"weaviate",
|
|
"qdrant",
|
|
"milvus",
|
|
"zilliz",
|
|
"astra",
|
|
];
|
|
return supported.includes(input)
|
|
? null
|
|
: `Invalid VectorDB type. Must be one of ${supported.join(", ")}.`;
|
|
}
|
|
|
|
function validChromaURL(input = "") {
|
|
return input.slice(-1) === "/"
|
|
? `Chroma Instance URL should not end in a trailing slash.`
|
|
: null;
|
|
}
|
|
|
|
function validOpenAiTokenLimit(input = "") {
|
|
const tokenLimit = Number(input);
|
|
if (isNaN(tokenLimit)) return "Token limit is not a number";
|
|
if (![4_096, 16_384, 8_192, 32_768, 128_000].includes(tokenLimit))
|
|
return "Invalid OpenAI token limit.";
|
|
return null;
|
|
}
|
|
|
|
function requiresForceMode(_, forceModeEnabled = false) {
|
|
return forceModeEnabled === true ? null : "Cannot set this setting.";
|
|
}
|
|
|
|
function isDownloadedModel(input = "") {
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const storageDir = path.resolve(
|
|
process.env.STORAGE_DIR
|
|
? path.resolve(process.env.STORAGE_DIR, "models", "downloaded")
|
|
: path.resolve(__dirname, `../../storage/models/downloaded`)
|
|
);
|
|
if (!fs.existsSync(storageDir)) return false;
|
|
|
|
const files = fs
|
|
.readdirSync(storageDir)
|
|
.filter((file) => file.includes(".gguf"));
|
|
return files.includes(input);
|
|
}
|
|
|
|
async function validDockerizedUrl(input = "") {
|
|
if (process.env.ANYTHING_LLM_RUNTIME !== "docker") return null;
|
|
|
|
try {
|
|
const { isPortInUse, getLocalHosts } = require("./portAvailabilityChecker");
|
|
const localInterfaces = getLocalHosts();
|
|
const url = new URL(input);
|
|
const hostname = url.hostname.toLowerCase();
|
|
const port = parseInt(url.port, 10);
|
|
|
|
// If not a loopback, skip this check.
|
|
if (!localInterfaces.includes(hostname)) return null;
|
|
if (isNaN(port)) return "Invalid URL: Port is not specified or invalid";
|
|
|
|
const isPortAvailableFromDocker = await isPortInUse(port, hostname);
|
|
if (isPortAvailableFromDocker)
|
|
return "Port is not running a reachable service on loopback address from inside the AnythingLLM container. Please use host.docker.internal (for linux use 172.17.0.1), a real machine ip, or domain to connect to your service.";
|
|
} catch (error) {
|
|
console.error(error.message);
|
|
return "An error occurred while validating the URL";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function validHuggingFaceEndpoint(input = "") {
|
|
return input.slice(-6) !== ".cloud"
|
|
? `Your HF Endpoint should end in ".cloud"`
|
|
: null;
|
|
}
|
|
|
|
function noRestrictedChars(input = "") {
|
|
const regExp = new RegExp(/^[a-zA-Z0-9_\-!@$%^&*();]+$/);
|
|
return !regExp.test(input)
|
|
? `Your password has restricted characters in it. Allowed symbols are _,-,!,@,$,%,^,&,*,(,),;`
|
|
: null;
|
|
}
|
|
|
|
async function handleVectorStoreReset(key, prevValue, nextValue) {
|
|
if (prevValue === nextValue) return;
|
|
if (key === "VectorDB") {
|
|
console.log(
|
|
`Vector configuration changed from ${prevValue} to ${nextValue} - resetting ${prevValue} namespaces`
|
|
);
|
|
return await resetAllVectorStores({ vectorDbKey: prevValue });
|
|
}
|
|
|
|
if (key === "EmbeddingEngine" || key === "EmbeddingModelPref") {
|
|
console.log(
|
|
`${key} changed from ${prevValue} to ${nextValue} - resetting ${process.env.VECTOR_DB} namespaces`
|
|
);
|
|
return await resetAllVectorStores({ vectorDbKey: process.env.VECTOR_DB });
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 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
|
|
// and is simply for debugging when the .env not found issue many come across.
|
|
async function updateENV(newENVs = {}, force = false, userId = null) {
|
|
let error = "";
|
|
const validKeys = Object.keys(KEY_MAPPING);
|
|
const ENV_KEYS = Object.keys(newENVs).filter(
|
|
(key) => validKeys.includes(key) && !newENVs[key].includes("******") // strip out answers where the value is all asterisks
|
|
);
|
|
const newValues = {};
|
|
|
|
for (const key of ENV_KEYS) {
|
|
const { envKey, checks, postUpdate = [] } = KEY_MAPPING[key];
|
|
const prevValue = process.env[envKey];
|
|
const nextValue = newENVs[key];
|
|
|
|
const errors = await executeValidationChecks(checks, nextValue, force);
|
|
if (errors.length > 0) {
|
|
error += errors.join("\n");
|
|
break;
|
|
}
|
|
|
|
newValues[key] = nextValue;
|
|
process.env[envKey] = nextValue;
|
|
|
|
for (const postUpdateFunc of postUpdate)
|
|
await postUpdateFunc(key, prevValue, nextValue);
|
|
}
|
|
|
|
await logChangesToEventLog(newValues, userId);
|
|
if (process.env.NODE_ENV === "production") dumpENV();
|
|
return { newValues, error: error?.length > 0 ? error : false };
|
|
}
|
|
|
|
async function executeValidationChecks(checks, value, force) {
|
|
const results = await Promise.all(
|
|
checks.map((validator) => validator(value, force))
|
|
);
|
|
return results.filter((err) => typeof err === "string");
|
|
}
|
|
|
|
async function logChangesToEventLog(newValues = {}, userId = null) {
|
|
const { EventLogs } = require("../../models/eventLogs");
|
|
const eventMapping = {
|
|
LLMProvider: "update_llm_provider",
|
|
EmbeddingEngine: "update_embedding_engine",
|
|
VectorDB: "update_vector_db",
|
|
};
|
|
|
|
for (const [key, eventName] of Object.entries(eventMapping)) {
|
|
if (!newValues.hasOwnProperty(key)) continue;
|
|
await EventLogs.logEvent(eventName, {}, userId);
|
|
}
|
|
return;
|
|
}
|
|
|
|
function dumpENV() {
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const frozenEnvs = {};
|
|
const protectedKeys = [
|
|
...Object.values(KEY_MAPPING).map((values) => values.envKey),
|
|
// Manually Add Keys here which are not already defined in KEY_MAPPING
|
|
// and are either managed or manually set ENV key:values.
|
|
"STORAGE_DIR",
|
|
"SERVER_PORT",
|
|
// For persistent data encryption
|
|
"SIG_KEY",
|
|
"SIG_SALT",
|
|
// Password Schema Keys if present.
|
|
"PASSWORDMINCHAR",
|
|
"PASSWORDMAXCHAR",
|
|
"PASSWORDLOWERCASE",
|
|
"PASSWORDUPPERCASE",
|
|
"PASSWORDNUMERIC",
|
|
"PASSWORDSYMBOL",
|
|
"PASSWORDREQUIREMENTS",
|
|
// HTTPS SETUP KEYS
|
|
"ENABLE_HTTPS",
|
|
"HTTPS_CERT_PATH",
|
|
"HTTPS_KEY_PATH",
|
|
// Other Configuration Keys
|
|
"DISABLE_VIEW_CHAT_HISTORY",
|
|
// Simple SSO
|
|
"SIMPLE_SSO_ENABLED",
|
|
// Community Hub
|
|
"COMMUNITY_HUB_BUNDLE_DOWNLOADS_ENABLED",
|
|
|
|
// Nvidia NIM Keys that are automatically managed
|
|
"NVIDIA_NIM_LLM_MODEL_TOKEN_LIMIT",
|
|
];
|
|
|
|
// Simple sanitization of each value to prevent ENV injection via newline or quote escaping.
|
|
function sanitizeValue(value) {
|
|
const offendingChars =
|
|
/[\n\r\t\v\f\u0085\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000"'`#]/;
|
|
const firstOffendingCharIndex = value.search(offendingChars);
|
|
if (firstOffendingCharIndex === -1) return value;
|
|
|
|
return value.substring(0, firstOffendingCharIndex);
|
|
}
|
|
|
|
for (const key of protectedKeys) {
|
|
const envValue = process.env?.[key] || null;
|
|
if (!envValue) continue;
|
|
frozenEnvs[key] = process.env?.[key] || null;
|
|
}
|
|
|
|
var envResult = `# Auto-dump ENV from system call on ${new Date().toTimeString()}\n`;
|
|
envResult += Object.entries(frozenEnvs)
|
|
.map(([key, value]) => `${key}='${sanitizeValue(value)}'`)
|
|
.join("\n");
|
|
|
|
const envPath = path.join(__dirname, "../../.env");
|
|
fs.writeFileSync(envPath, envResult, { encoding: "utf8", flag: "w" });
|
|
return true;
|
|
}
|
|
|
|
module.exports = {
|
|
dumpENV,
|
|
updateENV,
|
|
};
|