feat: HSAP platform v2 — modular navigation, quality review, audit log, world model simulation

Major changes:
- New frontend (platform/web/): Vite + React 18 + TypeScript + Tailwind
- 4-module navigation: 数据送标 / 模型管理 / 车队管理 / 系统管理
- Data catalog with charts (DMS/ADAS/Lane 3-tab view)
- Quality review workflow (标注质检): Good/Fine/Bad scoring with auto-advance
- Audit enhancements: batch operations, rejection categories, Feishu notifications
- Operation audit log (操作日志)
- World model simulation studio (仿真工坊)
- Dataset version management with snapshots and diff
- ADAS 7-class dataset integration (138K images organized + compressed)
- User management with Feishu integration and pagination
- CRUD/search/filter on all pages, card layout redesign
- PIL-optimized image overlay rendering
- Auto-snapshot on build, in_review workflow stage
- Removed embedded algorithm code (now in workspace)
This commit is contained in:
2026-06-03 11:40:21 +08:00
parent 7c43b44c57
commit e72bc061c5
5487 changed files with 979207 additions and 6197 deletions

View File

@@ -1,6 +1,7 @@
"""DMS YOLO 引擎local 研发轨 / platform 平台轨。"""
from __future__ import annotations
import os
import subprocess
import sys
from pathlib import Path
@@ -13,11 +14,25 @@ YOLO_ROOT = WORKSPACE / "algorithms/dms_yolo/code"
DATASET_ROOT = WORKSPACE / "datasets/dms"
def _latest_run(task: str) -> tuple[str | None, str | None]:
def _resolve_yaml_key(task: str, submode: str | None = None) -> tuple[str, str]:
import sys
scripts = DATASET_ROOT / "scripts"
if str(scripts) not in sys.path:
sys.path.insert(0, str(scripts))
from task_registry import get_mode_config, resolve_task_id, train_yaml_key
reg = yaml.safe_load((DATASET_ROOT / "datasets.registry.yaml").read_text(encoding="utf-8"))
typ = reg["tasks"][task]["type"]
task, submode = resolve_task_id(task, submode)
mcfg = get_mode_config(task, submode, reg)
key = train_yaml_key(task, submode, reg)
return key, mcfg["type"]
def _latest_run(task: str, submode: str | None = None) -> tuple[str | None, str | None]:
yaml_key, typ = _resolve_yaml_key(task, submode)
mode_yolo = {"detect": "detect", "pose": "pose", "classify": "classify"}[typ]
runs = sorted((YOLO_ROOT / "runs" / mode_yolo).glob(f"{task}_*"), key=lambda p: p.stat().st_mtime, reverse=True)
runs = sorted((YOLO_ROOT / "runs" / mode_yolo).glob(f"{yaml_key}_*"), key=lambda p: p.stat().st_mtime, reverse=True)
if not runs:
return None, None
run_dir = runs[0].resolve()
@@ -25,17 +40,21 @@ def _latest_run(task: str) -> tuple[str | None, str | None]:
return str(run_dir), str(best) if best.is_file() else None
def train_local(task: str, mode: str = "full", config_overrides: dict | None = None) -> dict[str, Any]:
def train_local(task: str, mode: str = "full", config_overrides: dict | None = None, submode: str | None = None) -> dict[str, Any]:
"""研发轨train.sh不 refresh yaml_active不更新 candidate。"""
env = {**os.environ}
if submode:
env["SUBMODE"] = submode
proc = subprocess.run(
[str(DATASET_ROOT / "scripts" / "train.sh"), task, mode],
cwd=str(DATASET_ROOT),
capture_output=True,
text=True,
env=env,
)
if proc.returncode != 0:
raise RuntimeError(proc.stderr or proc.stdout)
run_dir, best_weights = _latest_run(task)
run_dir, best_weights = _latest_run(task, submode)
return {
"ok": True,
"stdout": proc.stdout,
@@ -48,28 +67,33 @@ def train_local(task: str, mode: str = "full", config_overrides: dict | None = N
}
def train_platform(task: str, mode: str = "full") -> dict[str, Any]:
def train_platform(task: str, mode: str = "full", submode: str | None = None) -> dict[str, Any]:
"""平台轨refresh yaml_active + train + 更新 candidate。"""
yaml_key, _ = _resolve_yaml_key(task, submode)
subprocess.check_call(
[sys.executable, str(DATASET_ROOT / "scripts/refresh_yaml.py"), "--task", task],
cwd=str(DATASET_ROOT),
)
env = {**os.environ}
if submode:
env["SUBMODE"] = submode
proc = subprocess.run(
[str(DATASET_ROOT / "scripts" / "train.sh"), task, mode],
cwd=str(DATASET_ROOT),
capture_output=True,
text=True,
env=env,
)
if proc.returncode != 0:
raise RuntimeError(proc.stderr or proc.stdout)
run_dir, best_weights = _latest_run(task)
run_dir, best_weights = _latest_run(task, submode)
candidate = None
if best_weights:
candidate = best_weights
versions_path = DATASET_ROOT / "manifests/train_versions.yaml"
versions = yaml.safe_load(versions_path.read_text(encoding="utf-8"))
versions[task]["candidate"] = candidate
versions[yaml_key]["candidate"] = candidate
versions_path.write_text(yaml.dump(versions, allow_unicode=True, sort_keys=False), encoding="utf-8")
return {