fix(studio): surface live step with null loss through the SSE progress stream (#6206)

* fix(studio): surface live step with null loss through the SSE progress stream

The metric histories skip non-finite steps, so during a NaN stretch the
SSE live loop and final complete event replayed the last finite
step/loss pair. Follow the live progress step when it is ahead of the
history tail and report its loss honestly (null until recovery).

Completes the NaN honesty fix for the SSE consumer flagged in review.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Apply live-step handling to inactive streams and clear the UI loss on null for PR #6206

Fresh /progress connections after a finished run took the inactive branch
which still replayed the last finite step and loss pair; apply the same
live-step correction there. On the frontend, applyProgress kept the stale
currentLoss when a payload advanced the step with a null loss; clear it so
the display shows -- until the loss recovers. Widen the runtime state type
to number | null, which the view layer already handles.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Daniel Han 2026-06-11 07:50:13 -07:00 committed by GitHub
commit 3733e0b274
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 180 additions and 24 deletions

View file

@ -274,7 +274,10 @@ export const useTrainingRuntimeStore = create<TrainingRuntimeStore>()((set) => (
jobId: payload.job_id || state.jobId,
currentStep: step,
totalSteps: Math.max(payload.total_steps, state.totalSteps),
currentLoss: currentLoss ?? state.currentLoss,
// A null loss at a new step means the backend reported a non-finite
// loss; clear the display instead of keeping the stale value.
currentLoss:
currentLoss ?? (step > state.currentStep ? null : state.currentLoss),
currentLearningRate: currentLearningRate ?? state.currentLearningRate,
progressPercent: payload.progress_percent,
currentEpoch: payload.epoch ?? state.currentEpoch,

View file

@ -90,7 +90,8 @@ export interface TrainingRuntimeState {
currentStep: number;
totalSteps: number;
currentEpoch: number;
currentLoss: number;
// null while the latest reported loss is non-finite
currentLoss: number | null;
currentLearningRate: number;
progressPercent: number;
elapsedSeconds: number | null;