Spaces:
Running
Running
File size: 5,977 Bytes
7a7d4fa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | #!/usr/bin/env bash
# Deploy Learning Focused MCP as a Hugging Face Docker Space + private corpus dataset.
# Daily ingest is an HF Job (08:00 Pacific / 15:00 UTC during PDT), not Cloud Run.
# Does not deploy to GCP project inferpoker. Does not print secret values.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT}"
HF_USER="${HF_USER:-LeoWalker}"
SPACE_ID="${HF_SPACE_ID:-${HF_USER}/learningfocused-mcp}"
CORPUS_REPO="${HF_CORPUS_REPO:-${HF_USER}/learningfocused-corpus}"
SPACE_HOST="$(echo "${SPACE_ID}" | tr '[:upper:]' '[:lower:]' | tr '/' '-').hf.space"
SPACE_URL="https://${SPACE_HOST}"
SYNC_CORPUS="${SYNC_CORPUS:-1}"
SCHEDULE_JOB="${SCHEDULE_JOB:-1}"
# 08:00 America/Los_Angeles during PDT. HF Jobs cron is UTC (no TZ flag).
JOB_CRON="${HF_INGEST_CRON:-0 15 * * *}"
JOB_TIMEOUT="${HF_INGEST_TIMEOUT:-4h}"
JOB_FLAVOR="${HF_INGEST_FLAVOR:-cpu-basic}"
SECRETS_FILE="$(mktemp "${TMPDIR:-/tmp}/lf-hf-secrets.XXXXXX")"
trap 'rm -f "${SECRETS_FILE}"' EXIT
chmod 600 "${SECRETS_FILE}"
if ! hf auth whoami >/dev/null 2>&1; then
echo "Hugging Face CLI is not logged in. Run: hf auth login" >&2
echo "Need a write token for ${HF_USER} (Spaces + datasets + Jobs)." >&2
exit 2
fi
echo ">> Hugging Face account OK"
echo ">> Space: ${SPACE_ID}"
echo ">> Corpus: ${CORPUS_REPO}"
echo ">> URL: ${SPACE_URL}/mcp"
echo ">> Creating private corpus dataset ${CORPUS_REPO}"
hf repos create "${CORPUS_REPO}" --type dataset --private --exist-ok
echo ">> Creating Docker Space ${SPACE_ID} (cpu-basic, sleep enabled)"
hf repos create "${SPACE_ID}" \
--type space \
--space-sdk docker \
--public \
--exist-ok \
--flavor cpu-basic
if [[ "${SYNC_CORPUS}" == "1" ]]; then
echo ">> Syncing chroma + derived artifacts to ${CORPUS_REPO} (not podcast_downloads)"
PREFIXES=(
chroma_db
transcripts
segmented_transcripts
combined_summaries
substack_articles
article_summaries
youtube_videos
youtube_summaries
)
for prefix in "${PREFIXES[@]}"; do
dest="${ROOT}/${prefix}"
mkdir -p "${dest}"
if [[ "${prefix}" == "chroma_db" && -f "${dest}/chroma.sqlite3" ]]; then
echo " local ${prefix}/ (chroma.sqlite3 present)"
continue
fi
if [[ "${prefix}" != "chroma_db" && -n "$(ls -A "${dest}" 2>/dev/null || true)" ]]; then
echo " local ${prefix}/"
continue
fi
echo " gcs gs://inferpoker-learningfocused/${prefix}/ (one-time copy off InferPoker)"
gcloud storage rsync "gs://inferpoker-learningfocused/${prefix}" "${dest}" \
--recursive --checksums-only --project=inferpoker
done
if [[ ! -f "${ROOT}/chroma_db/chroma.sqlite3" ]]; then
echo "chroma.sqlite3 missing after sync" >&2
exit 1
fi
echo ">> Uploading corpus snapshot to Hugging Face"
HF_CORPUS_REPO="${CORPUS_REPO}" \
CHROMA_DIR="${ROOT}/chroma_db" \
uv run python -c "from src.hf_corpus import upload_artifacts; upload_artifacts()"
fi
echo ">> Uploading Space sources"
hf upload "${SPACE_ID}" "${ROOT}/spaces/README.md" README.md --repo-type space --commit-message "Space README"
hf upload "${SPACE_ID}" "${ROOT}/Dockerfile" Dockerfile --repo-type space
hf upload "${SPACE_ID}" "${ROOT}/pyproject.toml" pyproject.toml --repo-type space
hf upload "${SPACE_ID}" "${ROOT}/uv.lock" uv.lock --repo-type space
hf upload "${SPACE_ID}" "${ROOT}/.dockerignore" .dockerignore --repo-type space
hf upload "${SPACE_ID}" "${ROOT}/src" src --repo-type space --commit-message "MCP source"
hf upload "${SPACE_ID}" "${ROOT}/scripts" scripts --repo-type space --commit-message "entrypoints"
echo ">> Setting Space variables (not secrets)"
SPACE_ID="${SPACE_ID}" CORPUS_REPO="${CORPUS_REPO}" uv run python - <<'PY'
import os
from huggingface_hub import HfApi
api = HfApi()
space = os.environ["SPACE_ID"]
corpus = os.environ["CORPUS_REPO"]
variables = {
"MCP_HOST": "0.0.0.0",
"HYDRATE_CHROMA": "1",
"HF_CORPUS_REPO": corpus,
"HF_SPACE_ID": space,
"CHROMA_DIR": "/home/user/data/chroma_db",
"MCP_RATE_LIMIT_PER_MINUTE": "60",
"MCP_SNIPPET_LIMIT": "32768",
"PORT": "7860",
}
for key, value in variables.items():
api.add_space_variable(space, key, value)
print("space variables set:", ",".join(variables))
PY
echo ">> Copying API keys into Space secrets (values not printed)"
uv run python "${ROOT}/scripts/set_hf_secrets.py" --space "${SPACE_ID}" --write-job-secrets-file "${SECRETS_FILE}"
if [[ "${SCHEDULE_JOB}" == "1" ]]; then
echo ">> Removing previous learningfocused ingest schedules if any"
uv run python - <<'PY'
from huggingface_hub import delete_scheduled_job, list_scheduled_jobs
try:
jobs = list(list_scheduled_jobs())
except Exception as exc: # noqa: BLE001
print(f"WARNING: could not list scheduled jobs: {exc}")
jobs = []
for job in jobs:
labels = getattr(job, "labels", None) or {}
if labels.get("app") == "learningfocused" and labels.get("role") == "daily-ingest":
print(f"deleting scheduled job {job.id}")
delete_scheduled_job(job.id)
PY
echo ">> Scheduling daily ingest job ${JOB_CRON} UTC flavor=${JOB_FLAVOR}"
hf jobs scheduled run "${JOB_CRON}" \
"hf.co/spaces/${SPACE_ID}" \
python -m src.pipeline.daily_ingest \
--flavor "${JOB_FLAVOR}" \
--timeout "${JOB_TIMEOUT}" \
--env "HF_CORPUS_REPO=${CORPUS_REPO}" \
--env "HF_SPACE_ID=${SPACE_ID}" \
--env "HYDRATE_CHROMA=0" \
--env "CHROMA_DIR=/home/user/app/chroma_db" \
--env "DAILY_ART19_DOWNLOAD_LIMIT=5" \
--env "MCP_HOST=0.0.0.0" \
--secrets-file "${SECRETS_FILE}" \
--secrets HF_TOKEN \
--label app=learningfocused \
--label role=daily-ingest
fi
echo
echo "Space: https://huggingface.co/spaces/${SPACE_ID}"
echo "MCP: ${SPACE_URL}/mcp"
echo "Health: ${SPACE_URL}/health"
echo "Corpus: https://huggingface.co/datasets/${CORPUS_REPO}"
echo
echo "Wait for the Space to reach Running, then smoke /health and /mcp."
echo "Cursor mcp.json url: ${SPACE_URL}/mcp"
|