Spaces:
Sleeping
Sleeping
| """ | |
| Frontend Builder - Automatically builds or copies the Daggr frontend. | |
| This module checks if the frontend dist directory exists and builds/copies it as needed. | |
| Useful for Hugging Face Spaces where the frontend might be in site-packages. | |
| """ | |
| import os | |
| import subprocess | |
| import sys | |
| import shutil | |
| import importlib.util | |
| from pathlib import Path | |
| def find_daggr_package_location(): | |
| """Find where the daggr package is actually installed using importlib.""" | |
| try: | |
| # Try to import daggr and find its location | |
| spec = importlib.util.find_spec("daggr") | |
| if spec and spec.origin: | |
| daggr_init = Path(spec.origin) | |
| daggr_root = daggr_init.parent | |
| print(f"[DEBUG] Found daggr package at: {daggr_root}") | |
| frontend_path = daggr_root / "frontend" | |
| if frontend_path.exists(): | |
| print(f"[DEBUG] Found frontend in daggr package: {frontend_path}") | |
| return frontend_path | |
| except Exception as e: | |
| print(f"[DEBUG] Error finding daggr with importlib: {e}") | |
| return None | |
| def find_daggr_site_packages(): | |
| """Find daggr in Python site-packages using importlib.""" | |
| locations = [] | |
| # Try importlib first (most reliable) | |
| pkg_frontend = find_daggr_package_location() | |
| if pkg_frontend: | |
| locations.append(pkg_frontend) | |
| # Fallback: check common site-packages paths | |
| try: | |
| import site | |
| for site_dir in site.getsitepackages(): | |
| daggr_path = Path(site_dir) / "daggr" / "frontend" | |
| if daggr_path.exists(): | |
| print(f"[DEBUG] Found daggr frontend in site-packages: {daggr_path}") | |
| locations.append(daggr_path) | |
| except Exception as e: | |
| print(f"[DEBUG] Error checking site-packages: {e}") | |
| return locations | |
| def get_frontend_dir(): | |
| """Get the frontend directory path, checking local and site-packages locations.""" | |
| # Check local/mounted locations first | |
| local_paths = [ | |
| Path("/app/daggr/frontend"), # Hugging Face default | |
| Path.cwd() / "daggr" / "frontend", # Current working directory | |
| Path(__file__).parent / "daggr" / "frontend", # Relative to this file | |
| Path(__file__).parent.parent / "daggr" / "frontend", # Parent directory | |
| Path(__file__).parent.parent.parent / "daggr" / "frontend", # Grandparent | |
| ] | |
| print(f"[DEBUG] Looking for frontend directory...") | |
| print(f"[DEBUG] Current working directory: {Path.cwd()}") | |
| print(f"[DEBUG] Script location: {Path(__file__)}") | |
| # Check local paths | |
| for path in local_paths: | |
| print(f"[DEBUG] Checking local: {path}") | |
| if path.exists(): | |
| print(f"[DEBUG] Found at: {path}") | |
| return path | |
| # Check site-packages using importlib | |
| print(f"[DEBUG] Checking site-packages with importlib...") | |
| for path in find_daggr_site_packages(): | |
| if path.exists(): | |
| print(f"[DEBUG] Found in site-packages: {path}") | |
| return path | |
| print(f"[DEBUG] No frontend directory found") | |
| return None | |
| def copy_dist_from_source(): | |
| """ | |
| Try to copy pre-built dist from source code to site-packages. | |
| This is useful when the frontend is already built but in a different location. | |
| """ | |
| # Look for pre-built dist in common source locations | |
| source_paths = [ | |
| Path("/workspace/AcuarelaPortraits/daggr/frontend/dist"), | |
| Path("/workspace/temp_daggr_build/daggr/frontend/dist"), | |
| Path("/home/user/AcuarelaPortraits/daggr/frontend/dist"), | |
| Path.cwd() / "AcuarelaPortraits" / "daggr" / "frontend" / "dist", | |
| Path.cwd() / "daggr" / "frontend" / "dist", | |
| Path(__file__).parent / "daggr" / "frontend" / "dist", | |
| ] | |
| print("[DEBUG] Looking for pre-built dist in source locations...") | |
| for source_dist in source_paths: | |
| print(f"[DEBUG] Checking source: {source_dist}") | |
| if source_dist.exists() and (source_dist / "index.html").exists(): | |
| print(f"[DEBUG] Found pre-built dist at: {source_dist}") | |
| # Try to find and copy to site-packages | |
| frontend_dir = find_daggr_package_location() | |
| if frontend_dir and frontend_dir.exists(): | |
| target_dist = frontend_dir / "dist" | |
| print(f"[*] Copying pre-built frontend from {source_dist}") | |
| print(f" to {target_dist}") | |
| try: | |
| # Remove existing dist if present | |
| if target_dist.exists(): | |
| shutil.rmtree(target_dist) | |
| # Copy the dist directory | |
| shutil.copytree(source_dist, target_dist) | |
| print(f"[OK] Frontend copied successfully") | |
| return True | |
| except Exception as e: | |
| print(f"[WARNING] Failed to copy dist: {e}") | |
| continue | |
| print("[DEBUG] No pre-built dist found in source locations") | |
| return False | |
| def is_frontend_built(): | |
| """Check if the frontend has been built (dist directory exists).""" | |
| frontend_dir = get_frontend_dir() | |
| if not frontend_dir: | |
| print("[WARNING] Frontend directory not located") | |
| return False | |
| dist_dir = frontend_dir / "dist" | |
| has_dist = dist_dir.exists() | |
| has_index = (dist_dir / "index.html").exists() if has_dist else False | |
| print(f"[DEBUG] Frontend dir: {frontend_dir}") | |
| print(f"[DEBUG] Dist dir exists: {has_dist}") | |
| print(f"[DEBUG] index.html exists: {has_index}") | |
| return has_dist and has_index | |
| def build_frontend(): | |
| """Build the frontend using npm.""" | |
| frontend_dir = get_frontend_dir() | |
| if not frontend_dir: | |
| print("[WARNING] Frontend directory not found. Skipping frontend build.") | |
| return False | |
| print(f"[*] Building frontend at {frontend_dir}...") | |
| try: | |
| # Install dependencies if needed | |
| node_modules = frontend_dir / "node_modules" | |
| if not node_modules.exists(): | |
| print("[*] Installing npm dependencies...") | |
| result = subprocess.run( | |
| ["npm", "install", "--legacy-peer-deps"], | |
| cwd=str(frontend_dir), | |
| capture_output=True, | |
| text=True, | |
| timeout=600, # 10 minute timeout | |
| ) | |
| if result.returncode != 0: | |
| print(f"[ERROR] npm install failed:") | |
| print(result.stderr) | |
| return False | |
| print("[OK] Dependencies installed") | |
| else: | |
| print("[OK] node_modules already exists") | |
| # Build the frontend | |
| print("[*] Running npm run build...") | |
| result = subprocess.run( | |
| ["npm", "run", "build"], | |
| cwd=str(frontend_dir), | |
| capture_output=True, | |
| text=True, | |
| timeout=600, # 10 minute timeout | |
| ) | |
| if result.returncode != 0: | |
| print(f"[ERROR] npm build failed:") | |
| print(result.stderr) | |
| return False | |
| print("[OK] Frontend built successfully") | |
| if result.stdout: | |
| print(result.stdout[-500:] if len(result.stdout) > 500 else result.stdout) | |
| # Verify dist was created | |
| dist_dir = frontend_dir / "dist" | |
| if not dist_dir.exists(): | |
| print("[ERROR] dist directory was not created after build") | |
| return False | |
| index_html = dist_dir / "index.html" | |
| if not index_html.exists(): | |
| print("[ERROR] index.html not found in dist directory") | |
| return False | |
| print(f"[OK] Frontend ready at {dist_dir}") | |
| return True | |
| except FileNotFoundError as e: | |
| print(f"[ERROR] npm not found: {e}. Make sure Node.js is installed.") | |
| return False | |
| except subprocess.TimeoutExpired: | |
| print("[ERROR] Frontend build timed out (10 minutes exceeded)") | |
| return False | |
| except Exception as e: | |
| print(f"[ERROR] Unexpected error building frontend: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| return False | |
| def ensure_frontend_ready(): | |
| """ | |
| Ensure the frontend is ready. Tries multiple strategies: | |
| 1. Check if already built | |
| 2. Copy from pre-built source | |
| 3. Build from source | |
| Uses fallback UI if all fail. | |
| """ | |
| print("[*] Checking frontend status...") | |
| if is_frontend_built(): | |
| print("[OK] Frontend is already built") | |
| return True | |
| print("[*] Frontend not found, trying to copy pre-built version...") | |
| if copy_dist_from_source(): | |
| print("[OK] Pre-built frontend copied successfully") | |
| return True | |
| print("[*] Attempting to build frontend from source...") | |
| if build_frontend(): | |
| print("[OK] Frontend build successful!") | |
| return True | |
| print("[WARNING] Frontend not available, using fallback UI") | |
| return False | |
| if __name__ == "__main__": | |
| # Test the builder | |
| if ensure_frontend_ready(): | |
| print("[OK] Frontend is ready!") | |
| sys.exit(0) | |
| else: | |
| print("[WARNING] Frontend build/copy encountered issues") | |
| sys.exit(1) | |