fix: NVIDIA single-frame JSON parsing + Gemini timeout/circuit breaker tuning

NVIDIA fix:
- Replace parse_vlm_json (requires full schema: global_summary/entities_json/
  frame_details/compute_provider) with lightweight _parse_single_frame_json
  that only extracts per-frame fields (person/action/clothing/etc)
- Root cause: NVIDIA adapter does per-frame analysis returning single-frame
  JSON, but parse_vlm_json rejected it for missing full-schema fields
- Verified: task 297 → 6/6 frames parsed successfully, first SUCCESS

Gemini + circuit breaker tuning:
- Gemini timeout: 30s → 90s (multi-image vision analysis needs more time)
- NVIDIA timeout: 20s → 30s (per-frame API call)
- Circuit breaker threshold: 3 → 5 (less aggressive tripping)
- Circuit breaker cooldown: 600s → 300s (faster recovery)
This commit is contained in:
ericwyuan
2026-08-20 11:02:26 +08:00
parent 2e43afb6b2
commit 853cb21542
2 changed files with 34 additions and 11 deletions

View File

@@ -54,11 +54,11 @@ models:
enabled: true
model_name: "gemini-flash-latest" # v1beta 下 gemini-1.5-flash 会 404
api_key: "${GEMINI_API_KEY}"
timeout: 30
timeout: 90
circuit_breaker:
enabled: true
threshold: 3
cooldown: 600
threshold: 5
cooldown: 300
- provider: "nvidia"
role: "vision"
@@ -66,11 +66,11 @@ models:
model_name: "meta/llama-3.2-11b-vision-instruct"
base_url: "https://integrate.api.nvidia.com/v1"
api_key: "${NVIDIA_API_KEY}"
timeout: 20
timeout: 30
circuit_breaker:
enabled: true
threshold: 3
cooldown: 600
threshold: 5
cooldown: 300
# 本地模型:纯文本 qwen2.5:7b仅参与智能问答作为 Gemini/NVIDIA 都失败时的兜底
- provider: "ollama"

View File

@@ -10,12 +10,13 @@ SDK: openai (NIM 兼容 OpenAI API 规范)
"""
import os
import base64
import json
import re
from typing import Dict, List, Optional
from .base_adapter import BaseModelAdapter
from .circuit_breaker import CircuitBreaker
from ..logger import setup_logger
from ..ai_orchestrator.json_parser import parse_vlm_json, VLMOutputInvalidError
logger = setup_logger('fam-edge.nvidia_adapter')
@@ -98,6 +99,30 @@ class NvidiaVisionAdapter(BaseModelAdapter):
# NVIDIA 单帧无法跨帧综合 global_summary交由 Edge format_cloud_result 格式化生成
return {"frame_details": frame_details}
def _parse_single_frame_json(self, content: str) -> Optional[dict]:
"""轻量解析单帧 JSON不要求全 schema仅提取字段"""
content = content.strip()
# 直接解析
try:
return json.loads(content)
except json.JSONDecodeError:
pass
# 提取 markdown fence
fence = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', content, re.DOTALL)
if fence:
try:
return json.loads(fence.group(1))
except json.JSONDecodeError:
pass
# 贪婪匹配最大 {...}
brace = re.search(r'\{.*\}', content, re.DOTALL)
if brace:
try:
return json.loads(brace.group(0))
except json.JSONDecodeError:
pass
return None
def _analyze_one_structured(self, path: str, ts: str, idx: int,
known_members: str) -> Optional[Dict]:
try:
@@ -122,12 +147,10 @@ class NvidiaVisionAdapter(BaseModelAdapter):
content = resp.choices[0].message.content
if not content:
return None
try:
data = parse_vlm_json(content)
except VLMOutputInvalidError:
data = self._parse_single_frame_json(content)
if not data:
logger.warning(f"NVIDIA 单帧 JSON 解析失败: {content[:120]}")
return None
# 组装统一字段
return {
"frame_index": idx,
"frame_timestamp": str(data.get("frame_timestamp", ts)),