From 853cb21542c861f9915c617f295910b02cc3ab20 Mon Sep 17 00:00:00 2001 From: ericwyuan Date: Thu, 20 Aug 2026 11:02:26 +0800 Subject: [PATCH] fix: NVIDIA single-frame JSON parsing + Gemini timeout/circuit breaker tuning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- fam-edge/config/config.yaml | 12 +++---- .../fam_edge/model_adapters/nvidia_adapter.py | 33 ++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/fam-edge/config/config.yaml b/fam-edge/config/config.yaml index 32a3e08..79f0e68 100644 --- a/fam-edge/config/config.yaml +++ b/fam-edge/config/config.yaml @@ -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" diff --git a/fam-edge/src/fam_edge/model_adapters/nvidia_adapter.py b/fam-edge/src/fam_edge/model_adapters/nvidia_adapter.py index f702b1f..83cd511 100644 --- a/fam-edge/src/fam_edge/model_adapters/nvidia_adapter.py +++ b/fam-edge/src/fam_edge/model_adapters/nvidia_adapter.py @@ -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)),