On Windows this required PraisonAI #4515; earlier releases killed the trainer on every relaunch.
Persistent run state (a run reappears in history after any engine restart) landed in PraisonAI #4510; earlier releases dropped the live run on restart and would start a second trainer beside it.
Quit-safety on macOS and Linux (the trainer dies with the engine on Quit, not just on Stop) landed in PraisonAI #4508; earlier releases orphaned the trainer to
init and held the GPU until reboot.Quick Start
1
Open the Fine-Tuning tab
Pick a method, a base model, and a dataset. The form collects everything the engine needs to start a run.
2
Start the run
The app posts your config to
/train/start. Invalid configs are rejected immediately, before any model download begins.3
Watch progress and stop when needed
Loss, step, and log lines stream live. Stop ends the run and releases the GPU. The checkpoint stays on disk under the run’s directory.
4
Load the checkpoint
When the run reports
done, point an agent’s llm= at the checkpoint directory to chat with your fine-tuned model.Supported Methods
The form supports these training methods. GRPO is validated but cannot be launched from the UI form — it needsreward_funcs, which the form does not collect, so run it from the command line.
Column requirements come from the dataset’s contents, so the app names them in the per-method hint rather than checking them up front. The trainer still enforces them.
How It Works
The engine writes your config, spawns a training subprocess in its own process group, and follows its output.Run IDs
Each run becomes a directory, so run ids are unique case-insensitively —run-x and RUN-X are the same run because macOS and Windows filesystems fold case. Auto-generated ids append a numeric suffix, so two starts inside the same second no longer collide.
Starting a run with an id that already exists returns
400 Bad Request.
Stop Semantics
The Stop button posts to/train/stop/{run_id}, naming the run to cancel — so a stale tab cannot cancel a newer run.
The GPU is released in every case. A run this engine did not spawn is only adopted when the recorded start-time fingerprint still matches the live pid. If the pid has been recycled to an unrelated process, the run is not adopted — it is already surfaced as terminal, so Stop has nothing to signal and no unrelated process group is ever SIGTERM’d from the trainer.
History & Retention
Finished runs stay in memory so the history pane can show them, within bounded caps. The full log file stays on disk.train.log in each run directory is the complete record. The in-memory metric series is a tail for the chart, not an archive.Preflight Validation
Invalid configs are rejected before a single byte is downloaded, so you learn about a mistake in seconds rather than after a multi-gigabyte model load.Best Practices
Name your runs
Name your runs
An explicit run id makes the checkpoint directory predictable. Remember ids are case-insensitive —
Exp-1 and exp-1 are the same run.Run GRPO from the command line
Run GRPO from the command line
GRPO needs
reward_funcs, which the form cannot collect. Launch it with praisonai train instead and watch it from the same engine.Keep the trainer in its own environment
Keep the trainer in its own environment
praisonai-train pulls torch and unsloth, which you may keep in a separate CUDA-matched venv. Set PRAISONAI_TRAIN_CMD to choose the interpreter — --config <path> is always appended, so the override picks the interpreter, not the contract.Reconnect, don't restart
Reconnect, don't restart
Progress is a ring buffer replayed from a cursor and the run’s state is written to
runs/<run-id>/run.json at every transition. Close the window and reopening the run replays what you missed. A run whose child process is still alive after an engine restart reappears as running and Stop still reaches it — provided the child’s start-time fingerprint still matches the pid we recorded. A recycled pid is refused, and the run appears as interrupted instead. State files written before this fingerprint was added (pre-upgrade) are also refused; at most one run shows as interrupted across a single upgrade restart.Runs are adopted by identity, not just by pid
Runs are adopted by identity, not just by pid
Adoption after a restart requires both the recorded pid and its start-time fingerprint to match the live process — a pid alone is not enough because the OS recycles pids. If it did not check, a recycled pid could be adopted as
running (wedging Start behind a phantom run) and Stop could SIGTERM whatever unrelated program now holds that pid. The Rust shell guards its own engine the same way — see src-tauri/src/adopt.rs and its PidReused rejection.Keep the log file for the full record
Keep the log file for the full record
The chart shows only recent metrics. For the complete series, read
train.log in the run directory.Stop from the run's own tab
Stop from the run's own tab
Stop names the run it cancels, so a stale tab is refused. Trigger Stop from the tab showing the run you actually want to end.
Related
Engine API
The
/train/* routes and their status codesSettings Reference
Model, sampling, and safety fields
Chat & Streaming
Talk to your fine-tuned model
Models & API Keys
Point the app at a local checkpoint

