Spaces:
Configuration error
Configuration error
| #!/usr/bin/env python3 | |
| """Deploy NBA Quant AI to lbjlincoln/nomos-nba-quant HF Space. | |
| Uploads all files from hf-space/ dir, configures secrets, restarts. | |
| Usage: | |
| source .env.local | |
| python3 hf-space/deploy.py | |
| """ | |
| import os, sys | |
| from pathlib import Path | |
| from huggingface_hub import HfApi, CommitOperationAdd | |
| SPACE_ID = "lbjlincoln/nomos-nba-quant" | |
| HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HF_TOKEN_2") | |
| LOCAL_DIR = Path(__file__).parent | |
| SECRETS = { | |
| # ββ Core DB ββ | |
| "DATABASE_URL": os.environ.get("DATABASE_URL", ""), | |
| "SUPABASE_URL": os.environ.get("SUPABASE_URL", ""), | |
| "SUPABASE_API_KEY": os.environ.get("SUPABASE_API_KEY", ""), | |
| "SUPABASE_PASSWORD": os.environ.get("SUPABASE_PASSWORD", ""), | |
| "SUPABASE_URL_2": os.environ.get("SUPABASE_URL_2", ""), | |
| "SUPABASE_ANON_KEY_2": os.environ.get("SUPABASE_ANON_KEY_2", ""), | |
| "SUPABASE_PASSWORD_2": os.environ.get("SUPABASE_PASSWORD_2", ""), | |
| "SUPABASE_POOLER_2": os.environ.get("SUPABASE_POOLER_2", ""), | |
| # ββ Neo4j ββ | |
| "NEO4J_URI": os.environ.get("NEO4J_URI", ""), | |
| "NEO4J_USER": os.environ.get("NEO4J_USER", ""), | |
| "NEO4J_PASSWORD": os.environ.get("NEO4J_PASSWORD", ""), | |
| "NEO4J_URI_2": os.environ.get("NEO4J_URI_2", ""), | |
| "NEO4J_USER_2": os.environ.get("NEO4J_USER_2", ""), | |
| "NEO4J_PASSWORD_2": os.environ.get("NEO4J_PASSWORD_2", ""), | |
| # ββ Pinecone ββ | |
| "PINECONE_API_KEY": os.environ.get("PINECONE_API_KEY", ""), | |
| "PINECONE_API_KEY_2": os.environ.get("PINECONE_API_KEY_2", ""), | |
| "PINECONE_HOST": os.environ.get("PINECONE_HOST", ""), | |
| # ββ Sports / Odds ββ | |
| "ODDS_API_KEY": os.environ.get("ODDS_API_KEY", ""), | |
| # ββ LLM Keys (for CrewAI, agents, research) ββ | |
| "OPENROUTER_API_KEY": os.environ.get("OPENROUTER_API_KEY", ""), | |
| "OPENROUTER_KEY_QUANTITATIVE": os.environ.get("OPENROUTER_KEY_QUANTITATIVE", ""), | |
| "OPENROUTER_KEY_SPARE": os.environ.get("OPENROUTER_KEY_SPARE", ""), | |
| "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY", ""), | |
| "GROQ_API_KEY": os.environ.get("GROQ_API_KEY", ""), | |
| "GROQ_API_KEY_2": os.environ.get("GROQ_API_KEY_2", ""), | |
| "GROQ_API_KEY_3": os.environ.get("GROQ_API_KEY_3", ""), | |
| "XAI_API_KEY": os.environ.get("XAI_API_KEY", ""), | |
| "COHERE_API_KEY": os.environ.get("COHERE_API_KEY", ""), | |
| "KIMI_API_KEY": os.environ.get("KIMI_API_KEY", ""), | |
| # LiteLLM removed β direct provider calls only | |
| # ββ Telegram ββ | |
| "TELEGRAM_BOT_TOKEN": os.environ.get("TELEGRAM_BOT_TOKEN", ""), | |
| "ADMIN_TELEGRAM_ID": os.environ.get("ADMIN_TELEGRAM_ID", ""), | |
| "TELEGRAM_CHANNEL_ID": os.environ.get("TELEGRAM_CHANNEL_ID", ""), | |
| # ββ Search / Research ββ | |
| "BRAVE_API_KEY": os.environ.get("BRAVE_API_KEY", ""), | |
| "TAVILY_API_KEY": os.environ.get("TAVILY_API_KEY", ""), | |
| "EXA_API_KEY": os.environ.get("EXA_API_KEY", ""), | |
| "JINA_API_KEY": os.environ.get("JINA_API_KEY", ""), | |
| # ββ GitHub ββ | |
| "GH_TOKEN": os.environ.get("GH_TOKEN", ""), | |
| "GITHUB_TOKEN": os.environ.get("GITHUB_TOKEN", ""), | |
| # ββ HuggingFace ββ | |
| "HF_TOKEN": os.environ.get("HF_TOKEN", ""), | |
| "HF_TOKEN_2": os.environ.get("HF_TOKEN_2", ""), | |
| "HF_TOKEN_3": os.environ.get("HF_TOKEN_3", ""), | |
| # ββ Infrastructure ββ | |
| "VM_CALLBACK_URL": os.environ.get("VM_CALLBACK_URL", "http://34.136.180.66:8080"), | |
| "VM_HOST": os.environ.get("VM_HOST", ""), | |
| "REDIS_URL": os.environ.get("REDIS_URL", ""), | |
| "REMOTE_CONTROL_KEY": os.environ.get("REMOTE_CONTROL_KEY", ""), | |
| # ββ Google ββ | |
| "GOOGLE_API_KEY": os.environ.get("GOOGLE_API_KEY", ""), | |
| } | |
| def main(): | |
| if not HF_TOKEN: | |
| print("ERROR: HF_TOKEN not set. Run: source .env.local") | |
| sys.exit(1) | |
| # ββ Engine parity check ββ | |
| root_engine = (LOCAL_DIR.parent / "features" / "engine.py").read_bytes() | |
| hf_engine = (LOCAL_DIR / "features" / "engine.py").read_bytes() | |
| if root_engine != hf_engine: | |
| print("ERROR: features/engine.py and hf-space/features/engine.py have diverged!") | |
| print("Fix: cp hf-space/features/engine.py features/engine.py") | |
| print("Or: cp features/engine.py hf-space/features/engine.py") | |
| sys.exit(1) | |
| print("Engine parity check: OK") | |
| api = HfApi(token=HF_TOKEN) | |
| print(f"Deploying NBA Quant AI to {SPACE_ID}...") | |
| operations = [] | |
| skip = {"__pycache__", ".pyc", "node_modules", ".git", "deploy.py"} | |
| for fp in LOCAL_DIR.rglob("*"): | |
| if fp.is_dir(): | |
| continue | |
| if any(s in str(fp) for s in skip): | |
| continue | |
| rel = fp.relative_to(LOCAL_DIR) | |
| print(f" + {rel}") | |
| operations.append(CommitOperationAdd(path_in_repo=str(rel), path_or_fileobj=str(fp))) | |
| if not operations: | |
| print("ERROR: No files found") | |
| sys.exit(1) | |
| print(f"\nUploading {len(operations)} files...") | |
| try: | |
| api.create_commit( | |
| repo_id=SPACE_ID, repo_type="space", operations=operations, | |
| commit_message="feat: real box scores + skip_placeholder + checkpoint/rollback + Brier-dominant fitness", | |
| ) | |
| print("Upload OK!") | |
| except Exception as e: | |
| if "404" in str(e) or "not found" in str(e).lower(): | |
| print(f"Space not found, creating {SPACE_ID}...") | |
| api.create_repo(repo_id=SPACE_ID, repo_type="space", space_sdk="docker", | |
| space_hardware="cpu-basic", private=False) | |
| api.create_commit( | |
| repo_id=SPACE_ID, repo_type="space", operations=operations, | |
| commit_message="feat: real box scores + skip_placeholder + checkpoint/rollback + Brier-dominant fitness", | |
| ) | |
| else: | |
| raise | |
| print("\nConfiguring secrets...") | |
| for key, value in SECRETS.items(): | |
| if value: | |
| try: | |
| api.add_space_secret(SPACE_ID, key, value) | |
| print(f" Set {key}") | |
| except Exception as e: | |
| print(f" WARN: {key}: {e}") | |
| else: | |
| print(f" SKIP {key} (empty)") | |
| print("\nRestarting Space...") | |
| try: | |
| api.restart_space(SPACE_ID) | |
| except Exception as e: | |
| print(f" Restart: {e}") | |
| print(f"\nDone! Space: https://lbjlincoln-nomos-nba-quant.hf.space") | |
| print(f"Monitor: https://huggingface.co/spaces/{SPACE_ID}") | |
| if __name__ == "__main__": | |
| main() | |