| ---
|
| 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`](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:
|
|
|
| ```bash
|
| 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:
|
|
|
| ```bash
|
| 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):
|
|
|
| ```bash
|
| 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:
|
|
|
| ```bash
|
| 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)
|
|
|
| ```bash
|
| 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`):
|
|
|
| ```powershell
|
| .\scripts\dev-docker.ps1
|
| ```
|
|
|
| Or without the script (set `ENV_FILE_PATH` to your shared env file first):
|
|
|
| ```bash
|
| 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:
|
|
|
| ```powershell
|
| .\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:
|
|
|
| ```powershell
|
| $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:
|
|
|
| ```bash
|
| 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](.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`](Dockerfile) (includes `npm run build` + FastAPI serving static UI on port **7860**):
|
|
|
| ```bash
|
| 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
|
|
|
| ```bash
|
| 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.
|
|
|