2026-08-17 21:25:46 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
2026-08-18 01:37:57 +08:00
|
|
|
|
"""开发/部署入口:uv run main.py(或 python main.py)
|
2026-08-17 21:25:46 +08:00
|
|
|
|
|
2026-08-18 01:37:57 +08:00
|
|
|
|
启动前自动完成环境初始化(无需手动 export):
|
|
|
|
|
|
1. 读取 backend/.env(阿里云密钥、端口、MQTT 等,见 app/config.py)
|
|
|
|
|
|
2. 本地资源收进 backend/ 内,不依赖其他项目:
|
|
|
|
|
|
TORCH_HOME -> backend/.torch-cache (silero VAD)
|
|
|
|
|
|
NLTK_DATA -> backend/nltk_data (标点/分词数据)
|
|
|
|
|
|
3. 网络直连:SSL_CERT_FILE(certifi)、NO_PROXY(绕过系统代理直达阿里云)
|
|
|
|
|
|
4. 默认开启 s2s 实时语音栈(.env 设 DPM_S2S_ENABLED=0 可关闭)
|
2026-08-17 21:25:46 +08:00
|
|
|
|
|
2026-08-18 01:37:57 +08:00
|
|
|
|
对应 uvicorn 命令:uvicorn app.main:app --host 0.0.0.0 --port 10085
|
|
|
|
|
|
"""
|
|
|
|
|
|
import os
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
BACKEND_DIR = Path(__file__).resolve().parent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _load_dotenv(path: Path):
|
|
|
|
|
|
"""极简 .env 解析:KEY=VALUE,支持 # 注释与引号(与 app/config.py 一致)"""
|
|
|
|
|
|
if not path.exists():
|
|
|
|
|
|
return
|
|
|
|
|
|
for raw in 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prepare_env():
|
|
|
|
|
|
"""进程环境初始化 —— 必须在 import app.config / torch 之前调用。"""
|
|
|
|
|
|
# 1) 项目内 .env 优先(已有环境变量不覆盖)
|
|
|
|
|
|
_load_dotenv(BACKEND_DIR / ".env")
|
|
|
|
|
|
# 2) 本地模型/数据缓存全部收进 backend/(部署时随目录整体拷贝即可)
|
|
|
|
|
|
os.environ.setdefault("TORCH_HOME", str(BACKEND_DIR / ".torch-cache"))
|
|
|
|
|
|
os.environ.setdefault("NLTK_DATA", str(BACKEND_DIR / "nltk_data"))
|
|
|
|
|
|
# 3) 网络直连(绕过 macOS 系统 SOCKS 代理;TLS 用 certifi 证书)
|
|
|
|
|
|
os.environ.setdefault("NO_PROXY", "*")
|
|
|
|
|
|
os.environ.setdefault("no_proxy", "*")
|
|
|
|
|
|
try:
|
|
|
|
|
|
import certifi
|
|
|
|
|
|
os.environ.setdefault("SSL_CERT_FILE", certifi.where())
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
# 4) 默认开启 s2s 实时语音栈(.env 的 DPM_S2S_ENABLED=0 优先生效)
|
|
|
|
|
|
os.environ.setdefault("DPM_S2S_ENABLED", "1")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prepare_env()
|
|
|
|
|
|
|
|
|
|
|
|
import uvicorn # noqa: E402
|
|
|
|
|
|
|
|
|
|
|
|
from app.config import settings # noqa: E402
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
s2s = "on" if settings.S2S_ENABLED else "off"
|
|
|
|
|
|
print("=" * 64)
|
|
|
|
|
|
print(f" DPM 后端 : http://{settings.HOST}:{settings.PORT}")
|
|
|
|
|
|
print(f" s2s 语音栈 : {s2s}" + (f" (ws://{settings.S2S_HOST}:{settings.S2S_PORT}/v1/realtime)" if settings.S2S_ENABLED else ""))
|
|
|
|
|
|
print(f" 模型 : ASR={settings.S2S_STT_MODEL} LLM={settings.S2S_LLM_MODEL} TTS={settings.S2S_TTS_MODEL} ({settings.S2S_TTS_VOICE})")
|
|
|
|
|
|
print(f" 本地资源 : TORCH_HOME={os.environ.get('TORCH_HOME')}")
|
|
|
|
|
|
print(f" NLTK_DATA={os.environ.get('NLTK_DATA')}")
|
|
|
|
|
|
print("=" * 64)
|
2026-08-18 07:14:13 +08:00
|
|
|
|
uvicorn.run("app.main:app", host=settings.HOST, port=settings.PORT, reload=False)
|
2026-08-18 01:37:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|