# -*- coding: utf-8 -*- """OPC 智能园区后端 —— 全局配置(backend/.env 可覆盖,未配置时用默认值)""" import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # backend/ def _load_dotenv(path): """极简 .env 解析:KEY=VALUE,支持 # 注释与引号""" if not Path(path).exists(): return for raw in Path(path).read_text("utf-8").splitlines(): line = raw.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") key = key.strip() value = value.strip().strip('"').strip("'") if key and key not in os.environ: os.environ[key] = value _load_dotenv(BASE_DIR / ".env") def _env(key, default): return os.getenv(key, default) class Settings: # ---- HTTP ---- HOST = _env("DPM_HOST", _env("FASTAPI_HOST", "0.0.0.0")) PORT = int(_env("DPM_PORT", _env("FASTAPI_PORT", "8000"))) # ---- 数据目录 ---- MEDIA_DIR = Path(_env("DPM_MEDIA_DIR", str(BASE_DIR / "media"))) DATA_FILE = Path(_env("DPM_DATA_FILE", str(BASE_DIR / "data.json"))) DIST_DIR = Path(_env("DPM_DIST_DIR", str(BASE_DIR.parent / "dist"))) # 前端构建产物(可选托管) # ---- MQTT(优先使用 .env 中的局域网 Broker 配置) ---- MQTT_ENABLED = _env("DPM_MQTT_ENABLED", "1") == "1" MQTT_HOST = _env("DPM_MQTT_HOST", _env("MQTT_BROKER_HOST", "127.0.0.1")) MQTT_PORT = int(_env("DPM_MQTT_PORT", _env("MQTT_BROKER_PORT", "1883"))) MQTT_USERNAME = _env("DPM_MQTT_USERNAME", _env("MQTT_USERNAME", "")) or None MQTT_PASSWORD = _env("DPM_MQTT_PASSWORD", _env("MQTT_PASSWORD", "")) or None MQTT_CLIENT_ID = _env("DPM_MQTT_CLIENT_ID", "dpm-backend") # 前端 WebSocket 接入地址(默认按 .env 的 broker 主机 + EMQX 常规 ws 端口) MQTT_WS_URL = _env( "DPM_MQTT_WS", f"ws://{_env('MQTT_PUBLIC_HOST', _env('MQTT_BROKER_HOST', 'localhost'))}:8083/mqtt", ) MQTT_TICK_INTERVAL = float(_env("DPM_MQTT_TICK_INTERVAL", "2.2")) # ---- 主题 ---- TOPIC_COMMAND = _env("DPM_TOPIC_COMMAND", "opc/display/command") TOPIC_TICK = _env("DPM_TOPIC_TICK", "opc/dashboard/tick") TOPIC_ACK = _env("DPM_TOPIC_ACK", "opc/display/ack") TOPIC_HEARTBEAT = _env("DPM_TOPIC_HEARTBEAT", "opc/display/heartbeat") SCREEN_TTL = float(_env("DPM_SCREEN_TTL", "30")) # 大屏心跳过期秒数 # ---- 阿里云 DashScope(LLM + 语音识别) ---- DASHSCOPE_API_KEY = _env("DASHSCOPE_API_KEY", "") LLM_MODEL = _env("DPM_LLM_MODEL", "qwen-plus") # 通义千问 ASR_MODEL = _env("DPM_ASR_MODEL", "paraformer-realtime-v2") # 流式语音识别 LLM_BASE_URL = _env("DPM_LLM_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1") ASR_TIMEOUT = float(_env("DPM_ASR_TIMEOUT", "30")) # ---- 管理后台 ---- ADMIN_SECRET = _env("DPM_ADMIN_SECRET", "dpm-admin-secret-change-me") # ---- s2s 实时语音栈(VAD本地 + 云ASR/LLM/TTS) ---- S2S_ENABLED = _env("DPM_S2S_ENABLED", "0") == "1" S2S_HOST = _env("DPM_S2S_HOST", "0.0.0.0") S2S_PORT = int(_env("DPM_S2S_PORT", "8765")) S2S_NUM_PIPELINES = int(_env("DPM_S2S_NUM_PIPELINES", "1")) S2S_STT_MODEL = _env("DPM_S2S_STT_MODEL", "qwen3-asr-flash-realtime") S2S_LLM_MODEL = _env("DPM_S2S_LLM_MODEL", "qwen-plus") S2S_TTS_MODEL = _env("DPM_S2S_TTS_MODEL", "qwen3-tts-flash-realtime") S2S_TTS_VOICE = _env("DPM_S2S_TTS_VOICE", "Cherry") S2S_BUILD_RETRIES = int(_env("DPM_S2S_BUILD_RETRIES", "4")) S2S_BUILD_RETRY_DELAY = float(_env("DPM_S2S_BUILD_RETRY_DELAY", "10")) S2S_LOG_LEVEL = _env("DPM_S2S_LOG_LEVEL", "INFO").upper() # 对外可访问的 s2s 实时语音 WS 地址(供 /api/config 下发;DPM_VOICE_WS 可显式覆盖) S2S_WS_URL = _env( "DPM_VOICE_WS", f"ws://{_env('MQTT_PUBLIC_HOST', _env('MQTT_BROKER_HOST', 'localhost'))}:8765/v1/realtime", ) # 语音助手基础提示词(.env 设 DPM_S2S_INSTRUCTIONS 可覆盖;知识库内容由 rag 自动拼接) S2S_INSTRUCTIONS = _env( "DPM_S2S_INSTRUCTIONS", "你是 PineSound 园区智能语音助手。可用工具:get_park_overview(园区实时数据)、" "query_companies(企业名录)、control_display(大屏控制)、get_time(时间)。" "涉及园区数据/企业/大屏控制时务必调用工具获取准确信息。请用简洁专业的中文回答,不超过三句话。", ) settings = Settings()