config.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. """
  2. 配置管理模块
  3. """
  4. import os
  5. from pathlib import Path
  6. from pydantic_settings import BaseSettings
  7. from dotenv import load_dotenv
  8. # 加载环境变量
  9. load_dotenv()
  10. class Settings(BaseSettings):
  11. """应用配置"""
  12. # 应用基础配置
  13. APP_NAME: str = "Lyrria Voice Assistant"
  14. APP_VERSION: str = "1.0.0"
  15. DEBUG: bool = False
  16. # 服务器配置
  17. HOST: str = os.getenv("HOST", "0.0.0.0")
  18. PORT: int = int(os.getenv("PORT", "8000"))
  19. host: str = os.getenv("HOST", "0.0.0.0") # 别名,兼容小写
  20. port: int = int(os.getenv("PORT", "8000")) # 别名,兼容小写
  21. # WebSocket配置
  22. WS_HEARTBEAT_INTERVAL: int = 30 # 心跳间隔(秒)
  23. WS_TIMEOUT: int = 60 # 超时时间(秒)
  24. # 能量石配置
  25. ENERGY_STONE_TTL_SECONDS: int = int(os.getenv("ENERGY_STONE_TTL_SECONDS", "20"))
  26. CONVERSATION_STONE_INTERVAL_SECONDS: int = int(os.getenv("CONVERSATION_STONE_INTERVAL_SECONDS", "30"))
  27. MISSION_TYPE_CACHE_SECONDS: int = int(os.getenv("MISSION_TYPE_CACHE_SECONDS", "60"))
  28. # DashScope API配置(阿里云)
  29. dashscope_api_key: str = os.getenv("DASHSCOPE_API_KEY", "")
  30. dashscope_api_key_intl: str = os.getenv("DASHSCOPE_API_KEY_INTL", "") # 国际版单独的key(可选)
  31. use_dashscope_intl: bool = os.getenv("USE_DASHSCOPE_INTL", "false").lower() == "true" # 是否使用国际版
  32. @property
  33. def active_dashscope_api_key(self) -> str:
  34. """获取当前激活的API Key(根据国际版/国内版自动选择)"""
  35. if self.use_dashscope_intl and self.dashscope_api_key_intl:
  36. return self.dashscope_api_key_intl
  37. return self.dashscope_api_key
  38. # LLM配置
  39. llm_model: str = os.getenv("LLM_MODEL", "qwen-flash")
  40. LLM_BASE_URL: str = os.getenv("LLM_BASE_URL", "")
  41. # STT配置
  42. # ⚠️ 重要:language参数必须设置!
  43. # 支持的值:
  44. # - 'zh' (中文)
  45. # - 'en' (英文)
  46. # - 'ja' (日文)
  47. # - 'ko' (韩文)
  48. # - 'auto' (自动检测 - 实验性,需要测试)
  49. # 项目正式使用时应设为'en',测试阶段用'zh'或'auto'
  50. STT_MODEL: str = os.getenv("STT_MODEL", "qwen3-asr-flash-realtime")
  51. STT_WS_URL: str = os.getenv("STT_WS_URL", "")
  52. STT_LANGUAGE: str = os.getenv("STT_LANGUAGE", "zh")
  53. # TTS配置
  54. tts_model: str = os.getenv("TTS_MODEL", "qwen3-tts-flash")
  55. TTS_API_URL: str = os.getenv("TTS_API_URL", "")
  56. tts_voice: str = os.getenv("TTS_VOICE", "Cherry") # 女声(支持:Cherry, Stella)
  57. tts_speech_rate: float = float(os.getenv("TTS_SPEECH_RATE", "1.1")) # 语速:1.1 = 10%加速(优化延迟)
  58. # TTS重试配置(处理API速率限制)
  59. TTS_MAX_RETRIES: int = int(os.getenv("TTS_MAX_RETRIES", "3")) # 最大重试次数
  60. TTS_RETRY_DELAY: float = float(os.getenv("TTS_RETRY_DELAY", "5.0")) # 初始重试延迟(秒)- 增加到5秒
  61. TTS_RETRY_BACKOFF: float = float(os.getenv("TTS_RETRY_BACKOFF", "2.0")) # 指数退避系数
  62. # TTS速率控制(预防性措施,避免触发API限制)
  63. TTS_MIN_INTERVAL: float = float(os.getenv("TTS_MIN_INTERVAL", "0.8")) # 两次TTS调用的最小间隔(秒)
  64. # AI助手提示词配置
  65. LEARNER_PROFICIENCY: str = os.getenv("LEARNER_PROFICIENCY", "A1-A2")
  66. AGE_TAG: str = os.getenv("AGE_TAG", "Adult")
  67. TOPIC_TAG: str = os.getenv("TOPIC_TAG", "Daily Conversation")
  68. EXPERIMENT_TAG: str = os.getenv("EXPERIMENT_TAG", "control") # control 或 experiment
  69. USE_SIMPLIFIED_PROMPT: bool = os.getenv("USE_SIMPLIFIED_PROMPT", "false").lower() == "true" # 🔥 使用简化提示词(减少延迟)
  70. # 音频配置
  71. AUDIO_SAMPLE_RATE: int = 16000
  72. AUDIO_BIT_RATE: int = 16
  73. AUDIO_CHANNELS: int = 1
  74. # MySQL配置
  75. MYSQL_HOST: str = os.getenv("MYSQL_HOST", "47.99.164.66")
  76. MYSQL_PORT: int = int(os.getenv("MYSQL_PORT", "3306"))
  77. MYSQL_USER: str = os.getenv("MYSQL_USER", "root")
  78. MYSQL_PASSWORD: str = os.getenv("MYSQL_PASSWORD", "")
  79. MYSQL_DATABASE: str = os.getenv("MYSQL_DATABASE", "Lyrria")
  80. MYSQL_POOL_SIZE: int = int(os.getenv("MYSQL_POOL_SIZE", "10"))
  81. MYSQL_MAX_OVERFLOW: int = int(os.getenv("MYSQL_MAX_OVERFLOW", "20"))
  82. MYSQL_CONNECT_TIMEOUT: int = int(os.getenv("MYSQL_CONNECT_TIMEOUT", "10"))
  83. MYSQL_READ_TIMEOUT: int = int(os.getenv("MYSQL_READ_TIMEOUT", "10"))
  84. MYSQL_POOL_RECYCLE: int = int(os.getenv("MYSQL_POOL_RECYCLE", "7200")) # 🔥 增加到2小时,保持长连接
  85. MYSQL_HEALTHCHECK_INTERVAL: int = int(os.getenv("MYSQL_HEALTHCHECK_INTERVAL", "600")) # 🔥 增加到10分钟,减少检查频率
  86. # Redis配置
  87. REDIS_HOST: str = os.getenv("REDIS_HOST", "47.99.164.66")
  88. REDIS_PORT: int = int(os.getenv("REDIS_PORT", "16379"))
  89. REDIS_DB: int = int(os.getenv("REDIS_DB", "12"))
  90. REDIS_PASSWORD: str = os.getenv("REDIS_PASSWORD", "")
  91. REDIS_SSL: bool = os.getenv("REDIS_SSL", "false").lower() == "true"
  92. REDIS_SESSION_TTL: int = int(os.getenv("REDIS_SESSION_TTL", "86400"))
  93. REDIS_MAX_CONNECTIONS: int = int(os.getenv("REDIS_MAX_CONNECTIONS", "50"))
  94. REDIS_HEALTHCHECK_INTERVAL: int = int(os.getenv("REDIS_HEALTHCHECK_INTERVAL", "300")) # 🔥 Redis健康检查间隔(秒),5分钟检查一次
  95. # HTTP客户端配置(用于DashScope API)
  96. HTTPX_MAX_KEEPALIVE_CONNECTIONS: int = int(os.getenv("HTTPX_MAX_KEEPALIVE_CONNECTIONS", "20")) # 🔥 keepalive连接数
  97. HTTPX_MAX_CONNECTIONS: int = int(os.getenv("HTTPX_MAX_CONNECTIONS", "50")) # 🔥 最大连接数
  98. HTTPX_KEEPALIVE_EXPIRY: float = float(os.getenv("HTTPX_KEEPALIVE_EXPIRY", "600.0")) # 🔥 keepalive过期时间(秒),10分钟,减少长时间未使用后的连接重建
  99. # LLM连接健康检查配置
  100. LLM_HEALTHCHECK_INTERVAL: int = int(os.getenv("LLM_HEALTHCHECK_INTERVAL", "300")) # 🔥 LLM连接健康检查间隔(秒),5分钟检查一次
  101. # TTS连接健康检查配置
  102. TTS_HEALTHCHECK_INTERVAL: int = int(os.getenv("TTS_HEALTHCHECK_INTERVAL", "300")) # 🔥 TTS连接健康检查间隔(秒),5分钟检查一次
  103. # CORS配置
  104. # 支持localhost和任意IP地址访问(端口3000为Vite默认HTTPS端口)
  105. CORS_ORIGINS: list = [
  106. "http://localhost:3000",
  107. "https://localhost:3000",
  108. "http://127.0.0.1:3000",
  109. "https://127.0.0.1:3000",
  110. "http://localhost:5173",
  111. "https://localhost:5173",
  112. "http://127.0.0.1:5173",
  113. "https://127.0.0.1:5173",
  114. # 支持局域网IP访问
  115. "http://192.168.2.112:3000",
  116. "https://192.168.2.112:3000",
  117. "http://192.168.2.112:5173",
  118. "https://192.168.2.112:5173",
  119. "http://192.168.43.35:3000",
  120. "https://192.168.43.35:3000",
  121. "http://192.168.43.35:5173",
  122. "https://192.168.43.35:5173",
  123. ]
  124. # 是否允许所有来源(开发环境可设为True,生产环境务必设为False)
  125. CORS_ALLOW_ALL: bool = os.getenv("CORS_ALLOW_ALL", "true").lower() == "true"
  126. # 日志配置
  127. LOG_LEVEL: str = "INFO"
  128. LOG_DIR: str = "logs"
  129. LOG_ROTATION: str = "500 MB"
  130. LOG_RETENTION: str = "10 days"
  131. # SSL配置(可选,生产环境建议由nginx统一管理SSL)
  132. SSL_KEYFILE: str = os.getenv("SSL_KEYFILE", "")
  133. SSL_CERTFILE: str = os.getenv("SSL_CERTFILE", "")
  134. class Config:
  135. env_file = ".env"
  136. case_sensitive = True
  137. extra = "ignore" # 忽略额外的环境变量(如旧的连接池配置)
  138. # 创建全局配置实例
  139. settings = Settings()
  140. # 确保日志目录存在
  141. Path(settings.LOG_DIR).mkdir(parents=True, exist_ok=True)
  142. # DashScope API endpoint 配置
  143. def get_dashscope_domain() -> str:
  144. """获取 DashScope API 域名"""
  145. return "dashscope-intl.aliyuncs.com" if settings.use_dashscope_intl else "dashscope.aliyuncs.com"
  146. def get_dashscope_ws_url() -> str:
  147. """获取 DashScope WebSocket URL(用于STT)"""
  148. if settings.STT_WS_URL:
  149. return settings.STT_WS_URL
  150. domain = get_dashscope_domain()
  151. return f"wss://{domain}/api-ws/v1/realtime"
  152. def get_dashscope_http_base_url() -> str:
  153. """获取 DashScope HTTP base URL(用于LLM)"""
  154. if settings.LLM_BASE_URL:
  155. return settings.LLM_BASE_URL.rstrip("/")
  156. domain = get_dashscope_domain()
  157. return f"https://{domain}/compatible-mode/v1"
  158. def get_dashscope_tts_api_url() -> str:
  159. """获取 DashScope HTTP API URL(用于TTS)"""
  160. if settings.TTS_API_URL:
  161. return settings.TTS_API_URL.rstrip("/")
  162. domain = get_dashscope_domain()
  163. return f"https://{domain}/api/v1"