#!/usr/bin/env python3 """ FloorGen: Autonomous Master Setup, Benchmark & Pipeline Execution Engine. One-click execution script that validates environment, prepares checkpoints, generates publication assets, verifies the test suite, synthesizes sample architectural plans, and launches the web studio or API server. Usage: python run.py # End-to-end setup + sample synthesis python run.py --all # Setup, figures, tests, synthesis, and Gradio studio python run.py --demo # Setup and launch Gradio interactive web UI python run.py --api # Setup and launch FastAPI REST server python run.py --test # Run unit and integration test suite python run.py --figures # Render publication figures and diffusion animation python run.py --generate # Synthesize floorplan from custom prompt """ import os import sys import argparse import subprocess import time from pathlib import Path # Safe terminal output encoding on Windows if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8", errors="replace") if hasattr(sys.stderr, "reconfigure"): sys.stderr.reconfigure(encoding="utf-8", errors="replace") # Terminal ANSI styling helpers class Colors: HEADER = "\033[95m" OKBLUE = "\033[94m" OKCYAN = "\033[96m" OKGREEN = "\033[92m" WARNING = "\033[93m" FAIL = "\033[91m" ENDC = "\033[0m" BOLD = "\033[1m" def print_banner(): banner = f"""{Colors.OKCYAN}{Colors.BOLD} ================================================================================ F L O O R G E N --- S O T A M A S T E R E N G I N E Retrieval-Augmented Vector & Raster Architectural Synthesis Platform ================================================================================{Colors.ENDC}""" print(banner) print(f" {Colors.BOLD}Python Environment:{Colors.ENDC} {sys.executable}") print(f" {Colors.BOLD}Working Directory:{Colors.ENDC} {Path.cwd()}") import torch cuda_status = f"{Colors.OKGREEN}CUDA Available ({torch.cuda.get_device_name(0)}){Colors.ENDC}" if torch.cuda.is_available() else f"{Colors.WARNING}CPU Mode{Colors.ENDC}" print(f" {Colors.BOLD}Compute Device:{Colors.ENDC} {cuda_status}") print("=" * 80 + "\n") def check_and_install_dependencies(): """Step 1: Check required dependencies and install missing ones.""" print(f"{Colors.OKBLUE}[Step 1/6] Validating Python Dependencies...{Colors.ENDC}") required_packages = [ ("torch", "torch"), ("numpy", "numpy"), ("networkx", "networkx"), ("faiss", "faiss-cpu"), ("sentence_transformers", "sentence-transformers"), ("shapely", "shapely"), ("ezdxf", "ezdxf"), ("ortools", "ortools"), ("cv2", "opencv-python"), ("fastapi", "fastapi"), ("uvicorn", "uvicorn"), ("gradio", "gradio"), ("matplotlib", "matplotlib"), ] missing = [] for mod_name, pkg_name in required_packages: try: __import__(mod_name) except ImportError: missing.append(pkg_name) if missing: print(f" {Colors.WARNING}Missing packages detected: {missing}{Colors.ENDC}") print(f" Installing missing packages via pip...") cmd = [sys.executable, "-m", "pip", "install"] + missing res = subprocess.run(cmd) if res.returncode != 0: print(f" {Colors.FAIL}Error installing packages. Continuing with available environment.{Colors.ENDC}") else: print(f" {Colors.OKGREEN}All packages successfully installed.{Colors.ENDC}") else: print(f" {Colors.OKGREEN}[OK] All core dependencies verified and ready.{Colors.ENDC}") def setup_directories(): """Step 2: Ensure required directory hierarchy and environment files exist.""" print(f"\n{Colors.OKBLUE}[Step 2/6] Verifying Workspace Structure & Directories...{Colors.ENDC}") dirs_to_create = [ "checkpoints/sota", "data/processed", "data/raw", "outputs/run_demo", "static/images", "static/css", "static/js" ] for d in dirs_to_create: p = Path(d) p.mkdir(parents=True, exist_ok=True) print(f" {Colors.OKGREEN}[OK] Workspace directories verified.{Colors.ENDC}") # Ensure .env exists env_file = Path(".env") env_example = Path(".env.example") if not env_file.exists() and env_example.exists(): import shutil shutil.copy(env_example, env_file) print(f" {Colors.OKGREEN}[OK] Initialized .env from .env.example{Colors.ENDC}") # Check and ensure local Ollama service is active ensure_ollama_service() def ensure_ollama_service(): """Ensure local Ollama service is running for Qwen brief reasoning.""" from floorgen.rag.llm_brief_parser import ensure_ollama_service as _ensure if _ensure(timeout=8.0): print(f" {Colors.OKGREEN}[OK] Local Ollama service is online (Qwen reasoning active).{Colors.ENDC}") return True print(f" {Colors.WARNING}Notice: Ollama not running; rule-based parser active.{Colors.ENDC}") return False def verify_or_initialize_checkpoints(): """Step 3: Verify model checkpoints; auto-initialize if missing.""" print(f"\n{Colors.OKBLUE}[Step 3/6] Verifying Neural Model Checkpoints...{Colors.ENDC}") sota_ckpts = [ Path("checkpoints/sota/wall_graph.pt"), Path("checkpoints/sota/raster_decoder.pt"), Path("checkpoints/sota/vqvae.pt"), Path("checkpoints/rag_diffusion.pt"), ] missing = [p for p in sota_ckpts if not p.exists()] if missing: print(f" {Colors.WARNING}Initializing missing checkpoints: {[p.name for p in missing]}...{Colors.ENDC}") from floorgen.models.train_sota import train_sota_models train_sota_models(epochs=1, batch_size=8, device="cuda" if sys.platform != "darwin" and os.environ.get("CUDA_VISIBLE_DEVICES") != "" else "cpu") print(f" {Colors.OKGREEN}[OK] Checkpoints initialized successfully.{Colors.ENDC}") else: print(f" {Colors.OKGREEN}[OK] All checkpoints present and verified:{Colors.ENDC}") for p in sota_ckpts: sz_kb = p.stat().st_size / 1024 print(f" - {p.name:<22} ({sz_kb:.1f} KB)") def render_figures_and_assets(force=False): """Step 4: Generate publication figures and diffusion animation.""" print(f"\n{Colors.OKBLUE}[Step 4/6] Generating Publication Figures & Animations...{Colors.ENDC}") figures_exist = all((Path("static/images") / f"fig{i}_{name}.png").exists() for i, name in [ (1, "motivation"), (2, "system_model"), (3, "spatial_graph"), (4, "architecture"), (5, "telemetry"), (6, "benchmark_boxplots"), (7, "spatial_traces") ]) gif_exists = Path("diffusion_synthesis.gif").exists() if force or not figures_exist: print(" Rendering 7 academic publication figures (.pdf, .svg, 300 DPI .png)...") from generate_paper_figures import ( generate_figure_1, generate_figure_2, generate_figure_3, generate_figure_4, generate_figure_5, generate_figure_6, generate_figure_7 ) generate_figure_1() generate_figure_2() generate_figure_3() generate_figure_4() generate_figure_5() generate_figure_6() generate_figure_7() print(f" {Colors.OKGREEN}[OK] All 7 figures rendered into static/images/{Colors.ENDC}") if force or not gif_exists: print(" Synthesizing real reverse-diffusion animation (diffusion_synthesis.gif)...") cmd = [sys.executable, "-m", "floorgen.animate_diffusion", "--output", "diffusion_synthesis.gif", "--frames", "30"] subprocess.run(cmd) print(f" {Colors.OKGREEN}[OK] diffusion_synthesis.gif rendered.{Colors.ENDC}") else: print(f" {Colors.OKGREEN}[OK] Figures and animations are up to date.{Colors.ENDC}") def run_test_suite(): """Step 5: Execute unit and integration tests.""" print(f"\n{Colors.OKBLUE}[Step 5/6] Executing FloorGen Test Suite...{Colors.ENDC}") test_files = [ "tests/test_sota_pipeline.py", "tests/test_flux_lora.py", "tests/test_sota_remediation.py" ] cmd = [sys.executable, "-m", "pytest"] + test_files + ["-q"] t0 = time.time() res = subprocess.run(cmd) dt = time.time() - t0 if res.returncode == 0: print(f" {Colors.OKGREEN}[OK] Test suite passed in {dt:.1f}s (100% pass rate).{Colors.ENDC}") else: print(f" {Colors.WARNING}Some tests had warnings or errors (exit code {res.returncode}).{Colors.ENDC}") def synthesize_sample_floorplan(custom_brief=None): """Step 6: Synthesize a complete floorplan and export all CAD/BIM deliverables.""" print(f"\n{Colors.OKBLUE}[Step 6/6] Executing End-to-End Architectural Synthesis...{Colors.ENDC}") from floorgen.pipeline_sota import SOTAPipeline brief = custom_brief or ( "Modern 3-bedroom residential apartment with a master bedroom, " "secondary bedroom, living room, open kitchen, 2 bathrooms, and a balcony." ) print(f" {Colors.BOLD}Input Architectural Brief:{Colors.ENDC} \"{brief}\"") t0 = time.time() pipeline = SOTAPipeline() results = pipeline.run( brief=brief, output_dir="outputs/run_demo", export_dxf_flag=True, export_ifc_flag=True, export_raster_flag=True, export_edges_flag=True ) dt = time.time() - t0 print(f"\n {Colors.OKGREEN}[OK] Synthesis Completed in {dt*1000:.1f} ms!{Colors.ENDC}") files_dict = results.get("files", {}) if not files_dict: # Fallback to scanning the output directory out_dir = Path("outputs/run_demo") files_dict = {f.name: str(f) for f in out_dir.iterdir() if f.is_file()} print(f" {Colors.BOLD}Generated Deliverables (in outputs/run_demo/):{Colors.ENDC}") for k, v in files_dict.items(): if isinstance(v, str) and os.path.exists(v): sz = os.path.getsize(v) print(f" - {k:<18} -> {v} ({sz/1024:.1f} KB)") comp = results.get("compliance", {}) if comp: print(f"\n {Colors.BOLD}Architectural Compliance Audit:{Colors.ENDC}") print(f" - IRC/IBC Pass: {Colors.OKGREEN}{comp.get('is_compliant', True)}{Colors.ENDC}") print(f" - Compliance Score: {Colors.OKGREEN}{comp.get('compliance_score', 1.0)*100:.1f}%{Colors.ENDC}") # Print summary print(f"\n{Colors.OKCYAN}{Colors.BOLD}================================================================================{Colors.ENDC}") print(f"{Colors.OKGREEN}{Colors.BOLD} [+] ALL FLOORGEN SYSTEMS OPERATIONAL AND DELIVERABLES GENERATED! [+]{Colors.ENDC}") print(f"{Colors.OKCYAN}{Colors.BOLD}================================================================================{Colors.ENDC}") def launch_demo(): print(f"\n{Colors.OKCYAN}{Colors.BOLD}================================================================================{Colors.ENDC}") print(f"{Colors.OKGREEN}{Colors.BOLD} [*] Launching FloorGen Interactive Gradio Studio...{Colors.ENDC}") print(f" Local URL: {Colors.BOLD}http://localhost:7860{Colors.ENDC} or {Colors.BOLD}http://127.0.0.1:7860{Colors.ENDC}") print(f"{Colors.OKCYAN}{Colors.BOLD}================================================================================{Colors.ENDC}\n") sys.stdout.flush() import floorgen.demo.app as demo_app from floorgen.demo.theme import get_ocean_depth_theme, OCEAN_DEPTH_HEAD_SCRIPT, OCEAN_DEPTH_CSS app = getattr(demo_app, "demo", None) if app is None: app = demo_app.create_demo() try: app.launch( server_name="127.0.0.1", server_port=7860, inbrowser=False, theme=get_ocean_depth_theme(), css=OCEAN_DEPTH_CSS, head=OCEAN_DEPTH_HEAD_SCRIPT ) except KeyboardInterrupt: print(f"\n{Colors.WARNING}FloorGen Studio stopped by user.{Colors.ENDC}") def launch_api(): print(f"\n{Colors.OKCYAN}[*] Launching FloorGen FastAPI REST Server...{Colors.ENDC}") print(f" Interactive Swagger Docs: {Colors.BOLD}http://localhost:8000/docs{Colors.ENDC}\n") import uvicorn uvicorn.run("floorgen.api.server:app", host="0.0.0.0", port=8000, reload=False) def main(): parser = argparse.ArgumentParser( description="FloorGen SOTA Master Setup & Execution Engine" ) parser.add_argument("--demo", action="store_true", help="Launch interactive Gradio Web Studio") parser.add_argument("--api", action="store_true", help="Launch FastAPI REST server") parser.add_argument("--test", action="store_true", help="Run comprehensive test suite") parser.add_argument("--figures", action="store_true", help="Force regenerate publication figures and animation") parser.add_argument("--generate", action="store_true", help="Synthesize floorplan without launching servers") parser.add_argument("--brief", type=str, default=None, help="Custom architectural brief for synthesis") parser.add_argument("--all", action="store_true", help="Run setup, figures, tests, synthesis, and launch demo") args = parser.parse_args() # Fast path: direct launch for interactive modes if args.demo: ensure_ollama_service() launch_demo() return if args.api: ensure_ollama_service() launch_api() return if args.test and not args.all: run_test_suite() return if args.figures and not args.all: render_figures_and_assets(force=True) return # Full setup workflow (for first-time setup or --all / --generate) check_and_install_dependencies() setup_directories() verify_or_initialize_checkpoints() if args.all: render_figures_and_assets(force=True) run_test_suite() else: render_figures_and_assets(force=False) # Synthesize sample floorplan synthesize_sample_floorplan(custom_brief=args.brief) if args.all: launch_demo() else: print(f"\n{Colors.BOLD}Ready-to-use commands:{Colors.ENDC}") print(f" * Instant Web Studio: {Colors.OKCYAN}python app.py{Colors.ENDC} (or: {Colors.OKCYAN}python run.py --demo{Colors.ENDC})") print(f" * Launch REST Server: {Colors.OKCYAN}python run.py --api{Colors.ENDC}") print(f" * Run Unit Tests: {Colors.OKCYAN}python run.py --test{Colors.ENDC}") print(f" * Refresh Figures: {Colors.OKCYAN}python run.py --figures{Colors.ENDC}") print(f" * Custom Synthesis: {Colors.OKCYAN}python run.py --brief \"2-bedroom loft with kitchen and balcony\"{Colors.ENDC}\n") if __name__ == "__main__": main()