| """Deploy this app to Hugging Face Spaces (Docker SDK) and print the live URL. |
| |
| Requires: |
| * `pip install -r requirements-deploy.txt` (huggingface_hub — deploy-only dep) |
| * a Hugging Face token (cached via `huggingface-cli login`, or HF_TOKEN env) |
| * OPENROUTER_API_KEY (read from env or .streamlit/secrets.toml) — set as a |
| Space secret so the deployed app can call OpenRouter. |
| |
| python scripts/deploy_hf.py [--space-name text-to-sql-guardrails] |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import sys |
| import tempfile |
| from pathlib import Path |
|
|
| ROOT = Path(__file__).resolve().parents[1] |
|
|
| SPACE_README = """--- |
| title: Text-to-SQL with Guardrails |
| emoji: 🛡️ |
| colorFrom: blue |
| colorTo: indigo |
| sdk: docker |
| app_port: 7860 |
| pinned: false |
| short_description: NL to SQL with read-only guardrails + execution eval |
| --- |
| |
| # Text-to-SQL with Guardrails |
| |
| Ask a question in plain English; an LLM writes SQL, a guardrail layer validates it |
| (read-only, single-statement, table-allowlist, no file functions) using `sqlglot`, |
| and only then does it run on a real DuckDB. Includes an execution-accuracy eval. |
| |
| Source: https://github.com/saiteja007-mv/text-to-sql-guardrails |
| """ |
|
|
| IGNORE = [ |
| ".venv/*", |
| ".git/*", |
| ".github/*", |
| "__pycache__/*", |
| "*/__pycache__/*", |
| "*.pyc", |
| ".streamlit/secrets.toml", |
| ".streamlit/secrets.toml.example", |
| ".env", |
| "tests/*", |
| "docs/*", |
| ".boot.log", |
| "README.md", |
| ] |
|
|
|
|
| def _load_key() -> str | None: |
| key = os.environ.get("OPENROUTER_API_KEY") |
| if key: |
| return key.strip() |
| sec = ROOT / ".streamlit" / "secrets.toml" |
| if sec.exists(): |
| import tomllib |
|
|
| return str(tomllib.load(sec.open("rb")).get("OPENROUTER_API_KEY", "")).strip() or None |
| return None |
|
|
|
|
| def main() -> int: |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--space-name", default="text-to-sql-guardrails") |
| args = ap.parse_args() |
|
|
| from huggingface_hub import HfApi |
|
|
| api = HfApi() |
| user = api.whoami()["name"] |
| repo_id = f"{user}/{args.space_name}" |
| print(f"[deploy] HF user: {user} -> space {repo_id}") |
|
|
| api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="docker", exist_ok=True) |
|
|
| key = _load_key() |
| if key: |
| api.add_space_secret(repo_id=repo_id, key="OPENROUTER_API_KEY", value=key) |
| print("[deploy] set Space secret OPENROUTER_API_KEY") |
| else: |
| print("[deploy] WARNING: no OPENROUTER_API_KEY found — set it in Space settings.") |
|
|
| with tempfile.TemporaryDirectory() as td: |
| readme = Path(td) / "README.md" |
| readme.write_text(SPACE_README, encoding="utf-8") |
| api.upload_file( |
| path_or_fileobj=str(readme), |
| path_in_repo="README.md", |
| repo_id=repo_id, |
| repo_type="space", |
| ) |
|
|
| api.upload_folder( |
| folder_path=str(ROOT), |
| repo_id=repo_id, |
| repo_type="space", |
| ignore_patterns=IGNORE, |
| commit_message="Deploy text-to-sql-guardrails", |
| ) |
|
|
| url = f"https://huggingface.co/spaces/{repo_id}" |
| print(f"\n[deploy] DONE. Live (build takes ~1-2 min):\n {url}") |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|