113 lines
3 KiB
Markdown
113 lines
3 KiB
Markdown
# Training Pydantic Models Recommendation
|
|
|
|
## Current State
|
|
|
|
| Model | Purpose |
|
|
|-------|---------|
|
|
| `TrainingStartRequest` | Request payload to start training |
|
|
| `TrainingStartResponse` | Immediate response after `/start` |
|
|
| `TrainingStatusResponse` | Response for `/status` polling |
|
|
| `TrainingMetricsResponse` | Historical metrics |
|
|
| `TrainingProgressResponse` | Per-step progress data |
|
|
|
|
---
|
|
|
|
## Proposed Models
|
|
|
|
### 1. `TrainingStartRequest` — Keep as-is
|
|
Well-structured, no changes needed.
|
|
|
|
---
|
|
|
|
### 2. `TrainingJobResponse` (rename from `TrainingStartResponse`)
|
|
Returned **once** when `/train/start` is called.
|
|
|
|
```python
|
|
class TrainingJobResponse(BaseModel):
|
|
"""Immediate response when training is initiated"""
|
|
job_id: str
|
|
status: Literal["queued", "error"]
|
|
message: str
|
|
error: Optional[str] = None
|
|
```
|
|
|
|
---
|
|
|
|
### 3. `TrainingStatus` (unifies `TrainingStatusResponse` + `TrainingPhaseUpdate`)
|
|
Single model for **both streaming and polling**.
|
|
|
|
```python
|
|
class TrainingStatus(BaseModel):
|
|
"""Current training job status - works for streaming or polling"""
|
|
job_id: str
|
|
phase: Literal[
|
|
"idle",
|
|
"loading_model",
|
|
"loading_dataset",
|
|
"configuring",
|
|
"training",
|
|
"completed",
|
|
"error",
|
|
"stopped"
|
|
]
|
|
is_training_running: bool # True if training loop is actively running
|
|
message: str
|
|
error: Optional[str] = None
|
|
details: Optional[dict] = None # Phase-specific info, e.g. {"model_size": "8B"}
|
|
```
|
|
|
|
**Usage:**
|
|
- **Streaming**: Push when phase changes
|
|
- **Polling**: Return from `GET /train/status/{job_id}`
|
|
|
|
---
|
|
|
|
### 4. `TrainingProgress` (rename from `TrainingProgressResponse`)
|
|
Per-step metrics during active training.
|
|
|
|
```python
|
|
class TrainingProgress(BaseModel):
|
|
"""Training progress metrics - for streaming or polling"""
|
|
step: int
|
|
total_steps: int
|
|
loss: float
|
|
learning_rate: float
|
|
progress_percent: float
|
|
epoch: Optional[int] = None
|
|
elapsed_seconds: Optional[float] = None
|
|
eta_seconds: Optional[float] = None
|
|
```
|
|
|
|
---
|
|
|
|
### 5. `TrainingMetricsResponse` — Remove
|
|
Frontend stores history in IndexedDB.
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Current | Proposed | Action |
|
|
|---------|----------|--------|
|
|
| `TrainingStartRequest` | `TrainingStartRequest` | Keep |
|
|
| `TrainingStartResponse` | `TrainingJobResponse` | Rename |
|
|
| `TrainingStatusResponse` | `TrainingStatus` | Rename + enhance |
|
|
| `TrainingMetricsResponse` | — | **Remove** |
|
|
| `TrainingProgressResponse` | `TrainingProgress` | Rename + enhance |
|
|
|
|
---
|
|
|
|
## API Flow
|
|
|
|
```
|
|
POST /train/start
|
|
└─► TrainingJobResponse { job_id, status: "queued" }
|
|
|
|
StreamingResponse / Polling:
|
|
└─► TrainingStatus { phase: "loading_model", is_training_running: false }
|
|
└─► TrainingStatus { phase: "loading_dataset", is_training_running: false }
|
|
└─► TrainingStatus { phase: "training", is_training_running: true }
|
|
└─► TrainingProgress { step: 1, loss: 2.5, ... }
|
|
└─► TrainingProgress { step: 2, loss: 2.3, ... }
|
|
└─► TrainingStatus { phase: "completed", is_training_running: false }
|
|
```
|