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
|
enabled: true
|
||||||
model_name: "gemini-flash-latest" # v1beta 下 gemini-1.5-flash 会 404
|
model_name: "gemini-flash-latest" # v1beta 下 gemini-1.5-flash 会 404
|
||||||
api_key: "${GEMINI_API_KEY}"
|
api_key: "${GEMINI_API_KEY}"
|
||||||
timeout: 30
|
timeout: 90
|
||||||
circuit_breaker:
|
circuit_breaker:
|
||||||
enabled: true
|
enabled: true
|
||||||
threshold: 3
|
threshold: 5
|
||||||
cooldown: 600
|
cooldown: 300
|
||||||
|
|
||||||
- provider: "nvidia"
|
- provider: "nvidia"
|
||||||
role: "vision"
|
role: "vision"
|
||||||
@@ -66,11 +66,11 @@ models:
|
|||||||
model_name: "meta/llama-3.2-11b-vision-instruct"
|
model_name: "meta/llama-3.2-11b-vision-instruct"
|
||||||
base_url: "https://integrate.api.nvidia.com/v1"
|
base_url: "https://integrate.api.nvidia.com/v1"
|
||||||
api_key: "${NVIDIA_API_KEY}"
|
api_key: "${NVIDIA_API_KEY}"
|
||||||
timeout: 20
|
timeout: 30
|
||||||
circuit_breaker:
|
circuit_breaker:
|
||||||
enabled: true
|
enabled: true
|
||||||
threshold: 3
|
threshold: 5
|
||||||
cooldown: 600
|
cooldown: 300
|
||||||
|
|
||||||
# 本地模型:纯文本 qwen2.5:7b,仅参与智能问答,作为 Gemini/NVIDIA 都失败时的兜底
|
# 本地模型:纯文本 qwen2.5:7b,仅参与智能问答,作为 Gemini/NVIDIA 都失败时的兜底
|
||||||
- provider: "ollama"
|
- provider: "ollama"
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ SDK: openai (NIM 兼容 OpenAI API 规范)
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
|
import re
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from .base_adapter import BaseModelAdapter
|
from .base_adapter import BaseModelAdapter
|
||||||
from .circuit_breaker import CircuitBreaker
|
from .circuit_breaker import CircuitBreaker
|
||||||
from ..logger import setup_logger
|
from ..logger import setup_logger
|
||||||
from ..ai_orchestrator.json_parser import parse_vlm_json, VLMOutputInvalidError
|
|
||||||
|
|
||||||
logger = setup_logger('fam-edge.nvidia_adapter')
|
logger = setup_logger('fam-edge.nvidia_adapter')
|
||||||
|
|
||||||
@@ -98,6 +99,30 @@ class NvidiaVisionAdapter(BaseModelAdapter):
|
|||||||
# NVIDIA 单帧无法跨帧综合 global_summary,交由 Edge format_cloud_result 格式化生成
|
# NVIDIA 单帧无法跨帧综合 global_summary,交由 Edge format_cloud_result 格式化生成
|
||||||
return {"frame_details": frame_details}
|
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,
|
def _analyze_one_structured(self, path: str, ts: str, idx: int,
|
||||||
known_members: str) -> Optional[Dict]:
|
known_members: str) -> Optional[Dict]:
|
||||||
try:
|
try:
|
||||||
@@ -122,12 +147,10 @@ class NvidiaVisionAdapter(BaseModelAdapter):
|
|||||||
content = resp.choices[0].message.content
|
content = resp.choices[0].message.content
|
||||||
if not content:
|
if not content:
|
||||||
return None
|
return None
|
||||||
try:
|
data = self._parse_single_frame_json(content)
|
||||||
data = parse_vlm_json(content)
|
if not data:
|
||||||
except VLMOutputInvalidError:
|
|
||||||
logger.warning(f"NVIDIA 单帧 JSON 解析失败: {content[:120]}")
|
logger.warning(f"NVIDIA 单帧 JSON 解析失败: {content[:120]}")
|
||||||
return None
|
return None
|
||||||
# 组装统一字段
|
|
||||||
return {
|
return {
|
||||||
"frame_index": idx,
|
"frame_index": idx,
|
||||||
"frame_timestamp": str(data.get("frame_timestamp", ts)),
|
"frame_timestamp": str(data.get("frame_timestamp", ts)),
|
||||||
|
|||||||
Reference in New Issue
Block a user