feat: 异步任务队列架构 - SQLite队列 + 速率限制 + NAS Poller
Edge端: - 新增 SQLite 异步任务队列 (queue_manager + consumer) - 新增 TokenBucket 速率限制器 (Gemini 1000 RPM, NVIDIA 40 RPM, burst 2x) - 新增 /api/edge/video/enqueue + /api/edge/results 端点 - 消费者线程从队列消费任务,按速率限制调用AI模型 - orchestrator 集成 rate_limiter,Gemini优先→NVIDIA兜底 NAS端: - Dispatcher 重构为 enqueue 模式(上传后立即返回,不等结果) - 新增 Poller 线程(定期从Edge拉取结果写 MariaDB) - app.py 启动 Poller,config.yaml 新增 poller 配置 - db_layer 更新 valid_stages 添加 'process'
This commit is contained in:
@@ -52,12 +52,15 @@ class AIOrchestrator:
|
||||
def run_visual_analysis(self, adapters: List[BaseModelAdapter],
|
||||
frame_paths: List[str],
|
||||
frame_timestamps: List[str],
|
||||
known_members_context: str) -> Dict[str, dict]:
|
||||
known_members_context: str,
|
||||
rate_limiter=None) -> Dict[str, dict]:
|
||||
"""视觉分析阶段:仅 role=vision 的适配器参与
|
||||
|
||||
orchestrator.mode:
|
||||
- fallback (默认): 按 config 顺序依次尝试,首个成功即采用(单元素 dict)
|
||||
- ensemble: 并行所有健康 vision 模型,全部成功结果都保留(交叉验证)
|
||||
|
||||
rate_limiter: 可选 RateLimiter 实例,按 provider 限速(2x burst)
|
||||
"""
|
||||
vision_adapters = [a for a in adapters if getattr(a, 'role', 'vision') == 'vision']
|
||||
if not vision_adapters:
|
||||
@@ -68,7 +71,8 @@ class AIOrchestrator:
|
||||
|
||||
if mode == 'ensemble':
|
||||
return self._run_visual_ensemble(
|
||||
vision_adapters, frame_paths, frame_timestamps, known_members_context)
|
||||
vision_adapters, frame_paths, frame_timestamps,
|
||||
known_members_context, rate_limiter)
|
||||
|
||||
# fallback: 顺序降级,首个成功即采用
|
||||
model_outputs = {}
|
||||
@@ -76,6 +80,12 @@ class AIOrchestrator:
|
||||
if adapter.get_circuit_breaker().is_open():
|
||||
logger.warning(f"[{adapter.provider_name}] 熔断器 OPEN,跳过")
|
||||
continue
|
||||
# 速率限制:按 provider 获取 token(2x burst)
|
||||
if rate_limiter:
|
||||
acquired = rate_limiter.acquire(adapter.provider_name, timeout=300)
|
||||
if not acquired:
|
||||
logger.warning(f"[{adapter.provider_name}] 速率限制超时,跳过")
|
||||
continue
|
||||
start = time.time()
|
||||
try:
|
||||
output = adapter.analyze_frames(
|
||||
@@ -97,7 +107,8 @@ class AIOrchestrator:
|
||||
return model_outputs
|
||||
|
||||
def _run_visual_ensemble(self, vision_adapters, frame_paths,
|
||||
frame_timestamps, known_members_context) -> Dict[str, dict]:
|
||||
frame_timestamps, known_members_context,
|
||||
rate_limiter=None) -> Dict[str, dict]:
|
||||
"""并行调用所有健康 vision 模型,保留全部成功结果(交叉验证)"""
|
||||
model_outputs = {}
|
||||
max_timeout = max((a.get_timeout() for a in vision_adapters), default=240)
|
||||
@@ -107,6 +118,12 @@ class AIOrchestrator:
|
||||
if adapter.get_circuit_breaker().is_open():
|
||||
logger.warning(f"[{adapter.provider_name}] 熔断器 OPEN,跳过")
|
||||
continue
|
||||
# 速率限制:按 provider 获取 token(2x burst)
|
||||
if rate_limiter:
|
||||
acquired = rate_limiter.acquire(adapter.provider_name, timeout=300)
|
||||
if not acquired:
|
||||
logger.warning(f"[{adapter.provider_name}] 速率限制超时,跳过")
|
||||
continue
|
||||
future = pool.submit(
|
||||
adapter.analyze_frames,
|
||||
frame_paths, frame_timestamps, known_members_context)
|
||||
@@ -377,9 +394,12 @@ class AIOrchestrator:
|
||||
return 200
|
||||
|
||||
def process_push_task(self, task_data: dict, video_path: str,
|
||||
preprocessor: 'VideoPreprocessor') -> dict:
|
||||
preprocessor: 'VideoPreprocessor',
|
||||
rate_limiter=None) -> dict:
|
||||
"""推送模式:同步处理上传的视频,结果直接返回(无 webhook 回调)
|
||||
|
||||
rate_limiter: 可选 RateLimiter 实例,按 provider 限速(2x burst)
|
||||
|
||||
返回 payload 结构与原 webhook 回调一致:
|
||||
- 成功: {task_id, status: "success", event_start_time, ..., frame_details, ...}
|
||||
- 失败: {task_id, status: "failed", failure_stage, error_message}
|
||||
@@ -429,7 +449,8 @@ class AIOrchestrator:
|
||||
|
||||
# 3. 并行视觉分析
|
||||
model_outputs = self.run_visual_analysis(
|
||||
healthy_adapters, compressed_frames, frame_timestamps, known_members
|
||||
healthy_adapters, compressed_frames, frame_timestamps,
|
||||
known_members, rate_limiter
|
||||
)
|
||||
if not model_outputs:
|
||||
raise Exception('All models failed in visual analysis')
|
||||
|
||||
Reference in New Issue
Block a user