| """Main entry for Gradio app (single-instance mode for Hugging Face Spaces).""" |
|
|
| import logging |
| import os |
| import sys |
| import tempfile |
| from pathlib import Path |
|
|
|
|
|
|
|
|
| APP_DIR = Path(__file__).resolve().parent |
| PROJECT_ROOT = APP_DIR.parent |
| SRC_DIR = PROJECT_ROOT / "src" |
| VIDEOS_DIR = APP_DIR / "videos" |
| TEMP_DEMOS_DIR = PROJECT_ROOT / "temp_demos" |
| CWD_TEMP_DEMOS_DIR = Path.cwd() / "temp_demos" |
| DEFAULT_LLVMPipe_ICD = Path("/usr/share/vulkan/icd.d/lvp_icd.x86_64.json") |
| DEFAULT_CPU_RENDER_BACKEND = "pci:0" |
| CPU_ONLY_ENV_OVERRIDES = { |
| "CUDA_VISIBLE_DEVICES": "-1", |
| "NVIDIA_VISIBLE_DEVICES": "void", |
| "ROBOMME_RENDER_BACKEND": DEFAULT_CPU_RENDER_BACKEND, |
| } |
| CPU_ONLY_ENV_CLEAR_KEYS = ( |
| "NVIDIA_DRIVER_CAPABILITIES", |
| "SAPIEN_RENDER_DEVICE", |
| "MUJOCO_GL", |
| ) |
|
|
|
|
|
|
|
|
|
|
|
|
| if str(PROJECT_ROOT) not in sys.path: |
| sys.path.insert(0, str(PROJECT_ROOT)) |
| if str(SRC_DIR) not in sys.path: |
| sys.path.insert(0, str(SRC_DIR)) |
|
|
|
|
| def configure_cpu_only_runtime(logger: logging.Logger | None = None): |
| """Force CPU-only execution before importing project modules.""" |
| cleared = {} |
| for key, value in CPU_ONLY_ENV_OVERRIDES.items(): |
| os.environ[key] = value |
| for key in CPU_ONLY_ENV_CLEAR_KEYS: |
| previous = os.environ.pop(key, None) |
| if previous is not None: |
| cleared[key] = previous |
| vk_icd_status = "preserved" |
| if "VK_ICD_FILENAMES" not in os.environ: |
| if DEFAULT_LLVMPipe_ICD.exists(): |
| os.environ["VK_ICD_FILENAMES"] = str(DEFAULT_LLVMPipe_ICD) |
| vk_icd_status = "auto-set" |
| else: |
| vk_icd_status = "unavailable" |
| if logger is not None: |
| logger.info( |
| "Configured CPU-only runtime overrides=%s cleared=%s vk_icd_status=%s vk_icd=%s", |
| CPU_ONLY_ENV_OVERRIDES, |
| cleared, |
| vk_icd_status, |
| os.environ.get("VK_ICD_FILENAMES"), |
| ) |
| return cleared |
|
|
|
|
| configure_cpu_only_runtime() |
|
|
|
|
| def setup_logging() -> logging.Logger: |
| """Configure structured logging for Spaces runtime.""" |
| level_name = "DEBUG" |
| os.environ["LOG_LEVEL"] = level_name |
| level = logging.DEBUG |
| try: |
| sys.stdout.reconfigure(line_buffering=True) |
| sys.stderr.reconfigure(line_buffering=True) |
| except Exception: |
| pass |
| logging.basicConfig( |
| level=level, |
| format=( |
| "%(asctime)s | %(levelname)s | %(name)s | " |
| "pid=%(process)d tid=%(threadName)s | %(message)s" |
| ), |
| stream=sys.stdout, |
| force=True, |
| ) |
| for noisy_logger in [ |
| "asyncio", |
| "httpx", |
| "httpcore", |
| "urllib3", |
| "matplotlib", |
| "PIL", |
| "h5py", |
| "trimesh", |
| "toppra", |
| ]: |
| logging.getLogger(noisy_logger).setLevel(logging.WARNING) |
| logging.getLogger("robomme").setLevel(logging.DEBUG) |
| logger = logging.getLogger("robomme.main") |
| logger.info("Logging initialized with LOG_LEVEL=%s", level_name) |
| return logger |
|
|
|
|
| LOGGER = setup_logging() |
|
|
|
|
| def log_runtime_graphics_env(): |
| """Log graphics-related runtime env so Spaces diagnostics are visible in stdout.""" |
| keys = [ |
| "CUDA_VISIBLE_DEVICES", |
| "NVIDIA_VISIBLE_DEVICES", |
| "NVIDIA_DRIVER_CAPABILITIES", |
| "VK_ICD_FILENAMES", |
| "OMP_NUM_THREADS", |
| "ROBOMME_RENDER_BACKEND", |
| "SAPIEN_RENDER_DEVICE", |
| "MUJOCO_GL", |
| ] |
| snapshot = {key: os.getenv(key) for key in keys} |
| LOGGER.info("Runtime graphics env: %s", snapshot) |
|
|
|
|
| def ensure_media_dirs(): |
| """Ensure media temp directories exist before first write.""" |
| TEMP_DEMOS_DIR.mkdir(parents=True, exist_ok=True) |
| CWD_TEMP_DEMOS_DIR.mkdir(parents=True, exist_ok=True) |
| LOGGER.debug( |
| "Ensured media dirs: temp_demos=%s cwd_temp_demos=%s", |
| TEMP_DEMOS_DIR, |
| CWD_TEMP_DEMOS_DIR, |
| ) |
|
|
|
|
| def build_allowed_paths(): |
| """Build Gradio file access allowlist (absolute, deduplicated).""" |
| candidates = [ |
| Path.cwd(), |
| PROJECT_ROOT, |
| APP_DIR, |
| VIDEOS_DIR, |
| TEMP_DEMOS_DIR, |
| CWD_TEMP_DEMOS_DIR, |
| Path(tempfile.gettempdir()), |
| ] |
| deduped = [] |
| seen = set() |
| for path in candidates: |
| normalized = str(path.resolve()) |
| if normalized not in seen: |
| seen.add(normalized) |
| deduped.append(normalized) |
| LOGGER.debug("Allowed paths resolved (%d): %s", len(deduped), deduped) |
| return deduped |
|
|
|
|
| def main(): |
| configure_cpu_only_runtime(LOGGER) |
| from ui_layout import CSS, create_ui_blocks |
|
|
| LOGGER.info("Starting Gradio real environment entrypoint: %s", __file__) |
| log_runtime_graphics_env() |
| ensure_media_dirs() |
|
|
| os.environ.setdefault("ROBOMME_TEMP_DEMOS_DIR", str(TEMP_DEMOS_DIR)) |
| allowed_paths = build_allowed_paths() |
| server_port = int(os.getenv("PORT", "7860")) |
| |
| LOGGER.info( |
| "Launching UI with server_name=%s server_port=%s ROBOMME_TEMP_DEMOS_DIR=%s", |
| "0.0.0.0", |
| server_port, |
| os.environ.get("ROBOMME_TEMP_DEMOS_DIR"), |
| ) |
| LOGGER.debug("Python path head entries: %s", sys.path[:5]) |
|
|
| demo = create_ui_blocks() |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=server_port, |
| allowed_paths=allowed_paths, |
| ssr_mode=False, |
| debug=True, |
| show_error=True, |
| quiet=False, |
| theme=getattr(demo, "theme", None), |
| css=CSS, |
| head=getattr(demo, "head", None), |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|