Skip to main content

Flash

LoRA post-training for open-weight models: SFT, GRPO, and on-policy distillation. You describe a run in a TOML file, Flash allocates a GPU, trains, streams checkpoints, and serves the resulting adapter.

pip install freesolo-flash
export FREESOLO_API_KEY=fslo_...
flash login
flash train run.toml

The allocator picks the cheapest validated GPU class that fits the run, then supervises it server-side: stall watchdog, bounded auto-retry resuming from the last streamed checkpoint, and endpoint GC.

What this repository is

Flash is the client and control plane for Freesolo's hosted post-training service:

Path What it is
flash/cli/ the flash CLI — no declared runtime dependencies*
flash/server/ the FastAPI control plane — run submission, auth, project scoping
flash/engine/ the GPU worker and training recipes — verl + colocated vLLM
flash/providers/ the GPU substrate — pricing, allocation, submit/poll
flash/envs/ environment loading

* pip install freesolo-flash pulls nothing. Commands that run an environment locallyflash env test and flash env eval — do need the freesolo SDK, which they import at the point of use; install it with pip install freesolo.

Two components stay Freesolo-operated and are not in this repository:

Component Where it lives Self-hosted equivalent
Multi-tenant identity api.freesolo.co — verifies keys, owns projects/orgs FLASH_STANDALONE=1 runs single-tenant on your own key
Multi-LoRA serving serve.freesolo.coflash/serve/ is a thin client adapters land in your HF repos; serve them with any stack

So there are three ways to use Flash: against the hosted service, self-hosted against your own GPU accounts, or as training and provider code to read and modify. The training path is self-hostable end to end — see SELF_HOSTING.md.

Using the hosted service

flash login is not interactive — pass the key explicitly or export FREESOLO_API_KEY first:

pip install freesolo-flash
export FREESOLO_API_KEY=fslo_...
flash login          # validates the key, stores it in ~/.flash/config.json
flash whoami         # confirm the identity behind it

Every run names an environment, which supplies the task data and the reward or SFT target. Environments are published under a project, which scopes them to an organization:

flash projects create my-project                       # returns a project uuid
flash projects list                                    # look up existing uuids
flash env setup                                        # scaffold environment.py + dataset/train.jsonl
flash env push --project PROJECT_UUID --name my-env .  # returns an environment id

Every training TOML carries a required top-level project, validated against the authenticated organization before Flash allocates anything:

project = "your-project-uuid"
model = "Qwen/Qwen3.5-4B"
algorithm = "sft"

[environment]
id = "your-org/your-project/my-env"

[train]
epochs = 1
max_examples = 1000
lora_rank = 32              # lora_alpha defaults to 2 x lora_rank; set it to override
flash train run.toml                  # submit, prints a run id
flash runs status RUN_ID               # follow it
flash models deploy RUN_ID             # serve the trained adapter
flash models chat RUN_ID -m "hello"    # talk to it

Run management lives under flash runs (list, status, log, cancel, checkpoint) and serving under flash models (deploy, chat, deployments, undeploy, export). flash models lists supported base models — six curated Qwen checkpoints — and flash gpus lists GPU classes with estimated $/hr.

Intermediate RL checkpoints are deployable: list them with flash runs checkpoint RUN_ID, then pass RUN_ID/step-N as the adapter id. To copy a finished adapter into your own HuggingFace repo:

flash models export --adapter-id RUN_ID --repository your-org/your-repo

There are no built-in task environments — the environment you push defines the task. Single-turn and bounded multi-turn environments are supported.

Calling a deployed adapter from your own app

Deploy once with flash models deploy RUN_ID, then POST chat requests with your API key:

export RUN_ID=flash-1782194170-ce1cfcff
export FREESOLO_API_KEY=fslo_...

curl -X POST "https://flash.freesolo.co/v1/runs/$RUN_ID/chat" \
  -H "Authorization: Bearer $FREESOLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "hello"}], "max_tokens": 256}'

The response uses the OpenAI chat-completions shape; read choices[0].message.content. The run id is the adapter id. If the run is not deployed yet, the endpoint returns 409 with a hint to deploy first. Prefer this control-plane endpoint over calling the serving backend directly: it enforces run ownership and forwards per-run serving options such as thinking-mode parity.

Workload estimates (SFT)

flash train, --dry-run, and --cost read the pinned environment's packaged dataset without importing environment.py. dataset/train.jsonl is the canonical default; dataset/train.json also works, and [environment.params] split or dataset_path can select another packaged file.

The estimate tokenizes raw input/output fields plus the statically readable training contract. Environment-added prompts, few-shot examples, tool schemas, filters, and transformations are not executed, so real training can retain fewer rows, truncate more often, and cost more than the estimate. If no readable dataset exists, cost, dry-run, and submit all fail before GPU allocation rather than billing a profiling run.

Working on the code

The test suite is CPU-only and offline by default. No GPU, no network, no credentials:

uv sync --extra server --dev
uv run ruff check .
uv run ruff format --check .
uv run pytest -q

CI (.github/workflows/ci.yml) runs those on both supported interpreters (3.11 and 3.12), plus two size gates and a serial pass for timing-sensitive tests:

uv run python scripts/check_file_size.py      # no module in flash/ over 1000 lines
uv run python scripts/check_function_size.py  # no function in flash/ over 150 lines
uv run pytest -q -m wallclock                 # asserts on real elapsed time; runs alone

mypy also runs in CI but is advisory — it reports existing type errors without failing the build. Formatting is gated, so run uv run ruff format . before you push.

The flash command can belong to something else. The server and dev extras install runpod-flash, which declares its own flash console script; whichever installs last wins, and RunPod's exits 0 while doing nothing. Use flash-cli — the same entry point under a name nothing else claims — or python -m flash.cli from a checkout. A base pip install freesolo-flash is unaffected.

See CONTRIBUTING.md for the branching model — in short, pull requests go into dev.

Layout

  • flash/core/catalog.py — curated model catalog (Qwen3.5 and Qwen3.6, dense and MoE), VRAM sizing, and each model's thinking capability
  • flash/schema/, flash/core/spec.py — TOML to JobSpec
  • flash/runner/ — server-side run supervisor (durable job handle, retries, cost guard)
  • flash/providers/ — GPU substrate behind the base.Provider protocol, with allocator.py picking the cheapest fitting class
  • flash/engine/ — the on-GPU worker (verl + colocated vLLM rollouts) and the shared recipe. SFT targets and RL rewards route through the active environment, so task-specific grading lives with the example, not in the engine
  • flash/envs/ — environment registry and the Freesolo SDK adapter
  • flash/serve/, flash/server/ — serving client and the FastAPI control plane (run via the separate flash-server command)
  • tests/ — pytest suite, CPU-only and offline

Within flash/engine/worker/, trainers live under train/, split by algorithm (sft/, rl/, opd/) over a shared core/. Each carries a child/ holding stdlib-only modules copied into the verl subprocess rather than imported — flash and verl pin incompatible torch/vllm versions, so neither can import the other.

Self-hosting

Run your own control plane against your own GPU accounts, with no Freesolo backend involved. SELF_HOSTING.md is the full guide; the short version:

pip install 'freesolo-flash[server]'   # the base install is client-only

export FLASH_STANDALONE=1
export FREESOLO_INTERNAL_KEY=$(openssl rand -hex 32)
export HF_TOKEN=hf_...
export FLASH_HF_NAMESPACE=your-hf-username   # a namespace your HF_TOKEN can write to
export RUNPOD_API_KEY=...                    # or LAMBDA_API_KEY, or VAST_API_KEY

flash-server --host 0.0.0.0 --port 8080

You need one of RunPod, Lambda, or Vast. Providers whose key is unset are never considered, and the allocator only proposes classes it can actually provision. Startup fails only when all three are missing.

FLASH_STANDALONE=1 stops the plane calling out for project, environment, and billing validation, and trusts FREESOLO_INTERNAL_KEY as a single-tenant operator credential. External bearer tokens are rejected rather than accepted unverified. A standalone plane is single-tenant — whoever holds that key can spend your GPU budget, so keep it off untrusted networks. See the security model.

The GPU worker image is public and published under an explicit CUDA tag, not latest:

docker pull ghcr.io/freesolo-co/flash-worker:cu128

Release channels

Two channels are published to PyPI from the same source, distinguished by one line in flash/_internal/channel.py:

Channel PyPI package CLI Default plane Published on
prod freesolo-flash flash flash.freesolo.co a version bump merged to main
dev freesolo-flash-dev flash-dev flash-dev.freesolo.co a push to dev with an unused version

Each environment holds exactly one channel: both packages ship the same import package with one baked CHANNEL line, so installing both makes the later install win for both CLIs. For side-by-side prod and staging, use a virtualenv per channel (or pipx).

Within a commit, [project].version and [tool.flash-dev].version must match (CI enforces this), so cutting a release bumps both. The published channels still differ, because dev publishes on merge to dev while prod publishes only once dev is promoted to main.

Either CLI honours an explicit FLASH_API_URL or login --api-url; the channel only sets the default.

Contributing

See CONTRIBUTING.md. Security issues: SECURITY.md — do not open a public issue.

License

Apache-2.0. See LICENSE and NOTICE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

freesolo_flash_dev-1.1.95.tar.gz (2.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

freesolo_flash_dev-1.1.95-py3-none-any.whl (1.2 MB view details)

Uploaded Python 3

File details

Details for the file freesolo_flash_dev-1.1.95.tar.gz.

File metadata

  • Download URL: freesolo_flash_dev-1.1.95.tar.gz
  • Upload date:
  • Size: 2.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for freesolo_flash_dev-1.1.95.tar.gz
Algorithm Hash digest
SHA256 bfd01f0981b21840db9b6f2c6fcd0e07248e1b274cef0611a16feed286d86e54
MD5 dcb235ba15baa7e1339cc6b52c6e9f8e
BLAKE2b-256 db7ee0b85b867226c1ded3d9dd1a62a23feeb104e478026b0304576180587300

See more details on using hashes here.

File details

Details for the file freesolo_flash_dev-1.1.95-py3-none-any.whl.

File metadata

  • Download URL: freesolo_flash_dev-1.1.95-py3-none-any.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for freesolo_flash_dev-1.1.95-py3-none-any.whl
Algorithm Hash digest
SHA256 b1df513705e18721409f08e70edac4311578f431fe36053bf91c1998e7305f9e
MD5 1f58b505c01a5e4f8dfafffb64a67282
BLAKE2b-256 415895a91c8de011545138b458b67693a5b4e4dcbc78af4347894267eca32149

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.119

2 files

1.2.118

2 files

1.2.117

2 files

1.2.116

2 files

1.2.115

2 files

1.2.113

2 files

1.2.112

2 files

1.2.111

2 files

1.2.110

2 files

1.2.109

2 files

1.2.108

2 files

1.2.107

2 files

1.2.105

2 files

1.2.102

2 files

1.2.100

2 files

1.2.98

2 files

1.2.97

2 files

1.2.96

2 files

1.2.95

2 files

1.2.94

2 files

1.2.89

2 files

1.2.88

2 files

1.2.87

2 files

1.2.70

2 files

1.2.69

2 files

1.2.68

2 files

1.2.67

2 files

1.2.66

2 files

1.2.65

2 files

1.2.64

2 files

1.2.63

2 files

1.2.61

2 files

1.2.60

2 files

1.2.59

2 files

1.2.58

2 files

1.2.53

2 files

1.2.52

2 files

1.2.49

2 files

1.2.48

2 files

1.2.43

2 files

1.2.39

2 files

1.2.37

2 files

1.2.36

2 files

1.2.33

2 files

1.2.28

2 files

1.2.23

2 files

1.2.22

2 files

1.2.21

2 files

1.2.14

2 files

1.2.13

2 files

1.2.12

2 files

1.2.7

2 files

This release

1.1.95 This release

2 files

1.1.93

2 files

1.1.91

2 files

1.1.74

2 files

1.1.71

2 files

1.1.70

2 files

1.1.69

2 files

1.1.68

2 files

1.1.67

2 files

1.1.66

2 files

1.1.65

2 files

1.1.64

2 files

1.1.63

2 files

1.1.61

2 files

1.1.59

2 files

1.1.58

2 files

1.1.57

2 files

1.1.55

2 files

1.1.54

2 files

1.1.52

2 files

1.1.51

2 files

1.1.48

2 files

1.1.47

2 files

1.1.46

2 files

1.1.45

2 files

1.1.44

2 files

1.1.43

2 files

1.1.42

2 files

1.1.41

2 files

1.1.40

2 files

1.1.39

2 files

1.1.38

2 files

1.1.37

2 files

1.1.35

2 files

1.1.32

2 files

1.1.31

2 files

1.1.30

2 files

1.1.29

2 files

1.1.28

2 files

1.1.27

2 files

1.1.26

2 files

1.1.25

2 files

1.1.24

2 files

1.1.23

2 files

1.1.22

2 files

1.1.21

2 files

1.1.20

2 files

1.1.19

2 files

1.1.18

2 files

1.1.17

2 files

1.1.16

2 files

1.1.15

2 files

1.1.14

2 files

1.1.13

2 files

1.1.12

2 files

1.1.11

2 files

1.1.10

2 files

1.1.9

2 files

1.1.8

2 files

1.1.7

2 files

1.1.6

2 files

1.1.5

2 files

1.1.3

2 files

1.1.2

2 files

1.1.0

2 files

1.0.100

2 files

1.0.99

2 files

1.0.98

2 files

1.0.97

2 files

1.0.96

2 files

1.0.95

2 files

1.0.94

2 files

1.0.93

2 files

1.0.92

2 files

1.0.91

2 files

1.0.90

2 files

1.0.89

2 files

1.0.88

2 files

1.0.87

2 files

1.0.86

2 files

1.0.85

2 files

1.0.84

2 files

1.0.83

2 files

1.0.82

2 files

1.0.81

2 files

1.0.80

2 files

1.0.79

2 files

1.0.78

2 files

1.0.77

2 files

1.0.76

2 files

1.0.75

2 files

1.0.74

2 files

1.0.73

2 files

1.0.72

2 files

1.0.71

2 files

1.0.70

2 files

1.0.68

2 files

1.0.67

2 files

1.0.66

2 files

1.0.65

2 files

1.0.64

2 files

1.0.63

2 files

1.0.62

2 files

1.0.61

2 files

1.0.60

2 files

1.0.59

2 files

1.0.58

2 files

1.0.57

2 files

1.0.56

2 files

1.0.55

2 files

1.0.54

2 files

1.0.53

2 files

1.0.52

2 files

1.0.51

2 files

1.0.50

2 files

1.0.49

2 files

1.0.48

2 files

1.0.47

2 files

1.0.46

2 files

1.0.45

2 files

1.0.44

2 files

1.0.43

2 files

1.0.42

2 files

1.0.41

2 files

1.0.40

2 files

1.0.39

2 files

1.0.38

2 files

1.0.37

2 files

1.0.36

2 files

1.0.35

2 files

1.0.34

2 files

1.0.32

2 files

1.0.31

2 files

1.0.30

2 files

1.0.29

2 files

1.0.28

2 files

1.0.27

2 files

1.0.26

2 files

1.0.24

2 files

1.0.23

2 files

1.0.22

2 files

1.0.21

2 files

1.0.20

2 files

1.0.19

2 files

1.0.18

2 files

1.0.17

2 files

1.0.16

2 files

1.0.15

2 files

1.0.14

2 files

1.0.13

2 files

1.0.12

2 files

1.0.11

2 files

1.0.10

2 files

1.0.9

2 files

1.0.8

2 files

1.0.7

2 files

1.0.6

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.2.58

2 files

0.2.57

2 files

0.2.56

2 files

0.2.55

2 files

0.2.54

2 files

0.2.50

2 files

0.2.49

2 files

0.2.48

2 files

0.2.47

2 files

0.2.46

2 files

0.2.45

2 files

0.2.44

2 files

0.2.43

2 files

0.2.42

2 files

0.2.41

2 files

0.2.40

2 files

0.2.39

2 files

0.2.38

2 files

0.2.37

2 files

0.2.36

2 files

0.2.35

2 files

0.2.34

2 files

0.2.33

2 files

0.2.32

2 files

0.2.31

2 files

0.2.30

2 files

0.2.29

2 files

0.2.28

2 files

0.2.27

2 files

0.2.26

2 files

0.2.25

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page