feat(server): 新增 LLM 集成配置项与 pydantic-settings 嵌套模型修复
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user