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:
@@ -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"
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user