fix(vision): #2203 V2快速路径两个P0 bug修复
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m0s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m21s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 44s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m16s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 14s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m22s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 5m25s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m53s
AI Code Review / AI Code Review (pull_request) Successful in 6m57s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 7m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m35s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 13m20s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 15m11s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m0s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m21s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 44s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m16s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 14s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m22s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 5m25s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m53s
AI Code Review / AI Code Review (pull_request) Successful in 6m57s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 7m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m35s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 13m20s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 15m11s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
E2E发现两个P0 bug: 1. as_completed(timeout=...)抛TimeoutError时未被捕获,异常直接冒泡到外层, 跳过了pro VLM兜底路径,导致3/3返回空结果。 修复:包裹try/except TimeoutError,cancel未完成futures,继续走pro兜底。 2. 外层_step_image_analysis设置client.max_retries=0不生效(ThreadPool线程 里可能被其他任务覆盖/共享单例状态),实际仍重试1次(2次调用×6s=12s超时)。 修复:在vlm_fast_json/vlm_fallback内部强制max_retries=0并在finally里恢复。 3. 收紧超时:fast总超时8→6s、lite JSON 6→5s、OCR 8→5s,让lite不够快时 尽快走pro兜底(pro单次45s足够)。 本地mock验证:fast命中1.5s返回有效portrait_prompt;fast超时6s正确触发pro fallback。
This commit is contained in:
@@ -21,9 +21,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# 可通过环境变量调参(有默认值,无需配置即可跑)
|
||||
_IMG_WORKERS = int(os.environ.get("VISION_V2_IMG_WORKERS", "8"))
|
||||
_FAST_TIMEOUT = float(os.environ.get("VISION_V2_FAST_TIMEOUT", "8"))
|
||||
_FAST_JSON_TIMEOUT = float(os.environ.get("VISION_V2_FAST_JSON_TIMEOUT", "6"))
|
||||
_OCR_TIMEOUT = float(os.environ.get("VISION_V2_OCR_TIMEOUT", "6"))
|
||||
_FAST_TIMEOUT = float(os.environ.get("VISION_V2_FAST_TIMEOUT", "6"))
|
||||
_FAST_JSON_TIMEOUT = float(os.environ.get("VISION_V2_FAST_JSON_TIMEOUT", "5"))
|
||||
_OCR_TIMEOUT = float(os.environ.get("VISION_V2_OCR_TIMEOUT", "5"))
|
||||
_PRO_TIMEOUT = float(os.environ.get("VISION_V2_PRO_TIMEOUT", "45"))
|
||||
|
||||
_FALLBACK_RESULT = {
|
||||
@@ -62,16 +62,23 @@ def analyze_image_v2(idx: int, img_url: str) -> dict[str, Any]:
|
||||
with ThreadPoolExecutor(max_workers=2) as pool:
|
||||
f_fj = pool.submit(vlm_fast_json.call_fast_json, img_url, timeout=_FAST_JSON_TIMEOUT)
|
||||
f_ocr = pool.submit(ocr_volc.call_ocr, img_url, timeout=_OCR_TIMEOUT)
|
||||
for fut in as_completed([f_fj, f_ocr], timeout=_FAST_TIMEOUT + 2):
|
||||
try:
|
||||
res = fut.result(timeout=1)
|
||||
except Exception as e:
|
||||
logger.warning("[vision.v2] 图片 #%d 子任务异常: %s", idx, e)
|
||||
continue
|
||||
if fut is f_fj and isinstance(res, dict):
|
||||
fj_result = res
|
||||
elif fut is f_ocr and isinstance(res, list):
|
||||
ocr_result = res
|
||||
try:
|
||||
for fut in as_completed([f_fj, f_ocr], timeout=_FAST_TIMEOUT):
|
||||
try:
|
||||
res = fut.result(timeout=1)
|
||||
except Exception as e:
|
||||
logger.warning("[vision.v2] 图片 #%d 子任务异常: %s", idx, e)
|
||||
continue
|
||||
if fut is f_fj and isinstance(res, dict):
|
||||
fj_result = res
|
||||
elif fut is f_ocr and isinstance(res, list):
|
||||
ocr_result = res
|
||||
except TimeoutError:
|
||||
# fast 整体超时,取消还没跑完的子任务,继续走 pro 兜底
|
||||
for f in (f_fj, f_ocr):
|
||||
if not f.done():
|
||||
f.cancel()
|
||||
logger.warning("[vision.v2] 图片 #%d fast路径超时(%.0fs),走pro兜底", idx, _FAST_TIMEOUT)
|
||||
|
||||
fast_elapsed = time.time() - t0
|
||||
|
||||
|
||||
@@ -164,6 +164,8 @@ def call_pro_vlm(
|
||||
return None
|
||||
|
||||
use_model = model or DEFAULT_PRO_MODEL
|
||||
_orig_retries = client.max_retries
|
||||
client.max_retries = 0
|
||||
try:
|
||||
raw = client.vision_completion(
|
||||
messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
|
||||
@@ -175,7 +177,9 @@ def call_pro_vlm(
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("[vision.vlm] 图片 #%d pro VLM 调用失败 elapsed=%.1fs err=%s", idx, time.time() - t0, e)
|
||||
client.max_retries = _orig_retries
|
||||
return None
|
||||
client.max_retries = _orig_retries
|
||||
|
||||
elapsed = time.time() - t0
|
||||
if not raw:
|
||||
|
||||
@@ -93,17 +93,23 @@ def call_fast_json(
|
||||
return None
|
||||
|
||||
use_model = model or DEFAULT_LITE_MODEL
|
||||
raw = client.vision_completion(
|
||||
messages=[
|
||||
{"role": "system", "content": _FAST_SYSTEM},
|
||||
{"role": "user", "content": _FAST_USER},
|
||||
],
|
||||
images=[img_url],
|
||||
temperature=0.1,
|
||||
max_tokens=max_tokens,
|
||||
timeout=timeout,
|
||||
model=use_model,
|
||||
)
|
||||
# 强制不重试:lite 是快速路径,失败直接走外层 pro 兜底
|
||||
_orig_retries = client.max_retries
|
||||
client.max_retries = 0
|
||||
try:
|
||||
raw = client.vision_completion(
|
||||
messages=[
|
||||
{"role": "system", "content": _FAST_SYSTEM},
|
||||
{"role": "user", "content": _FAST_USER},
|
||||
],
|
||||
images=[img_url],
|
||||
temperature=0.1,
|
||||
max_tokens=max_tokens,
|
||||
timeout=timeout,
|
||||
model=use_model,
|
||||
)
|
||||
finally:
|
||||
client.max_retries = _orig_retries
|
||||
elapsed = time.time() - t0
|
||||
if raw is None:
|
||||
logger.warning("[vision.v2] fast_json 返回 None elapsed=%.1fs model=%s", elapsed, use_model)
|
||||
|
||||
Reference in New Issue
Block a user