File size: 3,485 Bytes
8db761b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3cdbd24
 
8db761b
 
3cdbd24
8db761b
3cdbd24
8db761b
 
 
 
 
 
981881b
8db761b
981881b
 
8db761b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
981881b
 
8db761b
 
981881b
 
 
 
 
 
 
 
8db761b
 
3cdbd24
981881b
 
8db761b
 
 
 
 
 
 
 
 
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
"""Docker-Space entry point: pull the dataset, build the app, run uvicorn.

Secrets/config (Space Secrets / env):
  STUDYHUB_DATASET        private dataset (index + originals + app/ + uploads/)
  HF_TOKEN                WRITE-scoped token (pulls the dataset AND persists users/history)
  GROQ_API_KEY (+ CEREBRAS_API_KEY)   generation
  APP_USERS               seed admin(s): "owner:admin:salt$hash"
  STUDYHUB_INVITE_CODE    the class code classmates use to register
  STUDYHUB_EMBEDDER       bge-m3
  STUDYHUB_SESSION_SECRET random string for signing session cookies
"""
import os
from pathlib import Path


def _maybe_pull_dataset() -> None:
    repo = os.getenv("STUDYHUB_DATASET")
    if not repo:
        return
    from huggingface_hub import snapshot_download

    from ..accounts.uploads import merge_uploads_into_materials
    local = snapshot_download(repo_id=repo, repo_type="dataset", token=os.getenv("HF_TOKEN"),
                              local_dir=os.getenv("STUDYHUB_DATA_DIR", "data"),
                              allow_patterns=["index/**", "originals/**", "uploads/**"])
    base = Path(local)
    merge_uploads_into_materials(base)   # so /api/download can serve uploaded files too
    os.environ.setdefault("STUDYHUB_INDEX_DIR", str(base / "index"))
    os.environ.setdefault("STUDYHUB_MATERIALS_DIR", str(base / "originals"))


def build_app():
    _maybe_pull_dataset()
    from studyhub.accounts.decks import DeckStore
    from studyhub.accounts.history import HistoryStore
    from studyhub.accounts.invites import InviteStore
    from studyhub.accounts.notes import NotesStore
    from studyhub.accounts.store import HfDatasetStore, LocalStore
    from studyhub.accounts.users import UserStore
    from studyhub.app.auth import parse_users
    from studyhub.app.config import load_settings
    from studyhub.app.factory import build_service
    from studyhub.web.api import create_app

    settings = load_settings()
    service = build_service(settings)

    repo, token = os.getenv("STUDYHUB_DATASET"), os.getenv("HF_TOKEN")
    store = HfDatasetStore(repo, token) if repo else LocalStore(os.getenv("STUDYHUB_APPDATA", "appdata"))

    seed = {u: {"role": usr.role, "salt": usr.salt_hex, "hash": usr.hash_hex}
            for u, usr in parse_users(settings.app_users_raw).items()}
    users = UserStore(store, seed_admins=seed, invite_code=os.getenv("STUDYHUB_INVITE_CODE"),
                      invites=InviteStore(store))
    history = HistoryStore(store)

    from studyhub.web.ingest_worker import IngestWorker
    worker = IngestWorker(service, index_dir=os.environ.get("STUDYHUB_INDEX_DIR", "index"),
                          materials_dir=os.environ.get("STUDYHUB_MATERIALS_DIR", "originals"),
                          dataset_repo=repo or "", hf_token=token or "")
    worker.start()
    # Any upload that arrived while the Space was down gets ingested on boot.
    worker.boot_reconcile(store.list_files("uploads/"))

    secret = os.getenv("STUDYHUB_SESSION_SECRET") or token or "dev-secret-change-me"
    static = str(Path(__file__).parent / "static")
    return create_app(service, users, history, session_secret=secret, static_dir=static,
                      dataset_name=repo or "", decks=DeckStore(store), notes=NotesStore(store),
                      ingest=worker)


def main() -> None:
    import uvicorn
    uvicorn.run(build_app(), host="0.0.0.0", port=int(os.getenv("PORT", "7860")))


if __name__ == "__main__":
    main()