77
88from datetime import datetime
99from pathlib import Path
10- from typing import Generic , Optional , TypeVar
10+ from typing import Generic , Union , Optional , TypeVar
1111from urllib .parse import urlparse
1212
1313import requests
@@ -168,9 +168,19 @@ def __init__(self, env_name: str, config_path: str, env_value: T):
168168 self .config_path = config_path
169169 self .env_value = env_value
170170 self .config_value = get_config_value (config_path )
171+
171172 if self .config_value is not None and ENABLE_PERSISTENT_CONFIG :
172- log .info (f"'{ env_name } ' loaded from the latest database entry" )
173- self .value = self .config_value
173+ if (
174+ self .config_path .startswith ("oauth." )
175+ and not ENABLE_OAUTH_PERSISTENT_CONFIG
176+ ):
177+ log .info (
178+ f"Skipping loading of '{ env_name } ' as OAuth persistent config is disabled"
179+ )
180+ self .value = env_value
181+ else :
182+ log .info (f"'{ env_name } ' loaded from the latest database entry" )
183+ self .value = self .config_value
174184 else :
175185 self .value = env_value
176186
@@ -213,21 +223,27 @@ def save(self):
213223
214224class AppConfig :
215225 _state : dict [str , PersistentConfig ]
216- _redis : Optional [redis .Redis ] = None
226+ _redis : Union [redis .Redis , redis . cluster . RedisCluster ] = None
217227 _redis_key_prefix : str
218228
219229 def __init__ (
220230 self ,
221231 redis_url : Optional [str ] = None ,
222232 redis_sentinels : Optional [list ] = [],
233+ redis_cluster : Optional [bool ] = False ,
223234 redis_key_prefix : str = "open-webui" ,
224235 ):
225236 super ().__setattr__ ("_state" , {})
226237 super ().__setattr__ ("_redis_key_prefix" , redis_key_prefix )
227238 if redis_url :
228239 super ().__setattr__ (
229240 "_redis" ,
230- get_redis_connection (redis_url , redis_sentinels , decode_responses = True ),
241+ get_redis_connection (
242+ redis_url ,
243+ redis_sentinels ,
244+ redis_cluster ,
245+ decode_responses = True ,
246+ ),
231247 )
232248
233249 def __setattr__ (self , key , value ):
@@ -296,6 +312,9 @@ def __getattr__(self, key):
296312# OAuth config
297313####################################
298314
315+ ENABLE_OAUTH_PERSISTENT_CONFIG = (
316+ os .environ .get ("ENABLE_OAUTH_PERSISTENT_CONFIG" , "True" ).lower () == "true"
317+ )
299318
300319ENABLE_OAUTH_SIGNUP = PersistentConfig (
301320 "ENABLE_OAUTH_SIGNUP" ,
@@ -463,6 +482,12 @@ def __getattr__(self, key):
463482 os .environ .get ("OAUTH_PROVIDER_NAME" , "SSO" ),
464483)
465484
485+ OAUTH_SUB_CLAIM = PersistentConfig (
486+ "OAUTH_SUB_CLAIM" ,
487+ "oauth.oidc.sub_claim" ,
488+ os .environ .get ("OAUTH_SUB_CLAIM" , None ),
489+ )
490+
466491OAUTH_USERNAME_CLAIM = PersistentConfig (
467492 "OAUTH_USERNAME_CLAIM" ,
468493 "oauth.oidc.username_claim" ,
@@ -680,6 +705,23 @@ def oidc_oauth_register(client: OAuth):
680705 "register" : oidc_oauth_register ,
681706 }
682707
708+ configured_providers = []
709+ if GOOGLE_CLIENT_ID .value :
710+ configured_providers .append ("Google" )
711+ if MICROSOFT_CLIENT_ID .value :
712+ configured_providers .append ("Microsoft" )
713+ if GITHUB_CLIENT_ID .value :
714+ configured_providers .append ("GitHub" )
715+
716+ if configured_providers and not OPENID_PROVIDER_URL .value :
717+ provider_list = ", " .join (configured_providers )
718+ log .warning (
719+ f"⚠️ OAuth providers configured ({ provider_list } ) but OPENID_PROVIDER_URL not set - logout will not work!"
720+ )
721+ log .warning (
722+ f"Set OPENID_PROVIDER_URL to your OAuth provider's OpenID Connect discovery endpoint to fix logout functionality."
723+ )
724+
683725
684726load_oauth_providers ()
685727
@@ -1143,10 +1185,18 @@ def oidc_oauth_register(client: OAuth):
11431185 os .environ .get ("USER_PERMISSIONS_CHAT_CONTROLS" , "True" ).lower () == "true"
11441186)
11451187
1188+ USER_PERMISSIONS_CHAT_VALVES = (
1189+ os .environ .get ("USER_PERMISSIONS_CHAT_VALVES" , "True" ).lower () == "true"
1190+ )
1191+
11461192USER_PERMISSIONS_CHAT_SYSTEM_PROMPT = (
11471193 os .environ .get ("USER_PERMISSIONS_CHAT_SYSTEM_PROMPT" , "True" ).lower () == "true"
11481194)
11491195
1196+ USER_PERMISSIONS_CHAT_PARAMS = (
1197+ os .environ .get ("USER_PERMISSIONS_CHAT_PARAMS" , "True" ).lower () == "true"
1198+ )
1199+
11501200USER_PERMISSIONS_CHAT_FILE_UPLOAD = (
11511201 os .environ .get ("USER_PERMISSIONS_CHAT_FILE_UPLOAD" , "True" ).lower () == "true"
11521202)
@@ -1232,7 +1282,9 @@ def oidc_oauth_register(client: OAuth):
12321282 },
12331283 "chat" : {
12341284 "controls" : USER_PERMISSIONS_CHAT_CONTROLS ,
1285+ "valves" : USER_PERMISSIONS_CHAT_VALVES ,
12351286 "system_prompt" : USER_PERMISSIONS_CHAT_SYSTEM_PROMPT ,
1287+ "params" : USER_PERMISSIONS_CHAT_PARAMS ,
12361288 "file_upload" : USER_PERMISSIONS_CHAT_FILE_UPLOAD ,
12371289 "delete" : USER_PERMISSIONS_CHAT_DELETE ,
12381290 "edit" : USER_PERMISSIONS_CHAT_EDIT ,
@@ -1299,6 +1351,10 @@ def oidc_oauth_register(client: OAuth):
12991351
13001352ENABLE_ADMIN_EXPORT = os .environ .get ("ENABLE_ADMIN_EXPORT" , "True" ).lower () == "true"
13011353
1354+ ENABLE_ADMIN_WORKSPACE_CONTENT_ACCESS = (
1355+ os .environ .get ("ENABLE_ADMIN_WORKSPACE_CONTENT_ACCESS" , "True" ).lower () == "true"
1356+ )
1357+
13021358ENABLE_ADMIN_CHAT_ACCESS = (
13031359 os .environ .get ("ENABLE_ADMIN_CHAT_ACCESS" , "True" ).lower () == "true"
13041360)
@@ -1337,10 +1393,11 @@ def oidc_oauth_register(client: OAuth):
13371393def validate_cors_origin (origin ):
13381394 parsed_url = urlparse (origin )
13391395
1340- # Check if the scheme is either http or https
1341- if parsed_url .scheme not in ["http" , "https" ]:
1396+ # Check if the scheme is either http or https, or a custom scheme
1397+ schemes = ["http" , "https" ] + CORS_ALLOW_CUSTOM_SCHEME
1398+ if parsed_url .scheme not in schemes :
13421399 raise ValueError (
1343- f"Invalid scheme in CORS_ALLOW_ORIGIN: '{ origin } '. Only 'http' and 'https' are allowed."
1400+ f"Invalid scheme in CORS_ALLOW_ORIGIN: '{ origin } '. Only 'http' and 'https' and CORS_ALLOW_CUSTOM_SCHEME are allowed."
13441401 )
13451402
13461403 # Ensure that the netloc (domain + port) is present, indicating it's a valid URL
@@ -1355,6 +1412,11 @@ def validate_cors_origin(origin):
13551412# in your .env file depending on your frontend port, 5173 in this case.
13561413CORS_ALLOW_ORIGIN = os .environ .get ("CORS_ALLOW_ORIGIN" , "*" ).split (";" )
13571414
1415+ # Allows custom URL schemes (e.g., app://) to be used as origins for CORS.
1416+ # Useful for local development or desktop clients with schemes like app:// or other custom protocols.
1417+ # Provide a semicolon-separated list of allowed schemes in the environment variable CORS_ALLOW_CUSTOM_SCHEMES.
1418+ CORS_ALLOW_CUSTOM_SCHEME = os .environ .get ("CORS_ALLOW_CUSTOM_SCHEME" , "" ).split (";" )
1419+
13581420if CORS_ALLOW_ORIGIN == ["*" ]:
13591421 log .warning (
13601422 "\n \n WARNING: CORS_ALLOW_ORIGIN IS SET TO '*' - NOT RECOMMENDED FOR PRODUCTION DEPLOYMENTS.\n "
@@ -1862,6 +1924,8 @@ class BannerModel(BaseModel):
18621924QDRANT_ON_DISK = os .environ .get ("QDRANT_ON_DISK" , "false" ).lower () == "true"
18631925QDRANT_PREFER_GRPC = os .environ .get ("QDRANT_PREFER_GRPC" , "false" ).lower () == "true"
18641926QDRANT_GRPC_PORT = int (os .environ .get ("QDRANT_GRPC_PORT" , "6334" ))
1927+ QDRANT_TIMEOUT = int (os .environ .get ("QDRANT_TIMEOUT" , "5" ))
1928+ QDRANT_HNSW_M = int (os .environ .get ("QDRANT_HNSW_M" , "16" ))
18651929ENABLE_QDRANT_MULTITENANCY_MODE = (
18661930 os .environ .get ("ENABLE_QDRANT_MULTITENANCY_MODE" , "true" ).lower () == "true"
18671931)
@@ -1951,6 +2015,37 @@ class BannerModel(BaseModel):
19512015PINECONE_METRIC = os .getenv ("PINECONE_METRIC" , "cosine" )
19522016PINECONE_CLOUD = os .getenv ("PINECONE_CLOUD" , "aws" ) # or "gcp" or "azure"
19532017
2018+ # ORACLE23AI (Oracle23ai Vector Search)
2019+
2020+ ORACLE_DB_USE_WALLET = os .environ .get ("ORACLE_DB_USE_WALLET" , "false" ).lower () == "true"
2021+ ORACLE_DB_USER = os .environ .get ("ORACLE_DB_USER" , None ) #
2022+ ORACLE_DB_PASSWORD = os .environ .get ("ORACLE_DB_PASSWORD" , None ) #
2023+ ORACLE_DB_DSN = os .environ .get ("ORACLE_DB_DSN" , None ) #
2024+ ORACLE_WALLET_DIR = os .environ .get ("ORACLE_WALLET_DIR" , None )
2025+ ORACLE_WALLET_PASSWORD = os .environ .get ("ORACLE_WALLET_PASSWORD" , None )
2026+ ORACLE_VECTOR_LENGTH = os .environ .get ("ORACLE_VECTOR_LENGTH" , 768 )
2027+
2028+ ORACLE_DB_POOL_MIN = int (os .environ .get ("ORACLE_DB_POOL_MIN" , 2 ))
2029+ ORACLE_DB_POOL_MAX = int (os .environ .get ("ORACLE_DB_POOL_MAX" , 10 ))
2030+ ORACLE_DB_POOL_INCREMENT = int (os .environ .get ("ORACLE_DB_POOL_INCREMENT" , 1 ))
2031+
2032+
2033+ if VECTOR_DB == "oracle23ai" :
2034+ if not ORACLE_DB_USER or not ORACLE_DB_PASSWORD or not ORACLE_DB_DSN :
2035+ raise ValueError (
2036+ "Oracle23ai requires setting ORACLE_DB_USER, ORACLE_DB_PASSWORD, and ORACLE_DB_DSN."
2037+ )
2038+ if ORACLE_DB_USE_WALLET and (not ORACLE_WALLET_DIR or not ORACLE_WALLET_PASSWORD ):
2039+ raise ValueError (
2040+ "Oracle23ai requires setting ORACLE_WALLET_DIR and ORACLE_WALLET_PASSWORD when using wallet authentication."
2041+ )
2042+
2043+ log .info (f"VECTOR_DB: { VECTOR_DB } " )
2044+
2045+ # S3 Vector
2046+ S3_VECTOR_BUCKET_NAME = os .environ .get ("S3_VECTOR_BUCKET_NAME" , None )
2047+ S3_VECTOR_REGION = os .environ .get ("S3_VECTOR_REGION" , None )
2048+
19542049####################################
19552050# Information Retrieval (RAG)
19562051####################################
@@ -2012,10 +2107,16 @@ class BannerModel(BaseModel):
20122107 os .environ .get ("DATALAB_MARKER_API_KEY" , "" ),
20132108)
20142109
2015- DATALAB_MARKER_LANGS = PersistentConfig (
2016- "DATALAB_MARKER_LANGS" ,
2017- "rag.datalab_marker_langs" ,
2018- os .environ .get ("DATALAB_MARKER_LANGS" , "" ),
2110+ DATALAB_MARKER_API_BASE_URL = PersistentConfig (
2111+ "DATALAB_MARKER_API_BASE_URL" ,
2112+ "rag.datalab_marker_api_base_url" ,
2113+ os .environ .get ("DATALAB_MARKER_API_BASE_URL" , "" ),
2114+ )
2115+
2116+ DATALAB_MARKER_ADDITIONAL_CONFIG = PersistentConfig (
2117+ "DATALAB_MARKER_ADDITIONAL_CONFIG" ,
2118+ "rag.datalab_marker_additional_config" ,
2119+ os .environ .get ("DATALAB_MARKER_ADDITIONAL_CONFIG" , "" ),
20192120)
20202121
20212122DATALAB_MARKER_USE_LLM = PersistentConfig (
@@ -2055,6 +2156,12 @@ class BannerModel(BaseModel):
20552156 == "true" ,
20562157)
20572158
2159+ DATALAB_MARKER_FORMAT_LINES = PersistentConfig (
2160+ "DATALAB_MARKER_FORMAT_LINES" ,
2161+ "rag.datalab_marker_format_lines" ,
2162+ os .environ .get ("DATALAB_MARKER_FORMAT_LINES" , "false" ).lower () == "true" ,
2163+ )
2164+
20582165DATALAB_MARKER_OUTPUT_FORMAT = PersistentConfig (
20592166 "DATALAB_MARKER_OUTPUT_FORMAT" ,
20602167 "rag.datalab_marker_output_format" ,
0 commit comments