fix(worker): #1970 注册 AI 标签 Celery 任务(worker.tag_atom_clip unregistered) (#1987)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
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 / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (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 API Image (pull_request) Successful in 25s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (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 / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m13s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m22s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m4s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m37s
CI/CD Pipeline / CI Gate (pull_request) Successful in 2s
AI Code Review / AI Code Review (pull_request) Successful in 6m47s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 29s
CI/CD Pipeline / Build Staging API Image (push) Successful in 29s
CI/CD Pipeline / Integration Tests (push) Successful in 1m35s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m1s
CI/CD Pipeline / Validate - Style (push) Successful in 2m32s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m12s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / Validate - Security (push) Successful in 3m52s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m7s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m28s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m47s
CI/CD Pipeline / Unit Tests (push) Successful in 11m9s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped

Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1987.
This commit is contained in:
2026-09-19 08:42:07 +08:00
committed by auto-approve-bot
parent 81e1eb47fb
commit 4d98e98b57
2 changed files with 86 additions and 0 deletions
+4
View File
@@ -28,6 +28,10 @@ celery_app.conf.imports = (
"worker_app.tasks.health",
"worker_app.tasks.ingest",
"worker_app.tasks.atom_clips",
# #1970 片段级 AI 标签:必须显式 import 注册,否则 worker 报
# "Received unregistered task of type 'worker.tag_atom_clip'"
"worker_app.tasks.atom_clip_tagging",
"worker_app.tasks.backfill_atom_clip_tags",
"worker_app.tasks.classification",
"worker_app.tasks.generation",
"worker_app.tasks.voice_extraction",
@@ -0,0 +1,82 @@
"""#1970 AI 标签 Celery 任务注册回归测试。
背景:staging 上 worker.generate_atom_clips 正常派发 tag_atom_clip,
但消费端报 "Received unregistered task of type 'worker.tag_atom_clip'",
根因是 celery_app.conf.imports 漏列任务模块,worker 进程从未 import 之。
注意:tests/unit 下大量旧测试在 import 期向 sys.modules 注入
worker_app.celery_app 的 MagicMock 且不还原,全量收集时会污染本测试,
因此这里用 AST 静态解析 + 隔离子进程验证,不依赖 sys.modules 状态。
"""
from __future__ import annotations
import ast
import os
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
CELERY_APP_PY = REPO_ROOT / "apps" / "worker" / "worker_app" / "celery_app.py"
REQUIRED_MODULES = (
"worker_app.tasks.atom_clip_tagging",
"worker_app.tasks.backfill_atom_clip_tags",
)
def _conf_imports_values() -> set[str]:
"""从 celery_app.py AST 中提取 celery_app.conf.imports 元组的字符串项。"""
tree = ast.parse(CELERY_APP_PY.read_text(encoding="utf-8"))
values: set[str] = set()
for node in ast.walk(tree):
if not (isinstance(node, ast.Assign) and len(node.targets) == 1):
continue
target = node.targets[0]
# celery_app.conf.imports = (...) 或 conf.imports = (...)
if not (isinstance(target, ast.Attribute) and target.attr == "imports"):
continue
if isinstance(node.value, (ast.Tuple, ast.List)):
for elt in node.value.elts:
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
values.add(elt.value)
return values
def test_ai_tag_modules_in_celery_imports():
imports = _conf_imports_values()
for module in REQUIRED_MODULES:
assert module in imports, f"{module} 未加入 celery_app.conf.imports"
def test_ai_tag_tasks_registered_in_isolated_process():
"""隔离子进程(无 conftest / 无 sys.modules mock)真实加载 Celery app。"""
# 模拟 worker 启动时按 conf.imports import 任务模块的行为;
# 只导入 AI 标签两个模块(其他模块依赖 cv2 等本地未安装的重依赖)。
code = (
"import importlib, sys; "
"from worker_app.celery_app import celery_app; "
"mods = [m for m in celery_app.conf.imports or () "
"if 'atom_clip_tagging' in m or 'backfill_atom_clip_tags' in m]; "
"[importlib.import_module(m) for m in mods]; "
"missing = [n for n in "
"['worker.tag_atom_clip', 'worker.backfill_atom_clip_tags'] "
"if n not in celery_app.tasks]; "
"sys.exit(1 if missing or len(mods) < 2 else 0)"
)
env = os.environ.copy()
paths = [
str(REPO_ROOT),
str(REPO_ROOT / "apps" / "worker"),
str(REPO_ROOT / "packages"),
]
env["PYTHONPATH"] = os.pathsep.join(paths) + os.pathsep + env.get("PYTHONPATH", "")
result = subprocess.run(
[sys.executable, "-c", code],
capture_output=True,
text=True,
env=env,
timeout=60,
)
assert result.returncode == 0, "隔离子进程中任务未注册成功:\n" f"stdout={result.stdout}\nstderr={result.stderr}"