# TrainHub API A self-hosted AI training platform backend, modelled on the Hugging Face Hub. **Stack:** FastAPI · SQLAlchemy (SQLite) · JWT auth · NumPy training worker · file storage · Hugging Face Hub bridge. | Capability | Status | |---|---| | User registration / login (JWT) | ✅ real, persistent | | Model / Dataset / Space repos (CRUD, likes) | ✅ real, persistent | | File upload / list / download (disk + DB) | ✅ real | | Training jobs (queue → run → metrics → complete) | ✅ real (NumPy backprop; swap in TRL for real models) | | Hugging Face Hub search + model import | ✅ real (proxies hf.co) | | Interactive API docs | ✅ `/docs` | --- ## Run locally ```bash pip install -r requirements.txt uvicorn app.main:app --reload --port 8000 # open http://localhost:8000/docs ``` ## Run with Docker ```bash docker build -t trainhub-api . docker run -p 7860:7860 -e SECRET_KEY=secret -v $(pwd)/storage:/app/storage trainhub-api ``` ## Deploy to Hugging Face Spaces This repo is a `docker`-SDK Space. Push to `main` and it builds automatically at `https://huggingface.co/spaces/Taimwe/trainhub-api`. > **Custom domain (you own a domain):** in the Space settings open **Settings → Custom > domain**, add e.g. `api.yourdomain.com`, then in your DNS create a `CNAME` from > `api.yourdomain.com` → `Taimwe-trainhub-api.hf.space` (or the Space's `.hf.space` > hostname). For a root domain (`yourdomain.com`) you need an `ALIAS`/`ANAME` record or > a proxy; refer to your DNS provider. Custom domains require a paid HF plan + an > HF token set on the Space. ## Environment variables | Var | Default | Purpose | |---|---|---| | `SECRET_KEY` | `dev-secret-change-me` | JWT signing key — **change in prod** | | `ACCESS_TOKEN_EXPIRE_MINUTES` | `1440` | token lifetime | | `DATABASE_URL` | `sqlite:///./trainhub.db` | SQLAlchemy URL (Postgres works too) | | `STORAGE_DIR` | `./storage` | where uploaded files land | | `HF_TOKEN` | — | optional, raises Hub rate limits / enables private access | --- ## API quick tour Base URL: `http://localhost:8000` (or your Space/domain). ### Auth ``` POST /auth/register {"username","email","password"} -> {access_token, user} POST /auth/login {"username","password"} -> {access_token, user} ``` Send the token as `Authorization: Bearer `. ### Models / Datasets / Spaces ``` POST /models {"name","description","pipeline_tag","tags","license"} GET /models?search=&owner= GET /models/{id} PATCH /models/{id} DELETE /models/{id} POST /models/{id}/like POST/GET/PATCH/DELETE /datasets (same pattern) POST/GET/DELETE /spaces ``` ### Files ``` POST /files/model/{id} multipart "file" -> upload GET /files/model/{id} -> list GET /files/model/{id}/{filename} -> download (streamed) ``` `dataset` works the same way (`/files/dataset/{id}`). ### Training jobs ``` POST /jobs {"base_model","dataset","config":{"epochs","lr","batch_size","seed"}} GET /jobs GET /jobs/{id} GET /jobs/{id}/metrics -> [{epoch, loss}, ...] (loss genuinely decreases) POST /jobs/{id}/cancel ``` Jobs run in a background thread and persist status + loss history to the DB. ### Hugging Face Hub bridge ``` GET /hub/search?q=llama -> real models from hf.co POST /hub/import {"repo_id"} -> import metadata into your local models ``` ### Meta ``` GET /health GET /stats GET / ``` --- ## Fine-tuning real models The worker in `app/services/training.py` trains a small MLP with NumPy so the whole stack runs anywhere with zero GPU. To fine-tune actual models, replace `_run()` with a `transformers`/`TRL` call, e.g.: ```python from transformers import AutoModelForCausalLM, Trainer, TrainingArguments model = AutoModelForCausalLM.from_pretrained(job.base_model) trainer = Trainer(model=model, args=TrainingArguments(..., report_to=[]), train_dataset=ds) for step in trainer.train(): # or a custom training loop ... # write loss to job.metrics each step, db.commit() ``` Point `base_model` at your HF model id (`Taimwe/...` or any public id) and add `torch`/`trl` to `requirements.txt`. ## Notes - Single-process design (threads) is ideal for a self-hosted box. To scale to many workers, replace `services/training.py` with a Celery/Redis or Kubernetes job runner and keep the same REST contract. - SQLite is the default for simplicity; set `DATABASE_URL` to Postgres for production. - `CORS` is wide open for local frontends; tighten `allow_origins` in production.