import os import socket import threading import gradio as gr # Force CPU by default (safe for Spaces and small hosts) os.environ.setdefault("CUDA_VISIBLE_DEVICES", "-1") from metrics import get_hf_bleurt, get_hf_rouge from ui.manual_tab import build_manual_tab from ui.csv_tab import build_csv_tab def _parse_auth_from_env(): """ Read APP_USERS env var if present. Format: APP_USERS="alice:StrongPass,bob:AnotherPass" Returns list[tuple[str, str]] or None. """ users_env = os.getenv("APP_USERS", "").strip() if not users_env: return None pairs = [] for part in users_env.split(","): if ":" in part: u, p = part.split(":", 1) u, p = u.strip(), p.strip() if u and p: pairs.append((u, p)) return pairs or None def _is_port_free(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(("0.0.0.0", port)) except OSError: return False return True def _find_free_port(preferred: int = 7860, scan_to: int = 7960) -> int: if _is_port_free(preferred): return preferred for p in range(preferred + 1, scan_to + 1): if _is_port_free(p): return p with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("0.0.0.0", 0)) return s.getsockname()[1] def _running_in_spaces() -> bool: # Common envs on HF Spaces return bool(os.getenv("SPACE_ID") or os.getenv("SPACE_REPO_URL") or os.getenv("HF_SPACE")) if __name__ == "__main__": # Preload heavy metrics in background so UI stays responsive try: threading.Thread(target=get_hf_bleurt, daemon=True).start() threading.Thread(target=get_hf_rouge, daemon=True).start() except Exception: pass # Build UI iface = gr.TabbedInterface( interface_list=[build_manual_tab(), build_csv_tab()], tab_names=["Manual Input", "CSV Upload"], ) # Optional basic auth via env (works both locally and on Spaces) auth_creds = _parse_auth_from_env() # Decide runtime mode in_spaces = _running_in_spaces() # Respect platform-provided port if any, else pick a free one env_port = os.getenv("PORT") or os.getenv("GRADIO_SERVER_PORT") port = int(env_port) if env_port else _find_free_port(7860, 7960) # Queue (handle different Gradio versions) try: iface.queue(concurrency_count=1, max_size=20) except TypeError: iface.queue() if in_spaces: # On Spaces: bind publicly & enable share (Spaces sandbox blocks localhost) iface.launch( server_name="0.0.0.0", server_port=port, show_error=True, auth=auth_creds, auth_message="Restricted • Enter company credentials", ssr_mode=False, # avoids svelte-i18n SSR issues on Spaces share=True, # Spaces needs a public URL ) else: # Local DEV: true private local URL (no gradio.live) iface.launch( server_name="127.0.0.1", # <-- use this URL in your browser server_port=port, show_error=True, auth=auth_creds, share=False, # <-- prevents expiring public link )