Files
jc-video-recognize/apps/server/core/settings.py

176 lines
5.3 KiB
Python

"""统一应用配置 (MVP-1 / 新增)
基于 pydantic-settings 的多环境配置管理,集中收敛原先散落在
``main.py``、各 service 模块中的环境变量、路径、阈值等配置。
加载顺序 (优先级从高到低)::
1. 显式构造参数
2. 环境变量 (大小写不敏感)
3. ``.env`` 文件 (位于 apps/server/.env)
4. 默认值
使用方式::
from core.settings import get_settings
settings = get_settings()
print(settings.api.port)
print(settings.detection.default_confidence)
"""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
from typing import List, Optional
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
# 项目根 (apps/server)
SERVER_DIR: Path = Path(__file__).resolve().parent.parent
PROJECT_ROOT: Path = SERVER_DIR.parent.parent
# ---------------------------------------------------------------------------
# 子配置
# ---------------------------------------------------------------------------
class APISettings(BaseSettings):
"""API 与服务器相关配置。"""
model_config = SettingsConfigDict(env_prefix="API_", extra="ignore")
host: str = Field(default="0.0.0.0", description="监听地址")
port: int = Field(default=8000, description="监听端口")
reload: bool = Field(default=True, description="开发模式自动重载")
cors_origins: List[str] = Field(
default_factory=lambda: ["*"], description="允许的跨域来源"
)
class DetectionSettings(BaseSettings):
"""检测相关全局默认值。"""
model_config = SettingsConfigDict(env_prefix="DETECTION_", extra="ignore")
default_confidence: float = Field(default=0.5, ge=0.0, le=1.0)
default_iou: float = Field(default=0.45, ge=0.0, le=1.0)
# 决策引擎过滤的最低置信度
min_confidence: float = Field(default=0.3, ge=0.0, le=1.0)
class ActionDetectionSettings(BaseSettings):
"""ppTSM 行为识别 (Docker) 服务配置。"""
model_config = SettingsConfigDict(env_prefix="ACTION_DETECTION_", extra="ignore")
api_url: str = Field(default="http://localhost:8081")
timeout: int = Field(default=30, ge=1)
class EventEngineSettings(BaseSettings):
"""事件决策 + 聚合 + 规则引擎配置。"""
model_config = SettingsConfigDict(env_prefix="EVENT_", extra="ignore")
# 时间窗口去重 (秒),同一 (source_id, event_type, track_id) 在窗口内只产生一条
dedup_window_seconds: float = Field(default=30.0, ge=0.0)
# 规则 YAML 目录 (相对 server 根)
rules_dir: str = Field(default="config/rules")
# 事件聚合最大活跃事件数 (超过将按 LRU 淘汰)
max_active_events: int = Field(default=1000, ge=1)
class LoggingSettings(BaseSettings):
"""日志配置。"""
model_config = SettingsConfigDict(env_prefix="LOG_", extra="ignore")
level: str = Field(default="INFO")
json_format: bool = Field(default=False)
class PathSettings(BaseSettings):
"""关键路径 (静态资源 / 外部模型) 配置。"""
model_config = SettingsConfigDict(env_prefix="PATH_", extra="ignore")
server_dir: Path = SERVER_DIR
project_root: Path = PROJECT_ROOT
static_dir: Path = SERVER_DIR / "static"
results_dir: Path = SERVER_DIR / "static" / "results"
temp_dir: Path = SERVER_DIR / "static" / "temp"
uploads_dir: Path = SERVER_DIR / "static" / "uploads"
external_paddle: Path = (
PROJECT_ROOT / "external" / "video-recognition-system" / "PaddlePaddle"
)
def ensure(self) -> None:
"""确保所有需要的目录存在 (启动时调用)。"""
for p in (self.static_dir, self.results_dir, self.temp_dir, self.uploads_dir):
p.mkdir(parents=True, exist_ok=True)
# ---------------------------------------------------------------------------
# 总配置
# ---------------------------------------------------------------------------
class Settings(BaseSettings):
"""应用总配置。
子配置统一在此聚合,便于以 ``settings.api.port`` 这种命名空间方式访问。
"""
model_config = SettingsConfigDict(
env_file=str(SERVER_DIR / ".env"),
env_file_encoding="utf-8",
env_nested_delimiter="__",
case_sensitive=False,
extra="ignore",
)
env: str = Field(default="development", description="运行环境")
debug: bool = Field(default=True)
api: APISettings = Field(default_factory=APISettings)
detection: DetectionSettings = Field(default_factory=DetectionSettings)
action_detection: ActionDetectionSettings = Field(
default_factory=ActionDetectionSettings
)
event_engine: EventEngineSettings = Field(default_factory=EventEngineSettings)
logging: LoggingSettings = Field(default_factory=LoggingSettings)
paths: PathSettings = Field(default_factory=PathSettings)
@lru_cache(maxsize=1)
def get_settings() -> Settings:
"""获取全局 Settings 单例 (带缓存)。
测试场景下可调用 ``get_settings.cache_clear()`` 重新加载。
"""
settings = Settings()
settings.paths.ensure()
return settings
__all__ = [
"Settings",
"APISettings",
"DetectionSettings",
"ActionDetectionSettings",
"EventEngineSettings",
"LoggingSettings",
"PathSettings",
"get_settings",
"SERVER_DIR",
"PROJECT_ROOT",
]