Skip to contents

The app is a thin client over the R functions. Nothing happens in the app that you cannot do from the console, and every run it starts is a normal run directory.

The six panels

1 Data. Drop a CSV, TSV, JSONL, JSON, or Parquet file on the input, or click “Load the example tickets”. You see row and column counts and the first eight rows.

2 Map. The columns appear as chips in the left bucket. Drag them into System, Prompt, or Response. Two chips in one slot are joined with a blank line. The template fields below fill in as you drag and can be edited by hand for anything else, for example Ticket #{id}: {subject}. The right side shows the first three rows exactly as the model will see them.

3 Model. Pick a preset or type a Hugging Face model id. “Detect hardware” runs the same check as dragon_check() and reports whether the chosen model fits in GPU memory and whether it needs a token.

4 Train. Epochs, learning rate, rank, sequence length, and batch size are on the front. Everything else is under Advanced. The panel refuses to start until the first three steps are complete, and estimates the number of optimizer steps from your settings.

5 Monitor. A live loss curve with evaluation points, step count, ETA, the trainer log, and the R code for the run. Cancel asks the trainer to stop after the current step and save. Resume picks up from the last checkpoint. Every run in the runs directory is listed, including ones started from the console.

6 Try it. Type a prompt and see the fine-tuned reply next to the base model’s reply. “Merge and save” writes a standalone model directory.

Running it for other people

The app has to run on the machine with the GPU, and training runs as a child process of the app. Shiny Server, Posit Connect, or a plain shiny::runApp() on a workstation all work. Hosted services without GPUs, such as shinyapps.io, can run the app but training will be CPU only.

Set the runs directory so every session sees the same runs:

dragon_app(runs_dir = "/srv/dragonfarm/runs", host = "0.0.0.0", port = 3838)

Under the hood

The Map panel uses the sortable package for the drag-and-drop buckets. Progress in the Monitor panel comes from polling progress.jsonl and status.json once a second with reactivePoll(), so it works over a remote connection and survives a browser refresh. The Try it panel calls dragon_generate(), which loads the model in a short-lived process, so each request takes a few seconds.