trainhub-api / README.md
Taimwe's picture
Add README
3149a63 verified
|
Raw History Blame Contribute Delete
4.54 kB

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

pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
# open http://localhost:8000/docs

Run with Docker

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 <token>.

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.:

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.