diff --git a/apps/server/api/detection.py b/apps/server/api/detection.py index 77cba43..cb4fbb5 100644 --- a/apps/server/api/detection.py +++ b/apps/server/api/detection.py @@ -14,6 +14,36 @@ router = APIRouter() logger = logging.getLogger(__name__) +# --------------------------------------------------------------------------- +# DetectionService 单例 (MVP-3 / D33) +# +# 之前每次请求都 new 一个 DetectionService,会导致 rule_engine / aggregator 状态 +# 不共享,规则配置 API 修改后无法同步生效。改造为按 model_service 缓存的单例。 +# --------------------------------------------------------------------------- + + +_detection_service = None + + +def get_detection_service(model_service=None): + """获取共享的 DetectionService 单例。 + + 若尚未初始化且未传入 ``model_service``,将抛出 RuntimeError。 + """ + + global _detection_service + if _detection_service is None: + if model_service is None: + raise RuntimeError( + "DetectionService 尚未初始化,请先在 main.lifespan 中调用 " + "get_detection_service(model_service)" + ) + from services.detection_service import DetectionService + _detection_service = DetectionService(model_service) + logger.info("DetectionService 单例已初始化") + return _detection_service + + @router.post("/detect/image", response_model=ImageDetectionResult) async def detect_image( file: UploadFile = File(...), @@ -42,7 +72,7 @@ async def detect_image( from main import model_service from services.detection_service import DetectionService - detection_service = DetectionService(model_service) + detection_service = get_detection_service(model_service) # 解析算法配置 algo_config = None @@ -309,7 +339,10 @@ async def detect_video( 'frame_index': frame_index, 'timestamp': round(frame_index / fps, 2) if fps > 0 else 0, 'detections': result_data['detections'], - 'detection_count': len(result_data['detections']) + 'detection_count': len(result_data['detections']), + # MVP-3: 携带 LLM 管道结果供前端展示 + 'llm_results': result_data.get('llm_results', []), + 'alert_events': result_data.get('alert_events', []), }) # 提取关键帧截图(最多保留 20 张,防止响应过大)