feat(server): 新增 LLM 集成配置项与 pydantic-settings 嵌套模型修复

This commit is contained in:
2026-06-16 11:48:24 +08:00
parent 86ac65c6d8
commit 1409734c8a
+111 -9
View File
@@ -34,6 +34,9 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
SERVER_DIR: Path = Path(__file__).resolve().parent.parent
PROJECT_ROOT: Path = SERVER_DIR.parent.parent
# 共享 .env 文件路径 (所有嵌套子配置共用)
_ENV_FILE = str(SERVER_DIR / ".env")
# ---------------------------------------------------------------------------
# 子配置
@@ -43,7 +46,7 @@ PROJECT_ROOT: Path = SERVER_DIR.parent.parent
class APISettings(BaseSettings):
"""API 与服务器相关配置。"""
model_config = SettingsConfigDict(env_prefix="API_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="API_", env_file=_ENV_FILE, extra="ignore")
host: str = Field(default="0.0.0.0", description="监听地址")
port: int = Field(default=8000, description="监听端口")
@@ -56,7 +59,7 @@ class APISettings(BaseSettings):
class DetectionSettings(BaseSettings):
"""检测相关全局默认值。"""
model_config = SettingsConfigDict(env_prefix="DETECTION_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="DETECTION_", env_file=_ENV_FILE, 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)
@@ -67,7 +70,7 @@ class DetectionSettings(BaseSettings):
class ActionDetectionSettings(BaseSettings):
"""ppTSM 行为识别 (Docker) 服务配置。"""
model_config = SettingsConfigDict(env_prefix="ACTION_DETECTION_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="ACTION_DETECTION_", env_file=_ENV_FILE, extra="ignore")
api_url: str = Field(default="http://localhost:8081")
timeout: int = Field(default=30, ge=1)
@@ -76,7 +79,7 @@ class ActionDetectionSettings(BaseSettings):
class EventEngineSettings(BaseSettings):
"""事件决策 + 聚合 + 规则引擎配置。"""
model_config = SettingsConfigDict(env_prefix="EVENT_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="EVENT_", env_file=_ENV_FILE, extra="ignore")
# 时间窗口去重 (秒),同一 (source_id, event_type, track_id) 在窗口内只产生一条
dedup_window_seconds: float = Field(default=30.0, ge=0.0)
@@ -89,7 +92,7 @@ class EventEngineSettings(BaseSettings):
class RTSPSettings(BaseSettings):
"""RTSP 流接入相关配置 (MVP-2)。"""
model_config = SettingsConfigDict(env_prefix="RTSP_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="RTSP_", env_file=_ENV_FILE, extra="ignore")
max_streams: int = Field(default=16, ge=1, description="最大同时接入流数量")
buffer_capacity: int = Field(default=300, ge=1, description="每路流帧缓冲区容量")
@@ -105,7 +108,7 @@ class RTSPSettings(BaseSettings):
class MQTTSettings(BaseSettings):
"""MQTT 预警发布相关配置 (MVP-2 / D16-D18)。"""
model_config = SettingsConfigDict(env_prefix="MQTT_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="MQTT_", env_file=_ENV_FILE, extra="ignore")
enabled: bool = Field(default=False, description="是否启用 MQTT 发布")
broker_host: str = Field(default="localhost", description="MQTT broker 主机")
@@ -126,7 +129,7 @@ class MQTTSettings(BaseSettings):
class TrackingSettings(BaseSettings):
"""目标跟踪 (ByteTrack) 配置 (MVP-2 / D19)。"""
model_config = SettingsConfigDict(env_prefix="TRACKING_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="TRACKING_", env_file=_ENV_FILE, extra="ignore")
enabled: bool = Field(default=True, description="是否启用目标跟踪")
track_thresh: float = Field(default=0.5, ge=0.0, le=1.0, description="跟踪置信度阈值")
@@ -139,7 +142,7 @@ class TrackingSettings(BaseSettings):
class AggregatorSettings(BaseSettings):
"""事件聚合器扩展配置 (MVP-2 / D20)。"""
model_config = SettingsConfigDict(env_prefix="AGGREGATOR_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="AGGREGATOR_", env_file=_ENV_FILE, extra="ignore")
enable_spatial_merge: bool = Field(default=True, description="是否启用空间邻近合并")
spatial_iou_threshold: float = Field(
@@ -154,10 +157,101 @@ class AggregatorSettings(BaseSettings):
)
class LLMSettings(BaseSettings):
"""LLM 二次判断服务配置 (MVP-3 / D29)。
支持 OpenAI 兼容协议 (GPT-4V / Qwen-VL / GLM-4V 等)
通过 ``provider=mock`` 在无 API Key 环境下保持离线可用。
"""
model_config = SettingsConfigDict(env_prefix="LLM_", env_file=_ENV_FILE, extra="ignore")
enabled: bool = Field(default=False, description="是否启用 LLM 二次判断")
provider: str = Field(
default="mock",
description="LLM provider: openai / qwen / glm / mock",
)
api_base: Optional[str] = Field(default=None, description="API base URL (OpenAI 兼容)")
api_key: Optional[str] = Field(default=None, description="API Key")
model: str = Field(default="gpt-4o", description="LLM 模型名称")
timeout: float = Field(default=15.0, ge=1.0, description="单次调用超时(秒)")
max_retries: int = Field(default=2, ge=0, description="失败重试次数")
max_tokens: int = Field(default=512, ge=64, description="最大返回 token 数")
temperature: float = Field(default=0.0, ge=0.0, le=2.0, description="采样温度")
max_concurrency: int = Field(default=2, ge=1, description="最大并发调用数")
image_max_side: int = Field(
default=768, ge=128, description="图像编码前最大边长 (节省 token)"
)
class LLMTriggerSettings(BaseSettings):
"""LLM 触发器配置 (MVP-3 / D26-D28)。"""
model_config = SettingsConfigDict(env_prefix="LLM_TRIGGER_", env_file=_ENV_FILE, extra="ignore")
enabled: bool = Field(default=False, description="是否启用 LLM 触发")
window_seconds: float = Field(
default=3.0, ge=0.5, description="多帧累积时间窗口(秒)"
)
min_consecutive_hits: int = Field(
default=3, ge=1, description="触发所需的最小连续命中帧数"
)
min_avg_confidence: float = Field(
default=0.55, ge=0.0, le=1.0, description="累积平均置信度阈值"
)
cooldown_seconds: float = Field(
default=20.0, ge=0.0, description="同目标 LLM 冷却时间(秒)"
)
max_track_capacity: int = Field(
default=2000, ge=1, description="累积器最大跟踪条目"
)
severity_bypass: List[str] = Field(
default_factory=lambda: ["critical"],
description="无需累积、立即触发的严重性级别",
)
class FusionSettings(BaseSettings):
"""LLM/YOLO 结果融合配置 (MVP-3 / D30)。"""
model_config = SettingsConfigDict(env_prefix="FUSION_", env_file=_ENV_FILE, extra="ignore")
strategy: str = Field(
default="weighted",
description="融合策略: weighted / conservative / llm_priority",
)
yolo_weight: float = Field(default=0.4, ge=0.0, le=1.0)
llm_weight: float = Field(default=0.6, ge=0.0, le=1.0)
# conservative 策略下,LLM 判定为否时直接抑制预警
suppress_on_llm_negative: bool = Field(default=True)
# LLM 不可用时是否回退到 YOLO 结果
fallback_to_yolo: bool = Field(default=True)
class LLMCostSettings(BaseSettings):
"""LLM 成本追踪 + 降级策略配置 (MVP-3 / D34)。"""
model_config = SettingsConfigDict(env_prefix="LLM_COST_", env_file=_ENV_FILE, extra="ignore")
daily_budget_usd: float = Field(
default=0.0, ge=0.0, description="日预算 (USD)0=不限"
)
history_days: int = Field(default=7, ge=1, description="保留多少天的日级统计")
recent_window: int = Field(
default=20, ge=3, description="错误率统计窗口 (最近 N 次调用)"
)
error_rate_threshold: float = Field(
default=0.5, gt=0.0, le=1.0, description="触发熔断的错误率阈值"
)
cooldown_seconds: float = Field(
default=60.0, ge=0.0, description="熔断冷却时间 (秒)"
)
class LoggingSettings(BaseSettings):
"""日志配置。"""
model_config = SettingsConfigDict(env_prefix="LOG_", extra="ignore")
model_config = SettingsConfigDict(env_prefix="LOG_", env_file=_ENV_FILE, extra="ignore")
level: str = Field(default="INFO")
json_format: bool = Field(default=False)
@@ -217,6 +311,10 @@ class Settings(BaseSettings):
mqtt: MQTTSettings = Field(default_factory=MQTTSettings)
tracking: TrackingSettings = Field(default_factory=TrackingSettings)
aggregator: AggregatorSettings = Field(default_factory=AggregatorSettings)
llm: LLMSettings = Field(default_factory=LLMSettings)
llm_trigger: LLMTriggerSettings = Field(default_factory=LLMTriggerSettings)
fusion: FusionSettings = Field(default_factory=FusionSettings)
llm_cost: LLMCostSettings = Field(default_factory=LLMCostSettings)
logging: LoggingSettings = Field(default_factory=LoggingSettings)
paths: PathSettings = Field(default_factory=PathSettings)
@@ -243,6 +341,10 @@ __all__ = [
"MQTTSettings",
"TrackingSettings",
"AggregatorSettings",
"LLMSettings",
"LLMTriggerSettings",
"FusionSettings",
"LLMCostSettings",
"LoggingSettings",
"PathSettings",
"get_settings",