| |
| """ |
| 应用程序配置模块。 |
| 从环境变量加载配置,并提供默认值。 |
| 使用 python-dotenv 库来支持从 .env 文件加载环境变量。 |
| """ |
| import os |
| from typing import Optional, Dict, List, Any |
| import logging |
| import json |
| from dotenv import load_dotenv |
|
|
| |
| |
| |
| load_dotenv() |
|
|
| logger = logging.getLogger('my_logger') |
|
|
| |
| |
| __version__ = "1.8.1" |
|
|
| |
| |
| |
| PASSWORD: Optional[str] = os.environ.get("PASSWORD") |
| |
| WEB_UI_PASSWORDS: List[str] = [p.strip() for p in PASSWORD.split(',') if p.strip()] if PASSWORD else [] |
|
|
| |
| |
| SECRET_KEY: Optional[str] = os.environ.get("SECRET_KEY") |
| if not SECRET_KEY: |
| logger.critical("严重错误:JWT SECRET_KEY 未在环境变量中设置!Web UI 登录和管理功能将无法正常工作。") |
| |
| |
|
|
| |
| |
| ADMIN_API_KEY: Optional[str] = os.environ.get("ADMIN_API_KEY") |
|
|
| |
| MAX_REQUESTS_PER_MINUTE: int = int(os.environ.get("MAX_REQUESTS_PER_MINUTE", "60")) |
| |
| MAX_REQUESTS_PER_DAY_PER_IP: int = int(os.environ.get("MAX_REQUESTS_PER_DAY_PER_IP", "600")) |
|
|
| |
| |
| DISABLE_SAFETY_FILTERING: bool = os.environ.get("DISABLE_SAFETY_FILTERING", "false").lower() == "true" |
|
|
| |
| PROTECT_STATUS_PAGE: bool = os.environ.get("PROTECT_STATUS_PAGE", "false").lower() == "true" |
|
|
| |
| |
| JWT_ALGORITHM: str = os.environ.get("JWT_ALGORITHM", "HS256") |
| |
| ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES", "30")) |
|
|
|
|
| |
| |
| _default_report_interval = 30 |
| try: |
| |
| USAGE_REPORT_INTERVAL_MINUTES: int = int(os.environ.get("USAGE_REPORT_INTERVAL_MINUTES", str(_default_report_interval))) |
| |
| if USAGE_REPORT_INTERVAL_MINUTES <= 0: |
| logger.warning(f"环境变量 USAGE_REPORT_INTERVAL_MINUTES ({os.environ.get('USAGE_REPORT_INTERVAL_MINUTES')}) 必须为正整数,将使用默认值 {_default_report_interval} 分钟。") |
| USAGE_REPORT_INTERVAL_MINUTES = _default_report_interval |
| except ValueError: |
| logger.warning(f"无法将环境变量 USAGE_REPORT_INTERVAL_MINUTES ('{os.environ.get('USAGE_REPORT_INTERVAL_MINUTES')}') 解析为整数,将使用默认值 {_default_report_interval} 分钟。") |
| USAGE_REPORT_INTERVAL_MINUTES = _default_report_interval |
|
|
| |
| |
| _log_level_map: Dict[str, int] = {"DEBUG": logging.DEBUG, "INFO": logging.INFO, "WARNING": logging.WARNING, "ERROR": logging.ERROR, "CRITICAL": logging.CRITICAL} |
| REPORT_LOG_LEVEL_STR: str = os.environ.get("REPORT_LOG_LEVEL", "INFO").upper() |
| REPORT_LOG_LEVEL_INT: int = _log_level_map.get(REPORT_LOG_LEVEL_STR, logging.INFO) |
| |
|
|
| |
| |
| CACHE_REFRESH_INTERVAL_SECONDS: int = int(os.environ.get("CACHE_REFRESH_INTERVAL_SECONDS", "600")) |
|
|
| |
| |
| safety_settings: List[Dict[str, str]] = [ |
| {"category": c, "threshold": "BLOCK_NONE"} |
| for c in [ |
| "HARM_CATEGORY_HARASSMENT", |
| "HARM_CATEGORY_HATE_SPEECH", |
| "HARM_CATEGORY_SEXUALLY_EXPLICIT", |
| "HARM_CATEGORY_DANGEROUS_CONTENT", |
| |
| ] |
| ] |
|
|
| |
| |
| safety_settings_g2: List[Dict[str, str]] = [ |
| {"category": c, "threshold": "BLOCK_NONE"} |
| for c in [ |
| "HARM_CATEGORY_HARASSMENT", |
| "HARM_CATEGORY_HATE_SPEECH", |
| "HARM_CATEGORY_SEXUALLY_EXPLICIT", |
| "HARM_CATEGORY_DANGEROUS_CONTENT", |
| |
| ] |
| ] |
|
|
| |
| |
| |
| CONTEXT_DB_PATH: Optional[str] = os.environ.get('CONTEXT_DB_PATH') |
|
|
| |
| DEFAULT_MAX_CONTEXT_TOKENS: int = int(os.environ.get('DEFAULT_MAX_CONTEXT_TOKENS', "30000")) |
| |
| CONTEXT_TOKEN_SAFETY_MARGIN: int = int(os.environ.get('CONTEXT_TOKEN_SAFETY_MARGIN', "200")) |
| |
| |
| DEFAULT_CONTEXT_TTL_DAYS: int = int(os.environ.get('DEFAULT_CONTEXT_TTL_DAYS', "7")) |
|
|
| |
| MEMORY_CONTEXT_CLEANUP_INTERVAL_SECONDS: int = int(os.environ.get('MEMORY_CONTEXT_CLEANUP_INTERVAL_SECONDS', "3600")) |
| |
| MAX_CONTEXT_RECORDS_MEMORY: int = int(os.environ.get('MAX_CONTEXT_RECORDS_MEMORY', "5000")) |
|
|
| |
| |
| |
| |
| _context_storage_mode_env = os.environ.get("CONTEXT_STORAGE_MODE", "memory").lower() |
| if _context_storage_mode_env not in ['database', 'memory']: |
| logger.warning(f"无效的环境变量 CONTEXT_STORAGE_MODE 值 '{os.environ.get('CONTEXT_STORAGE_MODE')}'。将使用默认值 'memory'。") |
| CONTEXT_STORAGE_MODE: str = "memory" |
| elif _context_storage_mode_env == "database" and not CONTEXT_DB_PATH: |
| logger.warning("CONTEXT_STORAGE_MODE 设置为 'database',但 CONTEXT_DB_PATH 未设置。将强制使用 'memory' 模式。") |
| CONTEXT_STORAGE_MODE: str = "memory" |
| else: |
| CONTEXT_STORAGE_MODE: str = _context_storage_mode_env |
| logger.info(f"上下文存储模式设置为: {CONTEXT_STORAGE_MODE}") |
|
|
| |
| |
| ENABLE_CONTEXT_COMPLETION: bool = os.environ.get("ENABLE_CONTEXT_COMPLETION", "true").lower() == "true" |
|
|
| |
| |
| STREAM_SAVE_REPLY: bool = os.environ.get("STREAM_SAVE_REPLY", "false").lower() == "true" |
| |
| ENABLE_STICKY_SESSION: bool = os.environ.get("ENABLE_STICKY_SESSION", "false").lower() == "true" |
|
|
| |
| |
| |
| |
| |
| _key_storage_mode_env = os.environ.get("KEY_STORAGE_MODE", "memory").lower() |
| |
| if _key_storage_mode_env not in ['database', 'memory']: |
| logger.warning(f"无效的环境变量 KEY_STORAGE_MODE 值 '{os.environ.get('KEY_STORAGE_MODE')}'。将使用默认值 'memory'。") |
| KEY_STORAGE_MODE: str = "memory" |
| else: |
| KEY_STORAGE_MODE: str = _key_storage_mode_env |
| logger.info(f"API Key 存储模式设置为: {KEY_STORAGE_MODE}") |
|
|
| |
| GEMINI_API_KEYS: Optional[str] = os.environ.get("GEMINI_API_KEYS") |
| |
| if KEY_STORAGE_MODE == 'memory' and not GEMINI_API_KEYS: |
| logger.warning("KEY_STORAGE_MODE 设置为 'memory',但未找到 GEMINI_API_KEYS 环境变量。内存模式下将没有可用的 API Key。") |
|
|
|
|
| |
| |
| |
| MODEL_LIMITS: Dict[str, Any] = {} |
|
|
| def load_model_limits(): |
| """ |
| 从位于 `app/data/model_limits.json` 的 JSON 文件加载模型限制数据, |
| 并填充到全局变量 `MODEL_LIMITS` 字典中。 |
| 在应用启动时由 `main.py` 中的 `lifespan` 函数调用。 |
| """ |
| global MODEL_LIMITS |
| |
| current_dir = os.path.dirname(os.path.abspath(__file__)) |
| |
| model_limits_path = os.path.join(current_dir, 'data', 'model_limits.json') |
| logger.info(f"尝试从 {model_limits_path} 加载模型限制...") |
| try: |
| |
| if os.path.exists(model_limits_path): |
| |
| with open(model_limits_path, 'r', encoding='utf-8') as f: |
| MODEL_LIMITS = json.load(f) |
| logger.info(f"成功加载模型限制。找到的模型: {list(MODEL_LIMITS.keys())}") |
| |
| for model, limits in MODEL_LIMITS.items(): |
| if not isinstance(limits, dict): |
| logger.warning(f"模型 '{model}' 的限制不是字典格式,已忽略。") |
| continue |
| |
| |
| |
| |
| |
| else: |
| logger.error(f"模型限制文件未找到: {model_limits_path}。将使用空限制。") |
| MODEL_LIMITS = {} |
| except json.JSONDecodeError as e: |
| logger.error(f"解析模型限制文件 {model_limits_path} 失败: {e}。将使用空限制。") |
| MODEL_LIMITS = {} |
| except Exception as e: |
| logger.error(f"加载模型限制时发生未知错误: {e}", exc_info=True) |
| MODEL_LIMITS = {} |
|
|
| |
|
|
| |
| |
| |
| REPORT_FILE_PATH: Optional[str] = None |
| |
| |
| ENABLE_NATIVE_CACHING: bool = os.environ.get("ENABLE_NATIVE_CACHING", "false").lower() == "true" |
|
|