2023-06-26 20:38:38 +02:00
const KEY _MAPPING = {
2023-08-04 23:56:27 +02:00
LLMProvider : {
envKey : "LLM_PROVIDER" ,
checks : [ isNotEmpty , supportedLLM ] ,
2024-01-17 21:59:25 +01:00
postUpdate : [ wipeWorkspaceModelPreference ] ,
2023-08-04 23:56:27 +02:00
} ,
// OpenAI Settings
2023-06-26 20:38:38 +02:00
OpenAiKey : {
envKey : "OPEN_AI_KEY" ,
checks : [ isNotEmpty , validOpenAIKey ] ,
} ,
OpenAiModelPref : {
envKey : "OPEN_MODEL_PREF" ,
2023-10-31 19:38:28 +01:00
checks : [ isNotEmpty ] ,
2023-06-26 20:38:38 +02:00
} ,
2023-08-04 23:56:27 +02:00
// Azure OpenAI Settings
AzureOpenAiEndpoint : {
envKey : "AZURE_OPENAI_ENDPOINT" ,
checks : [ isNotEmpty , validAzureURL ] ,
} ,
2023-11-06 22:13:53 +01:00
AzureOpenAiTokenLimit : {
envKey : "AZURE_OPENAI_TOKEN_LIMIT" ,
checks : [ validOpenAiTokenLimit ] ,
} ,
2023-08-04 23:56:27 +02:00
AzureOpenAiKey : {
envKey : "AZURE_OPENAI_KEY" ,
checks : [ isNotEmpty ] ,
} ,
AzureOpenAiModelPref : {
envKey : "OPEN_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
AzureOpenAiEmbeddingModelPref : {
envKey : "EMBEDDING_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-10-30 23:44:03 +01:00
// Anthropic Settings
AnthropicApiKey : {
envKey : "ANTHROPIC_API_KEY" ,
checks : [ isNotEmpty , validAnthropicApiKey ] ,
} ,
AnthropicModelPref : {
envKey : "ANTHROPIC_MODEL_PREF" ,
checks : [ isNotEmpty , validAnthropicModel ] ,
} ,
2023-12-28 02:08:03 +01:00
GeminiLLMApiKey : {
envKey : "GEMINI_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
GeminiLLMModelPref : {
envKey : "GEMINI_LLM_MODEL_PREF" ,
checks : [ isNotEmpty , validGeminiModel ] ,
} ,
2023-11-09 21:33:21 +01:00
// LMStudio Settings
LMStudioBasePath : {
envKey : "LMSTUDIO_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validLLMExternalBasePath , validDockerizedUrl ] ,
2023-11-09 21:33:21 +01:00
} ,
LMStudioTokenLimit : {
envKey : "LMSTUDIO_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-11-14 21:31:44 +01:00
// LocalAI Settings
LocalAiBasePath : {
envKey : "LOCAL_AI_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validLLMExternalBasePath , validDockerizedUrl ] ,
2023-11-14 21:31:44 +01:00
} ,
LocalAiModelPref : {
envKey : "LOCAL_AI_MODEL_PREF" ,
checks : [ ] ,
} ,
LocalAiTokenLimit : {
envKey : "LOCAL_AI_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-12-04 17:38:15 +01:00
LocalAiApiKey : {
envKey : "LOCAL_AI_API_KEY" ,
checks : [ ] ,
} ,
2023-11-14 21:31:44 +01:00
2023-12-28 02:21:47 +01:00
OllamaLLMBasePath : {
envKey : "OLLAMA_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validOllamaLLMBasePath , validDockerizedUrl ] ,
2023-12-28 02:21:47 +01:00
} ,
OllamaLLMModelPref : {
envKey : "OLLAMA_MODEL_PREF" ,
checks : [ ] ,
} ,
OllamaLLMTokenLimit : {
envKey : "OLLAMA_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2024-01-17 23:42:05 +01:00
MistralApiKey : {
envKey : "MISTRAL_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
MistralModelPref : {
envKey : "MISTRAL_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-12-07 23:48:27 +01:00
// Native LLM Settings
NativeLLMModelPref : {
envKey : "NATIVE_LLM_MODEL_PREF" ,
checks : [ isDownloadedModel ] ,
} ,
2024-01-18 01:25:30 +01:00
NativeLLMTokenLimit : {
envKey : "NATIVE_LLM_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-10-30 23:44:03 +01:00
EmbeddingEngine : {
envKey : "EMBEDDING_ENGINE" ,
checks : [ supportedEmbeddingModel ] ,
} ,
2023-11-14 22:49:31 +01:00
EmbeddingBasePath : {
envKey : "EMBEDDING_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validLLMExternalBasePath , validDockerizedUrl ] ,
2023-11-14 22:49:31 +01:00
} ,
EmbeddingModelPref : {
envKey : "EMBEDDING_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-12-08 01:27:36 +01:00
EmbeddingModelMaxChunkLength : {
envKey : "EMBEDDING_MODEL_MAX_CHUNK_LENGTH" ,
checks : [ nonZero ] ,
} ,
2023-10-30 23:44:03 +01:00
2023-08-04 23:56:27 +02:00
// Vector Database Selection Settings
2023-06-26 20:38:38 +02:00
VectorDB : {
envKey : "VECTOR_DB" ,
checks : [ isNotEmpty , supportedVectorDB ] ,
} ,
2023-09-29 22:20:06 +02:00
// Chroma Options
2023-06-26 20:38:38 +02:00
ChromaEndpoint : {
envKey : "CHROMA_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validChromaURL , validDockerizedUrl ] ,
2023-06-26 20:38:38 +02:00
} ,
2023-09-29 22:20:06 +02:00
ChromaApiHeader : {
envKey : "CHROMA_API_HEADER" ,
checks : [ ] ,
} ,
ChromaApiKey : {
envKey : "CHROMA_API_KEY" ,
checks : [ ] ,
} ,
// Weaviate Options
2023-08-09 03:02:30 +02:00
WeaviateEndpoint : {
envKey : "WEAVIATE_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validDockerizedUrl ] ,
2023-08-09 03:02:30 +02:00
} ,
WeaviateApiKey : {
envKey : "WEAVIATE_API_KEY" ,
checks : [ ] ,
} ,
2023-09-29 22:20:06 +02:00
// QDrant Options
2023-08-16 00:26:44 +02:00
QdrantEndpoint : {
envKey : "QDRANT_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validDockerizedUrl ] ,
2023-08-16 00:26:44 +02:00
} ,
QdrantApiKey : {
envKey : "QDRANT_API_KEY" ,
checks : [ ] ,
} ,
2023-06-26 20:38:38 +02:00
PineConeKey : {
envKey : "PINECONE_API_KEY" ,
checks : [ ] ,
} ,
PineConeIndex : {
envKey : "PINECONE_INDEX" ,
checks : [ ] ,
} ,
2023-08-04 23:56:27 +02:00
2024-01-12 22:23:57 +01:00
// Milvus Options
MilvusAddress : {
envKey : "MILVUS_ADDRESS" ,
checks : [ isValidURL , validDockerizedUrl ] ,
} ,
MilvusUsername : {
envKey : "MILVUS_USERNAME" ,
checks : [ isNotEmpty ] ,
} ,
MilvusPassword : {
envKey : "MILVUS_PASSWORD" ,
checks : [ isNotEmpty ] ,
} ,
2024-01-18 03:00:54 +01:00
// Zilliz Cloud Options
ZillizEndpoint : {
envKey : "ZILLIZ_ENDPOINT" ,
checks : [ isValidURL ] ,
} ,
ZillizApiToken : {
envKey : "ZILLIZ_API_TOKEN" ,
checks : [ isNotEmpty ] ,
} ,
2024-01-10 21:35:30 +01:00
// Together Ai Options
TogetherAiApiKey : {
envKey : "TOGETHER_AI_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
TogetherAiModelPref : {
envKey : "TOGETHER_AI_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-08-04 23:56:27 +02:00
// System Settings
2023-07-21 00:25:47 +02:00
AuthToken : {
envKey : "AUTH_TOKEN" ,
2023-09-29 19:44:40 +02:00
checks : [ requiresForceMode ] ,
2023-07-21 00:25:47 +02:00
} ,
JWTSecret : {
envKey : "JWT_SECRET" ,
2023-09-29 19:44:40 +02:00
checks : [ requiresForceMode ] ,
2023-07-21 00:25:47 +02:00
} ,
2023-06-26 20:38:38 +02:00
} ;
function isNotEmpty ( input = "" ) {
return ! input || input . length === 0 ? "Value cannot be empty" : null ;
}
2023-11-09 21:33:21 +01:00
function nonZero ( input = "" ) {
if ( isNaN ( Number ( input ) ) ) return "Value must be a number" ;
return Number ( input ) <= 0 ? "Value must be greater than zero" : null ;
}
2023-06-26 20:38:38 +02:00
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-" ;
}
2023-10-30 23:44:03 +01:00
function validAnthropicApiKey ( input = "" ) {
return input . startsWith ( "sk-ant-" )
? null
: "Anthropic Key must start with sk-ant-" ;
}
2023-11-14 21:31:44 +01:00
function validLLMExternalBasePath ( input = "" ) {
2023-11-09 21:33:21 +01:00
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" ;
}
}
2023-12-28 02:21:47 +01:00
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" ;
}
}
2023-08-04 23:56:27 +02:00
function supportedLLM ( input = "" ) {
2024-01-10 21:35:30 +01:00
const validSelection = [
2023-12-07 23:48:27 +01:00
"openai" ,
"azure" ,
"anthropic" ,
2023-12-28 02:08:03 +01:00
"gemini" ,
2023-12-07 23:48:27 +01:00
"lmstudio" ,
"localai" ,
2023-12-28 02:21:47 +01:00
"ollama" ,
2023-12-07 23:48:27 +01:00
"native" ,
2024-01-10 21:35:30 +01:00
"togetherai" ,
2024-01-17 23:42:05 +01:00
"mistral" ,
2023-12-07 23:48:27 +01:00
] . includes ( input ) ;
2024-01-10 21:35:30 +01:00
return validSelection ? null : ` ${ input } is not a valid LLM provider. ` ;
2023-08-04 23:56:27 +02:00
}
2023-12-28 02:08:03 +01:00
function validGeminiModel ( input = "" ) {
const validModels = [ "gemini-pro" ] ;
return validModels . includes ( input )
? null
: ` Invalid Model type. Must be one of ${ validModels . join ( ", " ) } . ` ;
}
2023-10-30 23:44:03 +01:00
function validAnthropicModel ( input = "" ) {
2023-11-06 22:13:53 +01:00
const validModels = [ "claude-2" , "claude-instant-1" ] ;
2023-10-30 23:44:03 +01:00
return validModels . includes ( input )
? null
: ` Invalid Model type. Must be one of ${ validModels . join ( ", " ) } . ` ;
}
function supportedEmbeddingModel ( input = "" ) {
2023-12-06 19:36:22 +01:00
const supported = [ "openai" , "azure" , "localai" , "native" ] ;
2023-10-30 23:44:03 +01:00
return supported . includes ( input )
? null
: ` Invalid Embedding model type. Must be one of ${ supported . join ( ", " ) } . ` ;
}
2023-06-26 20:38:38 +02:00
function supportedVectorDB ( input = "" ) {
2024-01-12 22:23:57 +01:00
const supported = [
"chroma" ,
"pinecone" ,
"lancedb" ,
"weaviate" ,
"qdrant" ,
"milvus" ,
2024-01-18 03:00:54 +01:00
"zilliz" ,
2024-01-12 22:23:57 +01:00
] ;
2023-06-26 20:38:38 +02:00
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 ;
}
2023-08-04 23:56:27 +02:00
function validAzureURL ( input = "" ) {
try {
new URL ( input ) ;
if ( ! input . includes ( "openai.azure.com" ) )
return "URL must include openai.azure.com" ;
return null ;
} catch {
return "Not a valid URL" ;
}
}
2023-11-06 22:13:53 +01:00
function validOpenAiTokenLimit ( input = "" ) {
const tokenLimit = Number ( input ) ;
if ( isNaN ( tokenLimit ) ) return "Token limit is not a number" ;
2024-01-02 21:49:48 +01:00
if ( ! [ 4_096 , 16_384 , 8_192 , 32_768 , 128_000 ] . includes ( tokenLimit ) )
2023-11-06 22:13:53 +01:00
return "Invalid OpenAI token limit." ;
return null ;
}
2023-09-29 19:44:40 +02:00
function requiresForceMode ( _ , forceModeEnabled = false ) {
return forceModeEnabled === true ? null : "Cannot set this setting." ;
}
2023-12-07 23:48:27 +01:00
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 ) ;
}
2023-12-28 19:47:02 +01:00
function validDockerizedUrl ( input = "" ) {
if ( process . env . ANYTHING _LLM _RUNTIME !== "docker" ) return null ;
try {
const { hostname } = new URL ( input ) ;
if ( [ "localhost" , "127.0.0.1" , "0.0.0.0" ] . includes ( hostname . toLowerCase ( ) ) )
return "Localhost, 127.0.0.1, or 0.0.0.0 origins cannot be reached from inside the AnythingLLM container. Please use host.docker.internal, a real machine ip, or domain to connect to your service." ;
return null ;
} catch { }
return null ;
}
2024-01-17 21:59:25 +01:00
// 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 ( ) ;
}
2023-06-26 20:38:38 +02:00
// 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.
2024-01-17 21:59:25 +01:00
async function updateENV ( newENVs = { } , force = false ) {
2023-06-26 20:38:38 +02:00
let error = "" ;
const validKeys = Object . keys ( KEY _MAPPING ) ;
2023-08-04 00:59:51 +02:00
const ENV _KEYS = Object . keys ( newENVs ) . filter (
( key ) => validKeys . includes ( key ) && ! newENVs [ key ] . includes ( "******" ) // strip out answers where the value is all asterisks
2023-06-26 20:38:38 +02:00
) ;
const newValues = { } ;
2024-01-17 21:59:25 +01:00
for ( const key of ENV _KEYS ) {
const { envKey , checks , postUpdate = [ ] } = KEY _MAPPING [ key ] ;
const prevValue = process . env [ envKey ] ;
const nextValue = newENVs [ key ] ;
2023-06-26 20:38:38 +02:00
const errors = checks
2024-01-17 21:59:25 +01:00
. map ( ( validityCheck ) => validityCheck ( nextValue , force ) )
2023-06-26 20:38:38 +02:00
. filter ( ( err ) => typeof err === "string" ) ;
if ( errors . length > 0 ) {
error += errors . join ( "\n" ) ;
2024-01-17 21:59:25 +01:00
break ;
2023-06-26 20:38:38 +02:00
}
2024-01-17 21:59:25 +01:00
newValues [ key ] = nextValue ;
process . env [ envKey ] = nextValue ;
for ( const postUpdateFunc of postUpdate )
await postUpdateFunc ( key , prevValue , nextValue ) ;
}
2023-06-26 20:38:38 +02:00
return { newValues , error : error ? . length > 0 ? error : false } ;
}
2023-08-16 01:00:27 +02:00
async function dumpENV ( ) {
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const frozenEnvs = { } ;
const protectedKeys = [
... Object . values ( KEY _MAPPING ) . map ( ( values ) => values . envKey ) ,
"STORAGE_DIR" ,
"SERVER_PORT" ,
2023-12-05 18:13:06 +01:00
// Password Schema Keys if present.
"PASSWORDMINCHAR" ,
"PASSWORDMAXCHAR" ,
"PASSWORDLOWERCASE" ,
"PASSWORDUPPERCASE" ,
"PASSWORDNUMERIC" ,
"PASSWORDSYMBOL" ,
"PASSWORDREQUIREMENTS" ,
2024-01-19 03:13:24 +01:00
// HTTPS SETUP KEYS
"ENABLE_HTTPS" ,
"HTTPS_CERT_PATH" ,
"HTTPS_KEY_PATH" ,
// DISABLED TELEMETRY
"DISABLE_TELEMETRY" ,
2023-08-16 01:00:27 +02:00
] ;
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 ] ) => {
return ` ${ key } =' ${ value } ' ` ;
} )
. join ( "\n" ) ;
const envPath = path . join ( _ _dirname , "../../.env" ) ;
fs . writeFileSync ( envPath , envResult , { encoding : "utf8" , flag : "w" } ) ;
return true ;
}
2023-06-26 20:38:38 +02:00
module . exports = {
2023-08-16 01:00:27 +02:00
dumpENV ,
2023-06-26 20:38:38 +02:00
updateENV ,
} ;