env.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import importlib.metadata
  2. import json
  3. import logging
  4. import os
  5. import pkgutil
  6. import sys
  7. import shutil
  8. from pathlib import Path
  9. import markdown
  10. from bs4 import BeautifulSoup
  11. from open_webui.constants import ERROR_MESSAGES
  12. ####################################
  13. # Load .env file
  14. ####################################
  15. OPEN_WEBUI_DIR = Path(__file__).parent # the path containing this file
  16. print(OPEN_WEBUI_DIR)
  17. BACKEND_DIR = OPEN_WEBUI_DIR.parent # the path containing this file
  18. BASE_DIR = BACKEND_DIR.parent # the path containing the backend/
  19. print(BACKEND_DIR)
  20. print(BASE_DIR)
  21. try:
  22. from dotenv import find_dotenv, load_dotenv
  23. load_dotenv(find_dotenv(str(BASE_DIR / ".env")))
  24. except ImportError:
  25. print("dotenv not installed, skipping...")
  26. # device type embedding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
  27. USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
  28. if USE_CUDA.lower() == "true":
  29. try:
  30. import torch
  31. assert torch.cuda.is_available(), "CUDA not available"
  32. DEVICE_TYPE = "cuda"
  33. except Exception as e:
  34. cuda_error = (
  35. "Error when testing CUDA but USE_CUDA_DOCKER is true. "
  36. f"Resetting USE_CUDA_DOCKER to false: {e}"
  37. )
  38. os.environ["USE_CUDA_DOCKER"] = "false"
  39. USE_CUDA = "false"
  40. DEVICE_TYPE = "cpu"
  41. else:
  42. DEVICE_TYPE = "cpu"
  43. ####################################
  44. # LOGGING
  45. ####################################
  46. log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
  47. GLOBAL_LOG_LEVEL = os.environ.get("GLOBAL_LOG_LEVEL", "").upper()
  48. if GLOBAL_LOG_LEVEL in log_levels:
  49. logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL, force=True)
  50. else:
  51. GLOBAL_LOG_LEVEL = "INFO"
  52. log = logging.getLogger(__name__)
  53. log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
  54. if "cuda_error" in locals():
  55. log.exception(cuda_error)
  56. log_sources = [
  57. "AUDIO",
  58. "COMFYUI",
  59. "CONFIG",
  60. "DB",
  61. "IMAGES",
  62. "MAIN",
  63. "MODELS",
  64. "OLLAMA",
  65. "OPENAI",
  66. "RAG",
  67. "WEBHOOK",
  68. ]
  69. SRC_LOG_LEVELS = {}
  70. for source in log_sources:
  71. log_env_var = source + "_LOG_LEVEL"
  72. SRC_LOG_LEVELS[source] = os.environ.get(log_env_var, "").upper()
  73. if SRC_LOG_LEVELS[source] not in log_levels:
  74. SRC_LOG_LEVELS[source] = GLOBAL_LOG_LEVEL
  75. log.info(f"{log_env_var}: {SRC_LOG_LEVELS[source]}")
  76. log.setLevel(SRC_LOG_LEVELS["CONFIG"])
  77. WEBUI_NAME = os.environ.get("WEBUI_NAME", "Open WebUI")
  78. if WEBUI_NAME != "Open WebUI":
  79. WEBUI_NAME += " (Open WebUI)"
  80. WEBUI_URL = os.environ.get("WEBUI_URL", "http://localhost:3000")
  81. WEBUI_FAVICON_URL = "https://openwebui.com/favicon.png"
  82. ####################################
  83. # ENV (dev,test,prod)
  84. ####################################
  85. ENV = os.environ.get("ENV", "dev")
  86. FROM_INIT_PY = os.environ.get("FROM_INIT_PY", "False").lower() == "true"
  87. if FROM_INIT_PY:
  88. PACKAGE_DATA = {"version": importlib.metadata.version("open-webui")}
  89. else:
  90. try:
  91. PACKAGE_DATA = json.loads((BASE_DIR / "package.json").read_text())
  92. except Exception:
  93. PACKAGE_DATA = {"version": "0.0.0"}
  94. VERSION = PACKAGE_DATA["version"]
  95. # Function to parse each section
  96. def parse_section(section):
  97. items = []
  98. for li in section.find_all("li"):
  99. # Extract raw HTML string
  100. raw_html = str(li)
  101. # Extract text without HTML tags
  102. text = li.get_text(separator=" ", strip=True)
  103. # Split into title and content
  104. parts = text.split(": ", 1)
  105. title = parts[0].strip() if len(parts) > 1 else ""
  106. content = parts[1].strip() if len(parts) > 1 else text
  107. items.append({"title": title, "content": content, "raw": raw_html})
  108. return items
  109. try:
  110. changelog_path = BASE_DIR / "CHANGELOG.md"
  111. with open(str(changelog_path.absolute()), "r", encoding="utf8") as file:
  112. changelog_content = file.read()
  113. except Exception:
  114. changelog_content = (pkgutil.get_data("open_webui", "CHANGELOG.md") or b"").decode()
  115. # Convert markdown content to HTML
  116. html_content = markdown.markdown(changelog_content)
  117. # Parse the HTML content
  118. soup = BeautifulSoup(html_content, "html.parser")
  119. # Initialize JSON structure
  120. changelog_json = {}
  121. # Iterate over each version
  122. for version in soup.find_all("h2"):
  123. version_number = version.get_text().strip().split(" - ")[0][1:-1] # Remove brackets
  124. date = version.get_text().strip().split(" - ")[1]
  125. version_data = {"date": date}
  126. # Find the next sibling that is a h3 tag (section title)
  127. current = version.find_next_sibling()
  128. while current and current.name != "h2":
  129. if current.name == "h3":
  130. section_title = current.get_text().lower() # e.g., "added", "fixed"
  131. section_items = parse_section(current.find_next_sibling("ul"))
  132. version_data[section_title] = section_items
  133. # Move to the next element
  134. current = current.find_next_sibling()
  135. changelog_json[version_number] = version_data
  136. CHANGELOG = changelog_json
  137. ####################################
  138. # SAFE_MODE
  139. ####################################
  140. SAFE_MODE = os.environ.get("SAFE_MODE", "false").lower() == "true"
  141. ####################################
  142. # WEBUI_BUILD_HASH
  143. ####################################
  144. WEBUI_BUILD_HASH = os.environ.get("WEBUI_BUILD_HASH", "dev-build")
  145. ####################################
  146. # DATA/FRONTEND BUILD DIR
  147. ####################################
  148. DATA_DIR = Path(os.getenv("DATA_DIR", BACKEND_DIR / "data")).resolve()
  149. if FROM_INIT_PY:
  150. NEW_DATA_DIR = Path(os.getenv("DATA_DIR", OPEN_WEBUI_DIR / "data")).resolve()
  151. NEW_DATA_DIR.mkdir(parents=True, exist_ok=True)
  152. # Check if the data directory exists in the package directory
  153. if DATA_DIR.exists() and DATA_DIR != NEW_DATA_DIR:
  154. log.info(f"Moving {DATA_DIR} to {NEW_DATA_DIR}")
  155. for item in DATA_DIR.iterdir():
  156. dest = NEW_DATA_DIR / item.name
  157. if item.is_dir():
  158. shutil.copytree(item, dest, dirs_exist_ok=True)
  159. else:
  160. shutil.copy2(item, dest)
  161. DATA_DIR = Path(os.getenv("DATA_DIR", OPEN_WEBUI_DIR / "data"))
  162. FONTS_DIR = Path(os.getenv("FONTS_DIR", OPEN_WEBUI_DIR / "static" / "fonts"))
  163. FRONTEND_BUILD_DIR = Path(os.getenv("FRONTEND_BUILD_DIR", BASE_DIR / "build")).resolve()
  164. if FROM_INIT_PY:
  165. FRONTEND_BUILD_DIR = Path(
  166. os.getenv("FRONTEND_BUILD_DIR", OPEN_WEBUI_DIR / "frontend")
  167. ).resolve()
  168. RESET_CONFIG_ON_START = (
  169. os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
  170. )
  171. if RESET_CONFIG_ON_START:
  172. try:
  173. os.remove(f"{DATA_DIR}/config.json")
  174. with open(f"{DATA_DIR}/config.json", "w") as f:
  175. f.write("{}")
  176. except Exception:
  177. pass
  178. ####################################
  179. # Database
  180. ####################################
  181. # Check if the file exists
  182. if os.path.exists(f"{DATA_DIR}/ollama.db"):
  183. # Rename the file
  184. os.rename(f"{DATA_DIR}/ollama.db", f"{DATA_DIR}/webui.db")
  185. log.info("Database migrated from Ollama-WebUI successfully.")
  186. else:
  187. pass
  188. DATABASE_URL = os.environ.get("DATABASE_URL", f"sqlite:///{DATA_DIR}/webui.db")
  189. # Replace the postgres:// with postgresql://
  190. if "postgres://" in DATABASE_URL:
  191. DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql://")
  192. ####################################
  193. # WEBUI_AUTH (Required for security)
  194. ####################################
  195. WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
  196. WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
  197. "WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
  198. )
  199. WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
  200. ####################################
  201. # WEBUI_SECRET_KEY
  202. ####################################
  203. WEBUI_SECRET_KEY = os.environ.get(
  204. "WEBUI_SECRET_KEY",
  205. os.environ.get(
  206. "WEBUI_JWT_SECRET_KEY", "t0p-s3cr3t"
  207. ), # DEPRECATED: remove at next major version
  208. )
  209. WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get(
  210. "WEBUI_SESSION_COOKIE_SAME_SITE",
  211. os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax"),
  212. )
  213. WEBUI_SESSION_COOKIE_SECURE = os.environ.get(
  214. "WEBUI_SESSION_COOKIE_SECURE",
  215. os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true",
  216. )
  217. if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
  218. raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)
  219. ENABLE_WEBSOCKET_SUPPORT = (
  220. os.environ.get("ENABLE_WEBSOCKET_SUPPORT", "True").lower() == "true"
  221. )