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 locally —
flash 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 and owns projects/orgs |
FLASH_STANDALONE=1 runs single-tenant on your own key |
| Hosted Multi-LoRA serving | serve.freesolo.co, deployed from flash/serving/; flash/serve/ owns the client and shared runtime |
flash serve deploy provisions serving in your own Modal account |
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
The active training catalog is Qwen/Qwen3.5-9B, Qwen/Qwen3.8-27B, and
Qwen/Qwen3.6-35B-A3B. The hosted serving catalog routes all three models on B200.
The retired dense Qwen3.6 27B identity is never translated into Qwen3.8. Customer-owned
Modal supports all three active models; 27B and 35B-A3B are bound to the certified serving image
digest. Customer-owned flash serve requires explicit --provider modal. Training and serving both use Modal.
flash login is not interactive. Export FREESOLO_API_KEY first so the key does not appear in process listings:
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-9B"
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/final # serve the final checkpoint
flash models chat RUN_ID/final -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 checkpoint id. To copy a finished adapter into your own
HuggingFace repo:
export HF_TOKEN=hf_...
flash models export --adapter-id RUN_ID/final --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/final, 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 "{\"checkpoint_id\": \"$RUN_ID/final\", \"messages\": [{\"role\": \"user\", \"content\": \"hello\"}], \"max_tokens\": 256}"
The response uses the OpenAI chat-completions shape; read choices[0].message.content. The
checkpoint id is the permanent serving identity. If that checkpoint 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.
Use flash, flash-cli, or python -m flash.cli from a checkout.
See CONTRIBUTING.md for the branching model — in short, pull requests go
into dev.
Layout
flash/core/catalog.py— curated model catalog (Qwen3.5, Qwen3.8 dense, and Qwen3.6 MoE), VRAM sizing, and each model'sthinkingcapabilityflash/schema/,flash/core/spec.py— TOML toJobSpecflash/runner/— server-side run supervisor (durable job handle, retries, cost guard)flash/providers/— GPU substrate behind thebase.Providerprotocol, withallocator.pypicking the cheapest fitting classflash/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 engineflash/envs/— environment registry and the Freesolo SDK adapterflash/serve/— serving client, shared runtime, and customer-owned provider deploymentflash/serving/— the hosted multi-LoRA Modal app behindserve.freesolo.coflash/server/— the FastAPI control plane (run via the separateflash-servercommand)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 MODAL_TOKEN_ID=...
export MODAL_TOKEN_SECRET=...
flash-server --host 0.0.0.0 --port 8080
Training and serving use Modal. Both Modal token variables are required. Training runs in GPU
Sandboxes with persisted attempt identities, checkpoint recovery, cancellation, and a shared
flash-weights Volume. GPU selection remains automatic within Modal's supported shapes.
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.
flash serve deploy provisions serving in your own Modal account and requires explicit
--provider modal. Each deployment owns one exact base model and its compatible adapters; another
base model requires another deployment. Legacy provider deployment identities are unsupported.
It implements the same contract flash/serve/ speaks; docs/serving-contract.md
documents it if you would rather write your own.
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
Metadata
Release files for freesolo-flash-dev 1.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| freesolo_flash_dev-1.3.3.tar.gz | 3.6 MB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| freesolo_flash_dev-1.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 5.3 MB
Release files / freesolo_flash_dev-1.3.3.tar.gz
| Download URL | freesolo_flash_dev-1.3.3.tar.gz |
|---|---|
| Size | 3.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba9e756066769083da2506a58c5d265f2efab8e53b91df73d01f59b3075b640a
|
|
BLAKE2b-256 checksum How to use checksums |
e3902c0ade68aed5204d95c8d1c2fa0246a1b6fc45d04d0875a479ecfc92c31d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / freesolo_flash_dev-1.3.3-py3-none-any.whl
| Download URL | freesolo_flash_dev-1.3.3-py3-none-any.whl |
|---|---|
| Size | 1.7 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4062fa4eb257291287b51597f2154bd7994c74d003e27860ed154b2cf9a8acc3
|
|
BLAKE2b-256 checksum How to use checksums |
8350b189b3ed10d553c4898d67d3f352c909a9abba95f4857dd21d773942b043
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for freesolo-flash-dev 1.3.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.