Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
23d128b
refactor(rl): use the shared training progress callback directly
albcui Aug 13, 2026
fc89fa6
fix(customization): stop non-step reports from erasing the metric series
albcui Aug 13, 2026
cdf19b6
feat(customization): accumulate a time series for every reported metric
albcui Aug 13, 2026
119ba8c
fix(customization): carry sticky status_details fields across updates
albcui Aug 13, 2026
d0df57c
fix(rl): report the final training step, and stop double-counting it
albcui Aug 13, 2026
b29d4e7
thanks CodeRabbit
albcui Aug 13, 2026
9b9183d
refactor(customization): drop the carry-forward machinery
albcui Aug 13, 2026
b17fe34
fix(customization): correct three defects in the reported payload
albcui Aug 13, 2026
8b20c59
chore(rl): give the new progress module the header its siblings use
albcui Aug 13, 2026
b4c8ab8
docs(customization): stop justifying the design with an unwired GRPO …
albcui Aug 13, 2026
f6cbecd
fix(rl): make the steps_per_epoch fallback reachable, and drop a stri…
albcui Aug 13, 2026
1d9fe78
docs(customization): keep the callback's rationale out of the reporter
albcui Aug 13, 2026
0469e25
refactor(rl): stop holding a reference the logger never reads
albcui Aug 13, 2026
c55f9d3
refactor(customization)!: one naming rule for every training metric
albcui Aug 13, 2026
3f1aa38
docs(customizer): document the metric naming rule and the series payload
albcui Aug 13, 2026
4d1bb47
fix(rl): bound progress reports by run length, not just val_period
albcui Aug 14, 2026
ed2aeba
fix(automodel): strip the val_ prefix the recipes already applied
albcui Aug 14, 2026
e0a47fa
fix(customization): discard the steps a resumed run replays
albcui Aug 14, 2026
194e5f3
fix(rl): report a validation pass that scores on something other than…
albcui Aug 14, 2026
aedc033
fix(customization): correct twelve defects across the progress report…
albcui Aug 14, 2026
05f523b
fix(rl): derive the train report cadence from run length alone
albcui Aug 14, 2026
bdfd812
update docstrings to reflect seeding mechanism
albcui Aug 14, 2026
ca28ef0
refactor(customization): rename the metric-name qualifier off "namesp…
albcui Aug 14, 2026
5ebca0a
fix(customization): bound progress reporting for every backend, not one
albcui Aug 17, 2026
38c8698
feat(customization): expose the reporting budget in every backend's j…
albcui Aug 17, 2026
a1dedab
feat(customization): let a job choose which metrics get a stored curve
albcui Aug 17, 2026
0945c83
refactor(automodel)!: delete the inert log_every_n_steps
albcui Aug 17, 2026
d2eb1b6
test(automodel): pin that the reporting block written is the one read
albcui Aug 17, 2026
ac82022
refactor(customization)!: name the time-series metrics, qualified and…
albcui Aug 17, 2026
c2f264e
fix(customization): correct four data-loss defects in the progress gate
albcui Aug 17, 2026
8680cdb
refactor(customization)!: stop throttling, and let the transport own …
albcui Aug 17, 2026
03f26b2
fix(customization): stop a misconfigured metric list from killing the…
albcui Aug 17, 2026
cf025ee
feat(customization): rate-limit progress reports, keeping every point
albcui Aug 18, 2026
6d322c5
fix(automodel,studio): switch two guards back on
albcui Aug 18, 2026
b5905ef
docs(customizer): correct the reporting cadence and document its config
albcui Aug 18, 2026
4edc41e
test(customization): make the vacuous tests fail when they should
albcui Aug 18, 2026
7ab8ebe
fix(customization): bound how long a report can stall the training loop
albcui Aug 18, 2026
1063ea3
docs(customization): correct three claims in the reporting comments
albcui Aug 18, 2026
5c941fb
refactor(automodel): drop the setup-phase report and the reporter att…
albcui Aug 18, 2026
cec03c2
refactor(customization): rename ReportRateLimiter.allows_now to allow…
albcui Aug 18, 2026
58361e0
fix(customization): one metric store, so a withheld report cannot be …
albcui Aug 18, 2026
9ba56df
docs(customization): cut the callback's prose to what a reader needs
albcui Aug 18, 2026
e9316d8
docs(customization): tighten the reporting config's prose
albcui Aug 18, 2026
1cc0555
docs(customization): say what ProgressReportingConfig configures
albcui Aug 18, 2026
b6bd146
style(studio): let prettier collapse a call that fits on one line
albcui Aug 18, 2026
3395eb3
docs(customizer): drop a negation the sentence did not need
albcui Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions docs/customizer/manage-customization-jobs/get-job-status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ Get detailed execution status for a customization job, including step-by-step pr
This endpoint provides granular execution details including:

- **Step-level status**: `model-and-dataset-download` → `training` → `model-upload` → `model-entity-creation`
- **Training metrics**: `step`, `epoch`, `train_loss`, `lr` (learning rate), `grad_norm`, `val_loss`
- **Training progress**: `step`, `epoch`, `max_steps`, `num_epochs`, `percentage_done`
- **Latest metric values**: one field per metric, named `<phase>_<metric>` — `train_loss`, `train_lr`, `train_grad_norm`, `val_loss`, and whatever else your backend reports
- **Metric history**: `metrics`, holding each metric as a series of `{step, epoch, value}` points
- **Progress tracking**: `downloaded_files`, `uploaded_bytes`, `progress_pct`

Which metrics appear depends on the backend and the algorithm. See [Checking Your Customization Job Metrics](/documentation/customizer-reference/tutorials/metrics) for how the names are formed.

To list jobs or get job definitions (model entity, hyperparameters, spec), use [List Active Jobs](/documentation/customizer-reference/manage-customization-jobs/list-active-jobs) instead.

</Tip>
Expand Down Expand Up @@ -149,9 +153,25 @@ curl -X GET \
"num_epochs": 2,
"step": 8,
"epoch": 1,
"percentage_done": 8,
"train_loss": 2.8918895721435547,
"lr": 4.9101714686276044e-05,
"grad_norm": 26.0
"train_lr": 4.9101714686276044e-05,
"train_grad_norm": 26.0,
"metrics": {
"train_loss": [
{ "step": 4, "epoch": 1, "value": 3.2087905406951904 },
{ "step": 8, "epoch": 1, "value": 2.8918895721435547 }
],
"val_loss": [],
"train_lr": [
{ "step": 4, "epoch": 1, "value": 4.9550857343138022e-05 },
{ "step": 8, "epoch": 1, "value": 4.9101714686276044e-05 }
],
"train_grad_norm": [
{ "step": 4, "epoch": 1, "value": 31.5 },
{ "step": 8, "epoch": 1, "value": 26.0 }
]
}
}
}
]
Expand Down Expand Up @@ -225,11 +245,30 @@ curl -X GET \
"num_epochs": 2,
"step": 94,
"epoch": 2,
"percentage_done": 100,
"train_loss": 0.3437718152999878,
"lr": 5.000000000000001e-07,
"grad_norm": 20.125,
"train_lr": 5.000000000000001e-07,
"train_grad_norm": 20.125,
"val_loss": 0.5527229905128479,
"checkpoint_path": "/var/run/scratch/job/training/checkpoints"
"checkpoint_path": "/var/run/scratch/job/training/checkpoints",
"metrics": {
"train_loss": [
{ "step": 47, "epoch": 1, "value": 1.1204545497894287 },
{ "step": 94, "epoch": 2, "value": 0.3437718152999878 }
],
"val_loss": [
{ "step": 47, "epoch": 1, "value": 0.9182837605476379 },
{ "step": 94, "epoch": 2, "value": 0.5527229905128479 }
],
"train_lr": [
{ "step": 47, "epoch": 1, "value": 2.5e-05 },
{ "step": 94, "epoch": 2, "value": 5.000000000000001e-07 }
],
"train_grad_norm": [
{ "step": 47, "epoch": 1, "value": 24.75 },
{ "step": 94, "epoch": 2, "value": 20.125 }
]
}
}
}
]
Expand Down
106 changes: 100 additions & 6 deletions docs/customizer/tutorials/metrics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,95 @@ The time to complete this tutorial is approximately 10 minutes.

## Available Metrics

Each customization job tracks two key metrics:
A customization job tracks every numeric metric its backend reports. The two you
will always find are:

- **Training Loss**: Calculated during training, logged every 10 steps (default, configurable via hyperparameters)
- **Validation Loss**: Calculated during validation, logged at each validation interval
- **Training Loss** (`train_loss`): recorded every step the training framework logs one
- **Validation Loss** (`val_loss`): recorded on every validation pass

Alongside those you will typically see `train_lr` (learning rate) and
`train_grad_norm`, plus whatever else the algorithm produces — a DPO job also
reports `train_preference_loss` and `val_accuracy`, for example.

An algorithm that scores validation on something other than a loss reports no
`val_loss`, and the series stays empty rather than filling with zeros.

### How Metrics Are Named

Each metric is named `<phase>_<metric>`, where the phase is `train` or `val` and
the metric keeps whatever name the training framework gave it. A metric reported
during both training and validation therefore stays separate: DPO's `accuracy`
becomes `train_accuracy` and `val_accuracy` rather than one interleaved series.

`train_loss` and `val_loss` are simply what this rule produces for a metric named
`loss`.

### Where Metrics Appear

Each metric shows up in two places in a training task's `status_details`:

- **The latest value**, as a top-level field under its full name (`train_loss`,
`train_lr`, ...). Present only when the metric was actually reported, so a
missing field means no value rather than a zero.
- **The full history**, under `metrics`, as a list of `{step, epoch, value}`
points per metric. This is what the loss curves in the UI are drawn from.

Non-numeric values a framework emits alongside the scalars — histograms, tables,
nested dictionaries — are not charted and do not appear in either place.

### How Often Metrics Are Sent

Recording and sending are separate, and only the second is throttled.

Every value your training framework logs is recorded, at full resolution. What
is limited is how often the accumulated set is *sent* to the platform, because
each update carries the whole history and is written more than once server-side.
Reports are buffered in memory and sent at most once every ten seconds by
default; a buffered report loses nothing, since its points travel with the next
one that goes. The final values are always sent when the job finishes.

The practical effect is that a chart may lag the run by a few seconds, and the
curve you eventually read is complete.

### Controlling Metric Detail

Two optional fields under `schedule.progress_reporting` change this. Most jobs
need neither.

- **`min_report_interval_seconds`** (default `10`): the least time between
updates reaching the platform. Raise it to spend less training time on
reporting; lower it — or set `0`, which sends every logged step — for a
progress bar that moves more often. Recording is unaffected either way.
- **`time_series_metrics`**: which metrics keep a *history* rather than only a
latest value. Names are the full ones you read back, so they carry the phase
prefix, and glob patterns are accepted: `["*_loss", "*_lr"]`. Omit the field to
take your backend's default, which keeps the loss, learning rate and gradient
norm. Use `["*"]` to keep a history of everything the backend reports.

A metric left out of `time_series_metrics` is still reported as a latest value
on every update — only its history is dropped. That keeps throughput counters
like `train_tps` costing one number instead of several hundred points.

Set them in the `schedule` block when you create the job:

```python
schedule={
"epochs": 3,
"progress_reporting": {
"min_report_interval_seconds": 30,
"time_series_metrics": ["*_loss", "*_lr"],
},
}
```

<Note>

These control what the *platform* stores. They are unrelated to the training
framework's own logging cadence — Unsloth's `schedule.logging_steps`, for
example — which decides how often your framework produces a value in the first
place, and drives its stdout and any W&B or MLflow run.

</Note>

## Viewing Your Metrics

Expand Down Expand Up @@ -67,11 +152,20 @@ for step in status.steps or []:
print(f"Epoch: {details.get('epoch')}/{details.get('num_epochs')}")
print(f"Training Loss: {details.get('train_loss')}")
print(f"Validation Loss: {details.get('val_loss')}")
print(f"Learning Rate: {details.get('lr')}")
print(f"Gradient Norm: {details.get('grad_norm')}")
print(f"Learning Rate: {details.get('train_lr')}")
print(f"Gradient Norm: {details.get('train_grad_norm')}")
```

The response includes training progress and metrics including loss, learning rate, and validation loss.
To read the curves rather than the latest values, use the `metrics` payload. It
carries every metric the job reported, so iterating it picks up backend-specific
ones without naming them in advance:

```python
for name, points in (details.get("metrics") or {}).items():
if not points:
continue
print(f"{name}: {len(points)} points, latest {points[-1]['value']}")
```

### Using MLflow

Expand Down
Loading
Loading