Spaces:
Running
Running
File size: 7,124 Bytes
907b200 | 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 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | # AILIXIR Hugging Face Spaces β Migration Flow
> **Base commit:** `fd94aa0` β pre-HF, Docker Compose with MySQL+Redis
---
## 1. Why Hugging Face Spaces?
| Need | Solution |
|------|----------|
| Free hosting for 5 microservices | 5 HF Spaces (2 accounts to keep more awake) |
| No MySQL/Redis infrastructure | SQLite for storage, `database` queue driver |
| Simple CI/CD | GitHub Actions auto-syncs code to each HF Space |
| Binary model files | Git LFS + Xet storage on HF |
---
## 2. Architecture Decisions
### SQLite over MySQL
- **Why:** HF Spaces provide ephemeral storage; SQLite has zero dependencies
- **Trade-off:** No concurrent write scaling; fine for single-user/small-team usage
- **DB Location:** `database/ailixir.sqlite` (mounted in the container)
- **Migrations:** Run automatically via `entrypoint.sh` on startup
### Two HF Accounts
- **`Ailixir-AI-Team`**: Backend API
- **`RottenShadow`**: Chemical RAG, Drug Repurposing
- **`shdwRow`** (secondary): ADMET, Generation
- **Why:** HF Spaces free tier puts unused spaces to sleep; spreading across 2 accounts keeps more services warm
### Queue: `sync` driver
- **Why:** No need for Redis/DB-backed queue worker; jobs run inline
- **Note:** For production scale, switch back to `database` and run a queue worker
### Binary Model Files: Git LFS + Xet
- **Why:** HF rejects large files in raw Git; Xet provides efficient binary storage
- **Tracked:** `*.ckpt`, `*.pt`, `*.pkl`
---
## 3. Per-Service Details
### Backend API (`Ailixir-AI-Team/ailixir-api`)
- **Tech:** Laravel 12 + PHP 8.3 CLI
- **Port:** 7860 (HF default)
- **Dockerfile:** Multi-stage (composer β vite β runtime)
- **Entrypoint:** Sources `RUN_MODE=hf` β starts supervisord (laravel serve + queue worker)
- **Fixes applied:**
- `config/view.php` β explicit compiled path (fixes 500 on `/`)
- `bootstrap/app.php` β `trustProxies(at: '*')` (fixes mixed-content CSS)
- `config/scribe.php` β `docs_url` moved to `/scribe-docs`
- `/docs` route β serves rendered `API.md` (curl examples, responses, grouped)
- `QUEUE_CONNECTION=sync` β no worker needed
### ADMET Prediction (`shdwRow/ailixir-admet`)
- **Tech:** FastAPI + PyTorch
- **Port:** 7860
- **Model:** `best_model.ckpt` (LFS-tracked)
- **Sync:** Xet-enabled checkout in workflow
### Chemical RAG (`RottenShadow/ailixir-chemical-rag`)
- **Tech:** FastAPI + ChromaDB
- **Port:** 7860
### Drug Repurposing (`RottenShadow/ailixir-drug-repurposing`)
- **Tech:** FastAPI + DeepPurpose
- **Port:** 7860
- **Special:** `Dockerfile.hf` with mock-mode fallback
### Generation (`shdwRow/ailixir-generation`)
- **Tech:** FastAPI + REINVENT4 + RDKit + DeepPurpose (optional)
- **Port:** 7860
- **Fixes applied:**
- **REINVENT4** β git cloned and `install.py cpu` in `Dockerfile.hf`
- **libXrender** β `libxrender1 libxext6` added (RDKit image rendering)
- **DeepPurpose** β optional install; integrated `/reinvent_predict` route on same port 7860
- **Graceful fallback** β if DeepPurpose unavailable, enrichment returns null affinity values
- **Enrichment** β catches connection errors, sets `pred_pAff_mean` to `None`
---
## 4. Sync Workflow
**File:** `.github/workflows/sync-hf-all.yml`
### Trigger
- Push to `main` branch
- Manual dispatch via GitHub UI
### Jobs (5 total)
| Job | HF Space | Account | Token | Notes |
|-----|----------|---------|-------|-------|
| `backend` | `ailixir-api` | Ailixir-AI-Team | `HF_TOKEN_AILIXIR` | Excludes `ai_apps/`, `database/ailixir.sqlite`, `storage/` |
| `drug-repurposing` | `ailixir-drug-repurposing` | RottenShadow | `HF_TOKEN` | Renames `Dockerfile.hf` β `Dockerfile` |
| `admet` | `ailixir-admet` | shdwRow | `HF_TOKEN_SHDWROW` | LFS + Xet for `.ckpt` |
| `chemical-rag` | `ailixir-chemical-rag` | RottenShadow | `HF_TOKEN` | |
| `generation` | `ailixir-generation` | shdwRow | `HF_TOKEN_SHDWROW` | LFS + Xet for `.ckpt`, `.pt`, `.pkl` |
### How each job works
1. Clone the HF Space repo (shallow, depth 1)
2. `rsync -av --delete` β sync files from main repo, delete files removed in source
3. Write `.env` with all required vars
4. Rename `Dockerfile.hf` β `Dockerfile` if present
5. Setup LFS/Xet for model files (ADMET, Generation)
6. Commit and push to HF Space
### Key exclusions (backend job)
```
--exclude=ai_apps
--exclude=.git
--exclude=.env
--exclude=.dockerignore
--exclude=public/imgs
--exclude=storage
--exclude=Diagrams
--exclude=database/ailixir.sqlite
```
### Required GitHub Secrets
| Secret | Used by |
|--------|---------|
| `HF_TOKEN` | drug-repurposing, chemical-rag |
| `HF_TOKEN_AILIXIR` | backend |
| `HF_TOKEN_SHDWROW` | admet, generation |
| `GOOGLE_CLIENT_ID` | backend .env |
| `GOOGLE_CLIENT_SECRET` | backend .env |
---
## 5. Environment Variables (`.env` written by sync)
```
RUN_MODE=hf
APP_KEY=base64:...
AI_INTEGRATION_ROUTES_ENABLED=true
APP_URL=https://Ailixir-AI-Team-ailixir-api.hf.space
APP_DEBUG=true
QUEUE_CONNECTION=sync
ADMET_AI_URL=https://shdwRow-ailixir-admet.hf.space
CHEMICAL_AI_URL=https://RottenShadow-ailixir-chemical-rag.hf.space
DRUG_REPURPOSING_URL=https://RottenShadow-ailixir-drug-repurposing.hf.space
GENERATION_SERVICE_URL=https://shdwRow-ailixir-generation.hf.space
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_REDIRECT_URI=https://Ailixir-AI-Team-ailixir-api.hf.space/api/user/auth/google
CLOUDINARY_CLOUD_NAME=dummy_cloud
CLOUDINARY_API_KEY=dummy_key
CLOUDINARY_API_SECRET=dummy_secret
```
---
## 6. Database Protection
The sync workflow **excludes** `database/ailixir.sqlite` from rsync and runs `git checkout HEAD -- database/ailixir.sqlite` before commit to prevent the live database from being pushed to git.
On HF Spaces, the database file persists across restarts (HF Spaces keep filesystem between restarts) but is **reset** when the Space rebuilds (code push).
---
## 7. Known Issues & Future Work
| Issue | Status | Resolution |
|-------|--------|------------|
| DeepPurpose on Python 3.10 | Unstable | Falls back to null affinity predictions; could switch to conda base image |
| Google OAuth secrets | Not set | Needs `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` added to GitHub secrets |
| Space sleep on free tier | Ongoing | Two-account setup mitigates; upgrade to HF Pro for always-on |
| Cloudinary credentials | Dummy values | Set real Cloudinary keys for image upload features |
| ADMET model files in Xet | Working | Large `.ckpt` files stored via HF Xet |
| Docs route `/docs` | Working | Serves rendered `API.md` with all examples and grouping |
---
## 8. Commit History (cleaned)
```
c49c94f Add HF Spaces sync workflow with two-account setup, DB protection
30f0a11 Generation HF: REINVENT4, DeepPurpose, libXrender, graceful fallback
a342297 AI microservices HF: ports 8000β7860, Dockerfiles, README_HF
af62962 Backend HF fixes: trustProxies, view cache path, docs route with API.md
fd80b74 Add HF Spaces environment config, deployment guide, and changelog
b4a5d77 Foundation: migrate MySQLβSQLite, add supervisor, port 7860, APP_KEY auto-gen
fd94aa0 Limit Docker Compose build parallelism in CI (original base)
```
|