Run training and watch jobs
Studio’s Home page (#/) is split into two columns: Run training on the left, the Jobs list on the right. Clicking a job in the list navigates to Job detail (#/jobs/:id), which is where the live event stream and the loss chart live.
Run training
The Run training panel calls/api/manifest once when the page loads. The response is the project’s createArkor({ trainer }) summary, which the panel uses to label the action:
Run training: <trainer name>once a trainer is found.No trainer in src/arkor/index.ts yet. Add createTrainer(...) and pass it to createArkor.if the bundle imported but exposed nothing.Couldn't read manifest: <error>if the build itself failed (typo insrc/arkor/, etc.).
POST /api/train. The backend spawns arkor start in a subprocess and streams its stdout / stderr back as raw text. The pre-formatted log box auto-scrolls; what you see is exactly what the spawned arkor start would print in a terminal.
There is no input form for picking the trainer or passing flags: Studio always runs the trainer registered through createArkor, and arkor start reuses .arkor/build/index.mjs if it already exists. Edits to src/arkor/ are not picked up automatically across multiple clicks on the same page; reload the Run training page (or run arkor build from a terminal) between edits and the next click. See CLI § build / start for the precise rebuild rules.
What “first run” looks like
Click Run training. Two phases follow:- GPU allocation. The job appears as
Warming up GPU. The loss chart shows aWaiting for GPUplaceholder, the events list is empty, and the Phase row in the metadata sidebar readsWarming up GPUwhile the GPU warm-up timer ticks. This phase varies in length: typically under a minute when a worker is still warm from a recent job, occasionally several minutes when one has to start from cold. See the Quickstart for why this happens. - The training run. When
training.startedarrives, the status flips toRunning, the loss chart starts updating fromtraining.logframes, and the Phase row readsTraining run. This is the 7 to 12 minute window in the template table on the Quickstart.
Jobs list
The Jobs list pollsGET /api/jobs once at mount, then every 5 seconds. There is no manual refresh button; the interval is fixed.
The list shows whatever order the backend returned. There is no client-side filter, search, or pagination. When the project has no jobs yet, the panel reads
No jobs yet..
Job detail
#/jobs/:id opens a Server-Sent Events connection to GET /api/jobs/:id/events via EventSource. The page listens for five named events plus a stream sentinel:
In every row below, the event log renders the event name in its own column next to the message; the message text shown is what JobDetail.pushEvent() builds from the SSE payload.
Stream errors are surfaced as a separate
Event stream interrupted. banner above the loss-chart and events-log cards (not as a log entry); the banner clears the next time an SSE frame arrives, and reconnect is left to the browser’s EventSource retry behaviour.
The event log keeps only the last 500 entries (older entries drop off as new ones arrive). It is a scrolling list rendered from named SSE events, intended for quick inspection rather than full forensic logs; for the complete history, look at the cloud-api directly.
The loss chart is an SVG plot drawn from training.log events. It uses min-max scaling on the y-axis and the step number on the x-axis, and shows up to two series:
- Training loss — solid teal line, one vertex per event with a numeric
loss. - Eval loss — dashed pink line with point markers, drawn from events that carry a numeric
evalLoss(typically everyevalStepsticks). The series is built from the events directly, so eval-only frames (numericevalLosswithlossomitted) still appear in the line, legend, and stats. The legend hides this entry until at least one eval point arrives.
loss / evalLoss are present at that step (eval-only steps don’t show a loss value, and vice-versa). The chart shows the Waiting for training.log events… placeholder until at least one event with a numeric loss or evalLoss arrives — training.log frames where both fields are null/omitted don’t count.
Advanced metrics
The Advanced toggle in the chart’s header reveals a per-series statistics panel. Each card reports:- Mean loss ± 95% CI — sample mean of the loss values together with the half-width of the 95% confidence interval (Student’s t-distribution; falls back to z = 1.96 for n > 31).
- Std dev and Variance — Bessel-corrected sample estimates (
ddof=1). - p90 and p95 — linearly interpolated percentiles, matching numpy’s default convention.
training.log event with a numeric evalLoss arrives.
Things this page does not do
- No cancel button. To stop a running job, call
trainer.cancel()from your own code that drives the trainer. Studio does not expose this in the UI today. - No artifact browser. The page reports the artifact count for completed jobs but does not list or link to individual artifacts. For full artifact access, use the cloud-api or the SDK’s
onCompleted({ artifacts })callback during the run. - No mid-run inference. The Playground is for completed jobs only (see Playground). For live inspection during a run, use the SDK’s
onCheckpoint({ infer }).