NeonClary's picture
Restore full pre-tutorial Space snapshot (b0ebe11)
8eac37e verified
|
Raw
History Blame Contribute Delete
10.6 kB
metadata
title: AI Expert Advisory Builder's Workshop
emoji: 🎓
colorFrom: indigo
colorTo: blue
sdk: docker
pinned: false
app_port: 7860

CU-Student-AIProject-Helper

Recruitment and tooling site for non-technical CU students joining a Collaborative Conversational AI (CCAI) advisory-panel project: long-form landing page, optional accounts, persona/advisor forms and interactive GPT chats (with mic input and read-aloud), and submission hooks for Google Sheets / email.

Stack

  • Frontend: React (Vite + TypeScript), React Router
  • Backend: FastAPI, SQLite (via aiosqlite) on a Hugging Face Storage Bucket mount, OpenAI API (OPENAI_API_KEY, default model gpt-5.4)
  • Deploy: Root Dockerfile builds a single image (API + static SPA) for Hugging Face Spaces or similar

Server-side user data (SQLite on a Hugging Face Storage Bucket)

All durable user data lives on a single Hugging Face Storage Bucket mounted into the Space at ${DATA_DIR} (default /data). One bucket holds both the SQLite database and the raw bytes of any uploaded RAG documents, so a single hf buckets sync salvages the entire user state — no MongoDB, no Atlas, no third-party service.

${DATA_DIR}/
├── cu_student_helper.db        # SQLite: users, profiles, artifacts, document metadata
└── user_uploads/
    └── <oid_hex>               # one file per upload, named by the doc's ObjectId

When users sign in, the browser merges local workshop data with the server and keeps it in sync.

Store (SQLite table) Purpose
users Account records (email, bcrypt password hash, name). UNIQUE constraint on email.
user_profiles persona_draft / advisor_draft (in-progress wizards) and creations (saved Persona + Advisor Panel records, same shape as local creations_v1).
generated_artifacts Generated prompts or panel packs; each row references a workshop entity via source_type (persona | advisor_panel) and source_client_id (the UUID used in the UI).
user_documents Metadata for uploaded files (filename, size_bytes, owner_type + owner_client_id, plus the gridfs_id ObjectId that names the bytes file under user_uploads/).

Internally backend/app/db.py exposes a tiny Motor-shaped facade (find_one, find().sort(), insert_one, update_one, delete_one, delete_many, find_one_and_delete, an aggregation pipeline, plus an open_download_stream / upload_from_stream "bucket" for binaries) so the routers and services were left almost untouched.

Relevant HTTP routes: GET/PUT /api/users/me/profile, GET/POST/DELETE /api/users/me/generated-artifacts, GET/POST/DELETE /api/users/me/documents (+ GET .../documents/{id}/file to download).

Backing up / salvaging user data before any redeploy

Pushing a new Docker image rebuilds the container but leaves the Storage Bucket untouched, so existing accounts and uploads survive automatically. Even so, take a snapshot before any risky change with the Hugging Face CLI:

pip install --upgrade huggingface_hub
hf auth login                                     # or: export HF_TOKEN=hf_xxx
hf buckets sync hf://buckets/neongeckocom/AdvisoryBuilderWorkshop-storage ./backup

You now have ./backup/cu_student_helper.db (open with sqlite3 ./backup/cu_student_helper.db ".tables" to inspect users, profiles, artifacts, document metadata) plus ./backup/user_uploads/ containing the raw bytes of every uploaded file. To restore the same state into a new (or the same) bucket:

hf buckets sync ./backup hf://buckets/neongeckocom/AdvisoryBuilderWorkshop-storage

(The hf command — formerly huggingface-cli — ships with huggingface_hub. See the Storage Buckets docs for credentials and per-bucket ACL options.)

Hugging Face Spaces

A private HF Storage Bucket — neongeckocom/AdvisoryBuilderWorkshop-storage — is the durable home for users, profiles, generated artifacts, document metadata, and the bytes of every uploaded RAG file. The Space mounts it at /data (a config knob, not a hard-coded path — see step 3).

  1. Create the bucket (one-time, only if it doesn't already exist):

    hf buckets create neongeckocom/AdvisoryBuilderWorkshop-storage --private
    
  2. Attach the bucket to the Space at /data. Either via the Hub UI (Space → Settings → Storage Buckets → Add bucket, mount path /data, read+write) or with the CLI:

    hf spaces volumes add neongeckocom/AdvisoryBuilderWorkshop \
        --bucket hf://buckets/neongeckocom/AdvisoryBuilderWorkshop-storage:/data
    
  3. Match the mount path with DATA_DIR. The Docker image bakes DATA_DIR=/data so it works out of the box. If you choose a different mount path, override the env var in Settings → Repository secrets (or Variables) so DATA_DIR matches.

  4. Set the rest of the secrets in Settings → Repository secrets at minimum:

    • OPENAI_API_KEY — required for chat, wizards, and transcription
    • JWT_SECRET_KEY — any long random string (signs auth tokens)
    • DEFAULT_LLM_MODEL — optional; defaults to gpt-5.4
    • DATA_DIR — only if your bucket mount path is not /data
  5. Optional secrets:

    • GOOGLE_SERVICE_ACCOUNT_JSON, GOOGLE_SPREADSHEET_ID — Google Sheets submission pipeline
    • ADMIN_NOTIFY_EMAIL, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM — email on submit
  6. Push the new image (or wait for the GitHub-connected Space to rebuild). On startup the app creates cu_student_helper.db and user_uploads/ on the bucket if they don't yet exist.

Secrets are injected as environment variables at runtime; do not commit secrets to git. Locally, use ~/.secrets/shared.env (see below). CORS_ORIGINS=* is baked into the Docker image for HF.

No Storage Bucket attached? The first request that touches the database returns HTTP 503 with the message "Account sign-in is unavailable right now (database). … make sure the Hugging Face Storage Bucket is attached and DATA_DIR points at the mount path (default /data)." Health check /api/health continues to return 200 so the Space stays "Running".

Create the Space from the Hub CLI (optional)

pip install huggingface_hub
huggingface-cli repo create neongeckocom/AdvisoryBuilderWorkshop --type space --space-sdk docker --public --exist-ok

Then add the Space as a git remote and push, or use Settings → Connect to GitHub.

Local development (hot reload)

Persistence is just a SQLite file plus a directory of uploads under ./data/, so no extra database service is required.

Option A — Docker Compose (API reload + Vite HMR)

From the repo root, with ~/.secrets/shared.env configured (at least OPENAI_API_KEY, JWT_SECRET_KEY):

.\scripts\dev-docker.ps1

Or without the script (set ENV_FILE_PATH to your shared env file first):

docker compose up --build
Service URL Notes
UI (Vite) http://localhost:5173 Hot reload (HMR); proxies /api → backend
API http://localhost:8000 uvicorn --reload; OpenAPI at http://localhost:8000/docs
Persistence ./data/ (mounted to /app/data) cu_student_helper.db + user_uploads/

Compose sets SERVE_FRONTEND_STATIC=false so FastAPI does not serve frontend/dist — use the Vite dev server. The frontend service uses polling so file watches work on Docker Desktop (Windows/macOS).

PowerShell helper:

.\scripts\dev-docker.ps1

Option B — Processes on your machine (no containers)

  1. Backend.\scripts\start-local-backend.ps1 loads ~/.secrets/shared.env and starts uvicorn with reload. Or manually:

    $env:ENV_FILE_PATH = "$HOME\.secrets\shared.env"
    cd backend
    python -m venv .venv
    .venv\Scripts\activate
    pip install -r requirements.txt
    uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
    

    On first startup the app creates ./data/cu_student_helper.db and ./data/user_uploads/ next to the project (override with DATA_DIR=… in shared.env).

  2. Frontend — second terminal:

    cd frontend
    npm install
    npm run dev
    

    Open http://localhost:5173 — Vite proxies /api to http://127.0.0.1:8000.

Without mounting static, the API exposes OpenAPI at /docs on port 8000.

Configuration

Secrets live in ~/.secrets/shared.env (Windows: C:\Users\dream\.secrets\shared.env), shared across your projects. See .env.example for the variable list.

Variable Purpose
OPENAI_API_KEY Required for chat, wizards, and transcription
DEFAULT_LLM_MODEL Default LLM everywhere (default gpt-5.4)
TRANSCRIBE_MODEL OpenAI speech-to-text model (default whisper-1)
DATA_DIR Persistence root: cu_student_helper.db + user_uploads/<oid> live here. Default ./data locally; set to /data (or wherever the HF Storage Bucket is mounted) on Spaces
JWT_SECRET_KEY Sign auth tokens (use a long random value in production)
CORS_ORIGINS Frontend origins (dev: http://localhost:5173)
SERVE_FRONTEND_STATIC true (default): serve frontend/dist from FastAPI. false: use Vite dev server only (hot reload).
GOOGLE_* / SMTP Optional submission pipeline (see app/routers/submit.py)

Speech: Mic input is sent to POST /api/transcribe (OpenAI Whisper). "Read aloud" uses the browser Web Speech API (no extra key).

Production image (HF / single container)

Build the root Dockerfile (includes npm run build + FastAPI serving static UI on port 7860):

docker build -t cu-student-helper .
docker run --rm -p 7860:7860 --env-file "$HOME/.secrets/shared.env" cu-student-helper

Open http://localhost:7860 — API docs are disabled when static files are mounted (see app/main.py).

GitHub

git remote add origin https://github.com/YOUR_ORG/CU-Student-AIProject-Helper.git
git add -A && git commit -m "Initial import"
git push -u origin main

License

Project scaffold for educational use; adapt as needed for your program.