Enable dedicated MMSeg full mmcv runtime

This commit is contained in:
2026-06-30 13:23:32 +08:00
parent 4d80ec4d75
commit 2d7d54ba13
10 changed files with 121 additions and 35 deletions

View File

@@ -13,20 +13,53 @@ from typing import Any
from .config import settings
def _run_command(command: list[str], cwd: Path | None = None, timeout: int = 60) -> dict[str, Any]:
try:
result = subprocess.run(
command,
cwd=str(cwd or settings.project_root),
capture_output=True,
text=True,
timeout=timeout,
)
return {
"passed": result.returncode == 0,
"returncode": result.returncode,
"stdout": result.stdout[-4000:],
"stderr": result.stderr[-4000:],
}
except subprocess.TimeoutExpired as exc:
return {
"passed": False,
"returncode": None,
"stdout": (exc.stdout or "")[-4000:] if isinstance(exc.stdout, str) else "",
"stderr": (exc.stderr or "")[-4000:] if isinstance(exc.stderr, str) else "",
"error": f"command timed out after {timeout}s",
}
except Exception as exc:
return {"passed": False, "returncode": None, "stdout": "", "stderr": "", "error": str(exc)}
def _run_snippet(code: str, cwd: Path | None = None, timeout: int = 60) -> dict[str, Any]:
result = subprocess.run(
[sys.executable, "-c", code],
cwd=str(cwd or settings.project_root),
capture_output=True,
text=True,
timeout=timeout,
)
return {
"passed": result.returncode == 0,
"returncode": result.returncode,
"stdout": result.stdout[-4000:],
"stderr": result.stderr[-4000:],
}
return _run_command([sys.executable, "-c", code], cwd=cwd, timeout=timeout)
def _run_conda_snippet(env_name: str, code: str, cwd: Path | None = None, timeout: int = 60) -> dict[str, Any]:
detail = _run_command(["conda", "run", "-n", env_name, "python", "-c", code], cwd=cwd, timeout=timeout)
detail["env"] = env_name
return detail
MMSEG_FULL_BUILD_SNIPPET = (
"from mmseg.utils import register_all_modules; "
"register_all_modules(init_default_scope=True); "
"from mmengine.config import Config; "
"from mmseg.registry import MODELS; "
"import mmcv._ext; "
"cfg=Config.fromfile({config_path!r}); "
"model=MODELS.build(cfg.model); "
"print(type(model).__name__)"
)
def _request_json(method: str, url: str, payload: dict[str, Any] | None = None, timeout: int = 10) -> dict[str, Any]:
@@ -182,13 +215,23 @@ def run_model_family_readiness() -> dict[str, Any]:
"required": True,
"detail": {"passed": mmseg_pretrained.exists(), "path": str(mmseg_pretrained), "size": mmseg_pretrained.stat().st_size if mmseg_pretrained.exists() else 0},
},
{
"name": "mmseg_full_env_imports",
"required": True,
"detail": _run_conda_snippet(
settings.mmseg_conda_env,
"import torch, cv2, mmcv, mmengine, mmseg; "
"import mmcv._ext; "
"print(torch.__version__, torch.version.cuda, cv2.__version__, mmcv.__version__, mmseg.__version__)",
timeout=90,
),
},
{
"name": "mmseg_full_model_build",
"required": False,
"detail": _run_snippet(
"from mmengine.config import Config; from mmseg.registry import MODELS; "
f"cfg=Config.fromfile({str(mmseg_config)!r}); "
"model=MODELS.build(cfg.model); print(type(model).__name__)",
"required": True,
"detail": _run_conda_snippet(
settings.mmseg_conda_env,
MMSEG_FULL_BUILD_SNIPPET.format(config_path=str(mmseg_config)),
timeout=90,
),
},

View File

@@ -55,21 +55,36 @@ def validate_project(run_build: bool = False) -> dict:
checks.append({"name": "weights_manifest_present", "passed": manifest.get("count", 0) >= 1})
checks.append({"name": "gpus_query", "passed": bool(get_gpus().get("available"))})
env_names = [item["name"] for item in get_conda_envs().get("envs", [])]
checks.append({"name": "seg_smp_env_exists", "passed": "seg_smp" in env_names})
checks.append({"name": "task_env_exists", "passed": settings.task_conda_env in env_names, "detail": {"env": settings.task_conda_env}})
checks.append({"name": "mmseg_env_exists", "passed": settings.mmseg_conda_env in env_names, "detail": {"env": settings.mmseg_conda_env}})
smoke = _run(
[
"conda",
"run",
"-n",
"seg_smp",
settings.task_conda_env,
"python",
"-c",
"import fastapi, uvicorn, torch, cv2, segmentation_models_pytorch, ultralytics, albumentations, mmengine, mmseg, mmcv; print(torch.__version__, torch.cuda.is_available())",
],
cwd=settings.project_root,
)
checks.append({"name": "seg_smp_backend_smoke", "passed": smoke["passed"], "detail": smoke})
checks.append({"name": "task_env_backend_smoke", "passed": smoke["passed"], "detail": smoke})
mmseg_smoke = _run(
[
"conda",
"run",
"-n",
settings.mmseg_conda_env,
"python",
"-c",
"import torch, cv2, mmcv, mmengine, mmseg; import mmcv._ext; print(torch.__version__, torch.version.cuda, cv2.__version__, mmcv.__version__, mmseg.__version__)",
],
cwd=settings.project_root,
)
checks.append({"name": "mmseg_env_full_mmcv_smoke", "passed": mmseg_smoke["passed"], "detail": mmseg_smoke})
no_weight = _run(["bash", "scripts/check_no_weight_git.sh"], cwd=settings.project_root)
checks.append({"name": "no_weight_in_git", "passed": no_weight["passed"], "detail": no_weight})

View File

@@ -20,6 +20,7 @@ class Settings:
log_dir: Path
weights_root: Path
task_conda_env: str
mmseg_conda_env: str
backend_conda_env: str
weight_mode: str
enable_shell_tasks: bool
@@ -46,6 +47,7 @@ def get_settings() -> Settings:
log_dir=log_dir,
weights_root=weights_root,
task_conda_env=os.getenv("SEG_TASK_CONDA_ENV", "seg_smp"),
mmseg_conda_env=os.getenv("SEG_MMSEG_CONDA_ENV", "seg_mmcv"),
backend_conda_env=os.getenv("SEG_BACKEND_CONDA_ENV", "seg_smp"),
weight_mode=os.getenv("SEG_WEIGHT_MODE", "copy"),
enable_shell_tasks=os.getenv("SEG_ENABLE_SHELL_TASKS", "1") == "1",

View File

@@ -157,7 +157,8 @@ def build_task_checks() -> list[dict[str, Any]]:
for task in TASK_TYPES:
params = TASK_DEFAULTS.get(task, {})
try:
spec = build_module_task(task, dict(params), settings.task_conda_env)
conda_env = settings.mmseg_conda_env if task.startswith("mmseg.") else settings.task_conda_env
spec = build_module_task(task, dict(params), conda_env)
script_path = _command_script_path(spec.command) if spec else None
checks.append(
{

View File

@@ -17,8 +17,14 @@ _running: dict[str, subprocess.Popen] = {}
_lock = threading.Lock()
def default_conda_env_for_job(job_type: str) -> str:
if job_type.startswith("mmseg."):
return settings.mmseg_conda_env
return settings.task_conda_env
def _build_task(request: JobCreate) -> CommandSpec:
conda_env = request.conda_env or settings.task_conda_env
conda_env = request.conda_env or default_conda_env_for_job(request.type)
spec = build_module_task(request.type, request.params, conda_env)
if spec is None:
raise ValueError(f"unsupported job type: {request.type}")
@@ -113,4 +119,3 @@ def cancel_job(job_id: str) -> dict | None:
except ProcessLookupError:
pass
return db.get_job(job_id)

View File

@@ -60,7 +60,7 @@ def get_conda_envs() -> dict:
parts = raw.replace("*", " ").split()
if len(parts) >= 2:
envs.append({"name": parts[0], "path": parts[-1], "active": marker})
return {"available": True, "envs": envs, "task_default": settings.task_conda_env}
return {"available": True, "envs": envs, "task_default": settings.task_conda_env, "mmseg_default": settings.mmseg_conda_env}
def disk_usage() -> dict:
@@ -103,4 +103,3 @@ def scan_results() -> list[dict]:
continue
results.sort(key=lambda item: item["modified"], reverse=True)
return results[:1000]