File size: 4,724 Bytes
f16f8f7
 
 
 
 
 
 
 
 
 
 
3149a63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ae86e9
3149a63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ae86e9
 
3149a63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
title: TrainHub API
emoji: 🚂
colorFrom: indigo
colorTo: purple
sdk: docker
app_port: 7860
pinned: false
short_description: Self-hosted AI training platform backend (FastAPI)
---

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

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