NeonClary's picture
Restore full pre-tutorial Space snapshot (b0ebe11)
8eac37e verified
|
Raw
History Blame Contribute Delete
10.6 kB
---
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.