freesolo-flash-dev 0.2.54__tar.gz → 0.2.55__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/PKG-INFO +1 -1
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/training_doc.py +11 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/recipe.py +9 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/vram.py +66 -7
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/__init__.py +2 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/adapter.py +36 -3
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/lora.py +13 -85
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/opd.py +148 -9
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/opd_gkd.py +7 -5
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/rl.py +15 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/sft.py +6 -1
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_hf_artifacts.py +2 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_instance_bootstrap.py +3 -3
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/base.py +58 -1
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/jobs/__init__.py +2 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/jobs.py +6 -6
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/train/endpoints.py +1 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/jobs/__init__.py +2 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/schema/__init__.py +3 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/serve/deploy.py +5 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/spec.py +4 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/pyproject.toml +2 -2
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_allocator.py +35 -18
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_flash_worker.py +12 -1
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_gpus.py +21 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_jobs.py +36 -3
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_opd.py +345 -16
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_serve.py +25 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_spec_and_validation.py +11 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_vl_warmstart_adapter_keys.py +10 -30
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_vl_warmstart_recombine.py +27 -59
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_stack.py +67 -5
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/uv.lock +1 -1
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.env.example +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/Dockerfile +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/README.md +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/_channel.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/catalog.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/client/config.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/client/http.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/opd_vllm.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/runpod/train/deps.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/runner/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/app.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/conftest.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_client.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_export.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_opd_vllm.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_server_api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_version.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.54 → freesolo_flash_dev-0.2.55}/tests/test_worker_thinking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.55
|
|
4
4
|
Summary: Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI
|
|
5
5
|
Project-URL: Homepage, https://github.com/freesolo-co/flash
|
|
6
6
|
Project-URL: Repository, https://github.com/freesolo-co/flash
|
|
@@ -418,8 +418,18 @@ epochs = 1
|
|
|
418
418
|
max_examples = 2
|
|
419
419
|
lora_rank = 32
|
|
420
420
|
# kl_penalty_coef = 1.0 # reverse-KL scale
|
|
421
|
+
# opd_eos_loss_coef = 0.5 # terminal-EOS reinforcement; raise if the
|
|
422
|
+
# # student runs past the length cap without
|
|
423
|
+
# # stopping, 0 to disable
|
|
421
424
|
```
|
|
422
425
|
|
|
426
|
+
The cross-tokenizer reverse-KL is computed over shared decoded-text spans and so **cannot supervise
|
|
427
|
+
the zero-width stop token** — distilling toward a verbose teacher (GLM-5.2) erodes the student's
|
|
428
|
+
termination and rollouts run away to `max_completion_tokens`. `opd_eos_loss_coef` (default 0.5) adds a
|
|
429
|
+
bounded, self-limiting behaviour-cloning term that reinstates the stop signal on rollouts that
|
|
430
|
+
terminated naturally; watch `truncated_rollouts` fall and `mean_eos_logprob` rise in the run metrics.
|
|
431
|
+
Warm-starting from an SFT adapter (which already encodes termination) compounds it.
|
|
432
|
+
|
|
423
433
|
---
|
|
424
434
|
|
|
425
435
|
## GRPO knobs that matter
|
|
@@ -472,6 +482,7 @@ targeted fix rather than leaning on the reward gate to slowly select against it.
|
|
|
472
482
|
| Repetition / looping collapse | the same phrase repeats until truncation | repetition or length penalty; lower `temperature` |
|
|
473
483
|
| Overthinking / verbose reasoning | reasoning eats the whole token budget | `thinking_length_penalty_coef`; tighten the prompt |
|
|
474
484
|
| Completion truncation | answers cut off mid-thought | raise `max_completion_tokens` / `max_context_tokens` |
|
|
485
|
+
| OPD rollouts never stop (high `truncated_rollouts`) | on-policy completions run to the length cap without an EOS; raising the cap barely helps | raise `opd_eos_loss_coef` and warm-start from SFT — the reverse-KL can't supervise the stop token on its own |
|
|
475
486
|
| Unparsed / over-escaped output | reward can't read the answer | robust parser; return `0.0` on parse fail; format gate |
|
|
476
487
|
| Wrapper / markdown around structured output | prose around the JSON/answer | a format gate; `stop_sequences` |
|
|
477
488
|
| Uniform-reward groups | every rollout in a group scores the same → no gradient | shape the reward for partial credit; raise `temperature` |
|
|
@@ -70,6 +70,15 @@ class OPDConfig:
|
|
|
70
70
|
# (student_logsum - teacher_logsum) advantage. 1.0 is plain reverse KL (Thinking Machines,
|
|
71
71
|
# *On-Policy Distillation*). Overridable via [train].kl_penalty_coef.
|
|
72
72
|
kl_coef: float = 1.0
|
|
73
|
+
# Weight of the terminal-EOS behaviour-cloning term added to the reverse-KL loss. The
|
|
74
|
+
# cross-tokenizer text-span alignment CANNOT supervise the stop token — a special/eos token is
|
|
75
|
+
# zero-width (decodes to nothing), so it lands in no alignment group and the reverse-KL gives it
|
|
76
|
+
# NO gradient. Distilling only the content tokens toward a strong, verbose teacher (GLM-5.2) then
|
|
77
|
+
# erodes the student's termination and rollouts run away to the length cap (observed across OPD
|
|
78
|
+
# runs as thousands of non-terminated rollouts). This adds a bounded, self-limiting cross-entropy
|
|
79
|
+
# that pushes up log P(eos) at the position a rollout naturally terminated, restoring the stop
|
|
80
|
+
# signal the alignment cannot express. 0 disables it; overridable via [train].opd_eos_loss_coef.
|
|
81
|
+
eos_loss_coef: float = 0.5
|
|
73
82
|
|
|
74
83
|
|
|
75
84
|
@dataclass(frozen=True)
|
|
@@ -110,6 +110,19 @@ def opd_rollout_concurrency(prompts_per_step: int = 1, group_size: int = 1) -> i
|
|
|
110
110
|
return _positive_int(prompts_per_step, 1) * _positive_int(group_size, 1)
|
|
111
111
|
|
|
112
112
|
|
|
113
|
+
def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size: int = 1) -> int:
|
|
114
|
+
"""Loss microbatch size used by OPD's dense-logit GKD backward.
|
|
115
|
+
|
|
116
|
+
Keep this in lockstep with worker.opd._opd_loss_microbatch_size without importing the worker from
|
|
117
|
+
the sizing path. Small/medium catalog models run up to four loss samples per forward; 35B-class
|
|
118
|
+
models stay serial for VRAM safety.
|
|
119
|
+
"""
|
|
120
|
+
total = opd_rollout_concurrency(prompts_per_step, group_size)
|
|
121
|
+
params = float(params_b or 0.0)
|
|
122
|
+
default = 4 if params and params <= 10.0 else 1
|
|
123
|
+
return max(1, min(total, default))
|
|
124
|
+
|
|
125
|
+
|
|
113
126
|
def _resident_kv_gb(
|
|
114
127
|
params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
|
|
115
128
|
) -> float:
|
|
@@ -366,11 +379,10 @@ def estimate_vram_gb(
|
|
|
366
379
|
# OPD recipe default (thinking uses the longer max_completion_len_thinking). A GRPO-style
|
|
367
380
|
# min(seq_len, 1024) fallback would UNDER-budget a thinking opd job (1536-token completions).
|
|
368
381
|
completion = opd_completion_len(max_tokens, thinking)
|
|
369
|
-
# run_opd backprops a small loss microbatch
|
|
370
|
-
#
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
activations = _ACT_COEF * (seq_len / 1024.0) * width
|
|
382
|
+
# run_opd backprops a small loss microbatch. Budget that actual dense-logit microbatch, not
|
|
383
|
+
# the full rollout/teacher batch and not a single sequence.
|
|
384
|
+
loss_mb = opd_loss_microbatch(params_b, batch_size, group_size)
|
|
385
|
+
activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
|
|
374
386
|
# Dense-logit peak spans BOTH the forward and the loss BACKWARD (OPD has no fused CE):
|
|
375
387
|
# - forward: bf16 full-sequence logits [seq, vocab] (seq * 2)
|
|
376
388
|
# fp32 completion rows [completion, vocab] for logsumexp, saved for backward
|
|
@@ -383,8 +395,8 @@ def estimate_vram_gb(
|
|
|
383
395
|
# so a long-completion / large-vocab (248k) opd job under-budgeted the loss backward by
|
|
384
396
|
# ~(completion*4 + seq*2)*vocab bytes and could route to a GPU that OOMs in OPD loss backward
|
|
385
397
|
# (codex[bot]). Mirror the SFT dense-logit sizing by budgeting the backward buffers too.
|
|
386
|
-
logits_fwd = (seq_len * 2 + completion * 4) * vocab
|
|
387
|
-
logits_bwd = (seq_len * 2 + completion * 4) * vocab
|
|
398
|
+
logits_fwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
|
|
399
|
+
logits_bwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
|
|
388
400
|
logits = (logits_fwd + logits_bwd) / 1e9
|
|
389
401
|
return base + rollout + activations + logits
|
|
390
402
|
# Actual TRL SFT keeps fused CE disabled (see worker/sft.py), so dense logits materialize even
|
|
@@ -578,6 +590,7 @@ def model_required_vram_gb(
|
|
|
578
590
|
use_vllm: bool = True,
|
|
579
591
|
vocab: int = _VOCAB_DEFAULT,
|
|
580
592
|
active_params_b: float | None = None,
|
|
593
|
+
fp8_kv: bool = False,
|
|
581
594
|
) -> int:
|
|
582
595
|
est = estimate_vram_gb(
|
|
583
596
|
params_b,
|
|
@@ -592,10 +605,45 @@ def model_required_vram_gb(
|
|
|
592
605
|
use_vllm=use_vllm,
|
|
593
606
|
vocab=vocab,
|
|
594
607
|
active_params_b=active_params_b,
|
|
608
|
+
fp8_kv=fp8_kv,
|
|
595
609
|
sft_fused_ce=sft_fused_ce,
|
|
596
610
|
)
|
|
597
611
|
return math.ceil(est * headroom)
|
|
598
612
|
|
|
613
|
+
def _opd_fp8_adjust(
|
|
614
|
+
need: int,
|
|
615
|
+
params_b: float,
|
|
616
|
+
*,
|
|
617
|
+
quant: str = "bf16",
|
|
618
|
+
use_vllm: bool = True,
|
|
619
|
+
vocab: int = _VOCAB_DEFAULT,
|
|
620
|
+
active_params_b: float | None = None,
|
|
621
|
+
) -> int:
|
|
622
|
+
"""Re-size an OPD requirement with an fp8 KV cache once the run is provably modern-card-only.
|
|
623
|
+
|
|
624
|
+
The colocated OPD vLLM rollout engine reserves an fp8 KV cache on cc >= 8.9 hardware
|
|
625
|
+
(engine/worker/opd_vllm.py), but the estimate defaults to a bf16 KV pool. Any OPD run needing
|
|
626
|
+
more VRAM than the biggest non-fp8 validated card (the 80 GB A100) can ONLY land on a modern
|
|
627
|
+
(cc >= 8.9) card, so that bf16 pool is a phantom: it doubles the real KV and wrongly rejects
|
|
628
|
+
full-context / grouped OPD configs on the 35B that actually fit a B200. Halve it — but only
|
|
629
|
+
while the fp8-sized requirement still clears the non-fp8 ceiling, so the discount can never
|
|
630
|
+
pull the run back onto a card that would NOT use fp8 (which would then OOM)."""
|
|
631
|
+
from flash.providers.base import max_non_fp8_kv_vram_gb
|
|
632
|
+
|
|
633
|
+
ceiling = max_non_fp8_kv_vram_gb()
|
|
634
|
+
if need <= ceiling:
|
|
635
|
+
return need
|
|
636
|
+
fp8_need = _need(
|
|
637
|
+
params_b,
|
|
638
|
+
"opd",
|
|
639
|
+
quant=quant,
|
|
640
|
+
use_vllm=use_vllm,
|
|
641
|
+
vocab=vocab,
|
|
642
|
+
active_params_b=active_params_b,
|
|
643
|
+
fp8_kv=True,
|
|
644
|
+
)
|
|
645
|
+
return fp8_need if fp8_need > ceiling else need
|
|
646
|
+
|
|
599
647
|
from flash.catalog import MODELS, vocab_size_for
|
|
600
648
|
|
|
601
649
|
info = MODELS.get(model_id)
|
|
@@ -625,6 +673,15 @@ def model_required_vram_gb(
|
|
|
625
673
|
vocab=model_vocab,
|
|
626
674
|
active_params_b=active_b,
|
|
627
675
|
)
|
|
676
|
+
if is_opd:
|
|
677
|
+
need = _opd_fp8_adjust(
|
|
678
|
+
need,
|
|
679
|
+
params_b or 4.0,
|
|
680
|
+
quant=quant,
|
|
681
|
+
use_vllm=use_vllm,
|
|
682
|
+
vocab=model_vocab,
|
|
683
|
+
active_params_b=active_b,
|
|
684
|
+
)
|
|
628
685
|
floor = 0
|
|
629
686
|
if is_grpo and getattr(info, "grpo_min_vram_gb", 0):
|
|
630
687
|
floor = int(info.grpo_min_vram_gb)
|
|
@@ -690,6 +747,8 @@ def model_required_vram_gb(
|
|
|
690
747
|
# open-model OPD uses its own dense-logit + colocated-vLLM estimator. The grpo-only sequence
|
|
691
748
|
# escalation stays gated on is_grpo.
|
|
692
749
|
need = _need(params_b, algorithm, vocab=model_vocab)
|
|
750
|
+
if is_opd:
|
|
751
|
+
need = _opd_fp8_adjust(need, params_b, vocab=model_vocab)
|
|
693
752
|
if is_grpo:
|
|
694
753
|
need += grpo_seq_escalation_gb(params_b, seq_len)
|
|
695
754
|
if is_vllm_rollout:
|
|
@@ -21,6 +21,7 @@ from flash.engine.worker.adapter import (
|
|
|
21
21
|
_init_adapter_model,
|
|
22
22
|
_resolve_adapter_ref,
|
|
23
23
|
make_lora,
|
|
24
|
+
prepare_fresh_lora_base,
|
|
24
25
|
recombined_warmstart_adapter_dir,
|
|
25
26
|
require_vllm_for_rollout_func,
|
|
26
27
|
)
|
|
@@ -87,7 +88,6 @@ from flash.engine.worker.lora import (
|
|
|
87
88
|
is_vl_checkpoint,
|
|
88
89
|
patch_grpo_mask_aware_lm_head,
|
|
89
90
|
recombine_lora_adapters,
|
|
90
|
-
strip_language_model_infix,
|
|
91
91
|
)
|
|
92
92
|
from flash.engine.worker.opd import run_opd
|
|
93
93
|
from flash.engine.worker.perf import (
|
|
@@ -447,6 +447,7 @@ __all__ = [
|
|
|
447
447
|
"patch_grpo_mask_aware_lm_head",
|
|
448
448
|
"patch_trl_colocate_llm_kwargs",
|
|
449
449
|
"prefetch_model",
|
|
450
|
+
"prepare_fresh_lora_base",
|
|
450
451
|
"prompt_opens_thinking",
|
|
451
452
|
"publish_deployable_checkpoint",
|
|
452
453
|
"recombine_lora_adapters",
|
|
@@ -460,7 +461,6 @@ __all__ = [
|
|
|
460
461
|
"run_rl",
|
|
461
462
|
"run_sft",
|
|
462
463
|
"setup_perf_backends",
|
|
463
|
-
"strip_language_model_infix",
|
|
464
464
|
"strip_think",
|
|
465
465
|
"think_token_count",
|
|
466
466
|
"thinking_text",
|
|
@@ -12,6 +12,7 @@ from flash.engine.worker.lora import (
|
|
|
12
12
|
assert_adapter_delta_nonzero,
|
|
13
13
|
assert_adapter_load_clean,
|
|
14
14
|
assert_lora_applied,
|
|
15
|
+
is_vl_checkpoint,
|
|
15
16
|
recombine_lora_adapters,
|
|
16
17
|
validate_recombined_lora_rank,
|
|
17
18
|
)
|
|
@@ -20,7 +21,7 @@ from flash.engine.worker.perf import optimal_attn_impl
|
|
|
20
21
|
|
|
21
22
|
def make_lora(model_id: str | None = None):
|
|
22
23
|
"""Build LoRA config targeting all linear layers (VL models included: the vision tower /
|
|
23
|
-
projector / MTP linears are adapted too; on
|
|
24
|
+
projector / MTP linears are adapted too; on examples without images they simply get no gradient)."""
|
|
24
25
|
from peft import LoraConfig
|
|
25
26
|
|
|
26
27
|
targets = "all-linear"
|
|
@@ -43,6 +44,38 @@ def make_lora(model_id: str | None = None):
|
|
|
43
44
|
return LoraConfig(**kwargs)
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
def prepare_fresh_lora_base(
|
|
48
|
+
model_source: str,
|
|
49
|
+
model_id: str,
|
|
50
|
+
model_init_kwargs: dict,
|
|
51
|
+
*,
|
|
52
|
+
force: bool = False,
|
|
53
|
+
phase: str = "train",
|
|
54
|
+
):
|
|
55
|
+
"""Prepare the correct base object/path for fresh-LoRA training.
|
|
56
|
+
|
|
57
|
+
PEFT expands ``target_modules`` against the concrete model object it wraps. TRL's default string
|
|
58
|
+
loader can resolve Qwen VL checkpoints to a language-only tree, while OPD and serving use the full
|
|
59
|
+
image-text tree. Therefore every fresh LoRA on a VL checkpoint must preload
|
|
60
|
+
``AutoModelForImageTextToText`` so SFT/GRPO/OPD adapter tensor sets stay identical, including
|
|
61
|
+
zero-gradient vision-tower LoRA tensors on examples without images. Non-VL checkpoints keep the original
|
|
62
|
+
model path for TRL's normal loader. ``force`` is used after a VL warm-start merge already proved
|
|
63
|
+
the source adapter is VL, so a transient config-probe failure cannot send the fresh stage back to
|
|
64
|
+
a language-only loader.
|
|
65
|
+
"""
|
|
66
|
+
if not (force or is_vl_checkpoint(model_id)):
|
|
67
|
+
return model_source
|
|
68
|
+
from transformers import AutoModelForImageTextToText
|
|
69
|
+
|
|
70
|
+
print(
|
|
71
|
+
f"[{phase}] VL checkpoint: loading full multimodal model for fresh LoRA target parity "
|
|
72
|
+
f"({model_source})"
|
|
73
|
+
)
|
|
74
|
+
return AutoModelForImageTextToText.from_pretrained(
|
|
75
|
+
model_source, trust_remote_code=True, **model_init_kwargs
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
46
79
|
def require_vllm_for_rollout_func(use_rollout_func: bool, use_vllm: bool, model_id: str) -> None:
|
|
47
80
|
"""Fail fast when multi-turn GRPO needs colocated vLLM but it's disabled."""
|
|
48
81
|
if use_rollout_func and not use_vllm:
|
|
@@ -65,10 +98,10 @@ def _merge_vl_warmstart_adapter(adir: str, model_id: str, attn_kw: dict) -> str:
|
|
|
65
98
|
"""VL warm-start (#296): MERGE the SFT into the FULL multimodal base and save the merged model to a
|
|
66
99
|
fresh temp dir — the new training base for a GRPO LoRA trained from scratch. Continuing the live SFT
|
|
67
100
|
LoRA instead makes the colocated vLLM rollout AND KL reference run off the BARE base (the SFT only
|
|
68
|
-
reaches vLLM via a
|
|
101
|
+
reaches vLLM via a language/VL weight-sync that round-trips poorly for ``*ForConditionalGeneration``
|
|
69
102
|
models), so GRPO rolls out base-verbose and collapses a working SFT back to base (observed: every
|
|
70
103
|
Qwen3.5 GRPO reverts; non-VL MiniCPM does not). Merging into the full multimodal model (NOT the
|
|
71
|
-
|
|
104
|
+
language-only tree) keeps the VL config + ``language_model.*`` keys that both the trainer reload and
|
|
72
105
|
vLLM's VL loader expect; the SFT keys match here WITHOUT the infix strip. Records the SFT dir for the
|
|
73
106
|
finalize recombine and returns the merged dir."""
|
|
74
107
|
import gc
|
|
@@ -10,9 +10,9 @@ CPU-importable.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
# Natively-multimodal model types (Qwen3.5/3.6). Their LoRA adapters adapt the FULL module
|
|
13
|
-
# tree — vision tower / projector / MTP head included, like every other linear (on
|
|
13
|
+
# tree — vision tower / projector / MTP head included, like every other linear (on no-image
|
|
14
14
|
# data those get no gradient, so their lora_B stays zero-init). The engine loads and serves
|
|
15
|
-
# the whole VL model (vision tower included); there is no
|
|
15
|
+
# the whole VL model (vision tower included); there is no language-only VL adapter path.
|
|
16
16
|
_VL_MODEL_TYPES = ("qwen3_5", "qwen3_5_moe", "qwen3_6")
|
|
17
17
|
|
|
18
18
|
|
|
@@ -171,36 +171,13 @@ def disable_liger_grpo_torch_compile(trainer) -> bool:
|
|
|
171
171
|
# --------------------------------------------------------------------------------------------
|
|
172
172
|
# Warm-start (init_from_adapter) SFT-adapter key namespace for VL checkpoints.
|
|
173
173
|
#
|
|
174
|
-
# SFT
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
# ``base_model.model.model.language_model.layers.X...``. Warm-started GRPO (``_init_adapter_model``)
|
|
178
|
-
# loads the base via ``AutoModelForCausalLM``; when that resolves to the TEXT-ONLY module tree its
|
|
179
|
-
# LoRA targets are named ``base_model.model.model.layers.X...`` (no ``language_model.`` infix), so
|
|
180
|
-
# the two adapters name the SAME modules differently. ``strip_language_model_infix`` lines them up
|
|
181
|
-
# for the ``recombine_lora_adapters`` SFT⊕GRPO stacking (deploy-time), then the recombine re-emits
|
|
182
|
-
# the serving ``language_model`` namespace. ``_LANGUAGE_MODEL_INFIX`` is also the signal
|
|
183
|
-
# ``adapter_is_vl_warmstart`` reads to detect a VL warm-start adapter from its keys.
|
|
174
|
+
# SFT/GRPO/OPD now force fresh LoRA training for VL checkpoints through the FULL multimodal model so
|
|
175
|
+
# their module sets match exactly. ``_LANGUAGE_MODEL_INFIX`` is the signal
|
|
176
|
+
# ``adapter_is_vl_warmstart`` reads to detect a full-VL warm-start adapter from its keys.
|
|
184
177
|
|
|
185
178
|
_LANGUAGE_MODEL_INFIX = ".language_model."
|
|
186
179
|
|
|
187
180
|
|
|
188
|
-
def strip_language_model_infix(key: str) -> str:
|
|
189
|
-
"""Strip the FIRST ``.language_model.`` infix from a peft adapter weight key.
|
|
190
|
-
|
|
191
|
-
``base_model.model.model.language_model.layers.0.linear_attn.out_proj.lora_A.default.weight``
|
|
192
|
-
-> ``base_model.model.model.layers.0.linear_attn.out_proj.lora_A.default.weight``.
|
|
193
|
-
|
|
194
|
-
Only the first occurrence is removed (the LM-vs-VL boundary appears once in the path); keys
|
|
195
|
-
without the infix are returned unchanged.
|
|
196
|
-
"""
|
|
197
|
-
i = key.find(_LANGUAGE_MODEL_INFIX)
|
|
198
|
-
if i == -1:
|
|
199
|
-
return key
|
|
200
|
-
# Replace ".language_model." with "." (keep one separator dot).
|
|
201
|
-
return key[:i] + "." + key[i + len(_LANGUAGE_MODEL_INFIX) :]
|
|
202
|
-
|
|
203
|
-
|
|
204
181
|
# Substrings that identify a peft LoRA weight key (vs a base-model param). The whole adapter file
|
|
205
182
|
# is LoRA weights, but a wrong-arch / corrupt checkpoint can contain non-LoRA tensors, so we filter.
|
|
206
183
|
_LORA_KEY_MARKERS = (".lora_A.", ".lora_B.", ".lora_embedding_A.", ".lora_embedding_B.", "lora_")
|
|
@@ -219,7 +196,7 @@ _MAX_SAFETENSORS_HEADER_BYTES = 100 * 1024 * 1024
|
|
|
219
196
|
def _read_adapter_tensor_keys(adir: str) -> list[str] | None:
|
|
220
197
|
"""Tensor key names in the downloaded adapter.
|
|
221
198
|
|
|
222
|
-
For safetensors, read ONLY the JSON header (pure stdlib, no tensor data). For
|
|
199
|
+
For safetensors, read ONLY the JSON header (pure stdlib, no tensor data). For PEFT
|
|
223
200
|
``adapter_model.bin``, use Torch's weights-only loader and inspect the state-dict keys. Returns
|
|
224
201
|
``None`` when no adapter weights exist in ``adir``.
|
|
225
202
|
"""
|
|
@@ -292,12 +269,12 @@ def adapter_is_vl_warmstart(adir: str, model_id: str) -> bool:
|
|
|
292
269
|
|
|
293
270
|
Robust to a transient ``is_vl_checkpoint`` config-probe failure (it calls
|
|
294
271
|
``AutoConfig.from_pretrained`` and swallows EVERY exception to return False, so an HF
|
|
295
|
-
rate-limit / network hiccup / uncached config could silently route a genuine VL warm-start
|
|
296
|
-
the
|
|
297
|
-
actually carries ``.language_model.`` LoRA keys was saved against the full multimodal model and
|
|
272
|
+
rate-limit / network hiccup / uncached config could silently route a genuine VL warm-start away
|
|
273
|
+
from the full-VL merge path and reintroduce the trainer<->vLLM mismatch — issue #286). An adapter
|
|
274
|
+
that actually carries ``.language_model.`` LoRA keys was saved against the full multimodal model and
|
|
298
275
|
IS a VL warm-start regardless of the probe (the adapter's own keys are the authoritative signal).
|
|
299
276
|
Falls back to the config probe only when the adapter can't be read or carries no
|
|
300
|
-
``.language_model.`` LoRA keys
|
|
277
|
+
``.language_model.`` LoRA keys."""
|
|
301
278
|
try:
|
|
302
279
|
keys = _read_adapter_tensor_keys(adir)
|
|
303
280
|
if keys and any(_LANGUAGE_MODEL_INFIX in k for k in keys if _is_lora_key(k)):
|
|
@@ -450,39 +427,6 @@ def recombine_lora_adapters(
|
|
|
450
427
|
old_cfg, old_sd = _load(old_dir)
|
|
451
428
|
new_cfg, new_sd = _load(new_dir)
|
|
452
429
|
|
|
453
|
-
# Normalize the ``.language_model.`` infix on BOTH adapters' keys before comparing/stacking.
|
|
454
|
-
# The VL merge warm-start (#296) trains the prior adapter against the FULL multimodal model, so the
|
|
455
|
-
# warm-start adapter's keys carry the infix (``base_model.model.model.language_model.layers...``),
|
|
456
|
-
# while a fresh LoRA saved by the text-only ``AutoModelForCausalLM`` trainer has no infix
|
|
457
|
-
# (``base_model.model.model.layers...``). Without this, the equivalent LM modules compare as
|
|
458
|
-
# DIFFERENT targets and the recombine wrongly aborts. The normalized form is only an INTERNAL math
|
|
459
|
-
# key: if either source adapter used the VL ``language_model`` namespace for a tensor, the
|
|
460
|
-
# recombined output uses that same serving-compatible key instead of the stripped text-only key.
|
|
461
|
-
def _normalize_infix(sd, which):
|
|
462
|
-
norm: dict = {}
|
|
463
|
-
infixed_keys: dict[str, str] = {}
|
|
464
|
-
for k, v in sd.items():
|
|
465
|
-
nk = strip_language_model_infix(k)
|
|
466
|
-
# Fail closed on ANY post-normalization collision. A malformed adapter carrying BOTH the
|
|
467
|
-
# infixed and the already-text-only form of a key would otherwise let the second write
|
|
468
|
-
# silently overwrite the first (the text-only key has nk == k, so a ``nk != k`` guard
|
|
469
|
-
# would skip it) — recombining whichever duplicate wins instead of rejecting the mix.
|
|
470
|
-
if nk in norm:
|
|
471
|
-
raise ValueError(
|
|
472
|
-
f"recombine: {which} adapter key {k!r} collides with another after stripping "
|
|
473
|
-
"the '.language_model.' infix — cannot normalize a mixed VL adapter"
|
|
474
|
-
)
|
|
475
|
-
norm[nk] = v
|
|
476
|
-
if _LANGUAGE_MODEL_INFIX in k:
|
|
477
|
-
infixed_keys[nk] = k
|
|
478
|
-
return norm, infixed_keys
|
|
479
|
-
|
|
480
|
-
old_sd, old_infixed_keys = _normalize_infix(old_sd, "warm-start")
|
|
481
|
-
new_sd, new_infixed_keys = _normalize_infix(new_sd, "fresh")
|
|
482
|
-
|
|
483
|
-
def _output_key(k: str) -> str:
|
|
484
|
-
return old_infixed_keys.get(k) or new_infixed_keys.get(k) or k
|
|
485
|
-
|
|
486
430
|
for name, cfg in (("warm-start", old_cfg), ("fresh", new_cfg)):
|
|
487
431
|
# peft_type defaults to LORA for older configs that omit it; anything else (e.g. ADALORA)
|
|
488
432
|
# has different tensor/scale semantics the cat-recombine math doesn't model.
|
|
@@ -583,15 +527,8 @@ def recombine_lora_adapters(
|
|
|
583
527
|
f"recombine: lora_A key {ak!r} has no matching lora_B key {bk!r} — the adapter is "
|
|
584
528
|
"malformed (unpaired LoRA tensors)"
|
|
585
529
|
)
|
|
586
|
-
out_ak = _output_key(ak)
|
|
587
|
-
out_bk = _output_key(bk)
|
|
588
|
-
if out_ak in out or out_bk in out:
|
|
589
|
-
raise ValueError(
|
|
590
|
-
"recombine: output key collision while restoring the '.language_model.' infix "
|
|
591
|
-
f"(A={out_ak!r}, B={out_bk!r})"
|
|
592
|
-
)
|
|
593
530
|
# A: (r, in_features) — stacked along the rank axis (no scaling; scale lives on B).
|
|
594
|
-
out[
|
|
531
|
+
out[ak] = torch.cat([old_sd[ak], new_sd[ak]], dim=0).contiguous()
|
|
595
532
|
# B: (out_features, r) — bake each adapter's own scale, then stack along the rank axis. The
|
|
596
533
|
# combined adapter carries unit scale, so the per-component scales survive intact.
|
|
597
534
|
# Promote to the higher of the two input dtypes (don't force the warm-start's, which would
|
|
@@ -600,16 +537,7 @@ def recombine_lora_adapters(
|
|
|
600
537
|
dt = torch.promote_types(old_sd[bk].dtype, new_sd[bk].dtype)
|
|
601
538
|
b_old = old_sd[bk].to(torch.float32) * s_old
|
|
602
539
|
b_new = new_sd[bk].to(torch.float32) * s_new
|
|
603
|
-
out[
|
|
604
|
-
|
|
605
|
-
if old_infixed_keys or new_infixed_keys:
|
|
606
|
-
plain_layer_keys = sorted(k for k in out if k.startswith("base_model.model.model.layers."))
|
|
607
|
-
if plain_layer_keys:
|
|
608
|
-
raise ValueError(
|
|
609
|
-
"recombine: VL warm-start output would use plain model.layers LoRA keys "
|
|
610
|
-
f"(e.g. {plain_layer_keys[:3]}). Serving expects the language_model namespace; "
|
|
611
|
-
"refusing to write a known-bad deploy artifact."
|
|
612
|
-
)
|
|
540
|
+
out[bk] = torch.cat([b_old, b_new], dim=1).to(dt).contiguous()
|
|
613
541
|
|
|
614
542
|
out_cfg = dict(new_cfg)
|
|
615
543
|
out_cfg["r"] = r_out
|
|
@@ -619,7 +547,7 @@ def recombine_lora_adapters(
|
|
|
619
547
|
out_cfg["alpha_pattern"] = {}
|
|
620
548
|
# The fresh adapter was trained on the ephemeral warm-start-merged temp dir, so out_cfg (copied
|
|
621
549
|
# from new_cfg) still names that temp path as base_model_name_or_path. Replace it with the real
|
|
622
|
-
# catalog base from the warm-start config; if that config carries none (e.g. an external
|
|
550
|
+
# catalog base from the warm-start config; if that config carries none (e.g. an external
|
|
623
551
|
# adapter), DROP the field rather than ship a deployed config pointing at a now-deleted temp dir.
|
|
624
552
|
old_base = old_cfg.get("base_model_name_or_path")
|
|
625
553
|
if old_base:
|