# -*- coding: utf-8 -*- """园区子应用(app/park)—— 全局配置 迁自 park-desktop/backend/app/config.py,数据目录改落 server-core/serverdata/park, 环境变量沿用 server-core/.env(前缀 DPM_ 或 PARK_ 均兼容,MIG/PARK 二选一)。 """ import os from pathlib import Path SERVER_ROOT = Path(__file__).resolve().parent.parent.parent # server-core 根 PARK_DIR = Path(os.environ.get("PARK_DATA_DIR", str(SERVER_ROOT / "serverdata" / "park"))) 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(SERVER_ROOT / ".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"))) # ---- 数据目录(统一落 serverdata/park)---- MEDIA_DIR = Path(_env("DPM_MEDIA_DIR", str(PARK_DIR / "media"))) DATA_FILE = Path(_env("DPM_DATA_FILE", str(PARK_DIR / "data.json"))) DIST_DIR = Path(_env("DPM_DIST_DIR", str(PARK_DIR / "dist"))) # 大屏前端产物(可选托管) # ---- MQTT(EMQX broker;凭据走 server-core/.env,勿入库)---- MQTT_ENABLED = _env("DPM_MQTT_ENABLED", "1") == "1" MQTT_HOST = _env("DPM_MQTT_HOST", _env("MQTT_BROKER_HOST", "192.168.1.9")) 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") 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() VOICE_WS = _env("DPM_VOICE_WS", "") S2S_WS_URL = ( VOICE_WS or f"ws://{_env('MQTT_PUBLIC_HOST', _env('MQTT_BROKER_HOST', 'localhost'))}:{S2S_PORT}/v1/realtime" ) S2S_INSTRUCTIONS = _env( "DPM_S2S_INSTRUCTIONS", "你是昆小创,昆明市大学生创业园的专属智能语音助手。可用工具:get_park_overview(园区实时数据)、" "query_companies(企业名录)、control_display(大屏控制)、get_time(时间)。" "涉及园区数据/企业/大屏控制时务必调用工具获取准确信息。请用简洁专业的中文回答,不超过三句话。", ) settings = Settings()