freesolo-flash-dev 1.0.7__tar.gz → 1.0.9__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-1.0.7 → freesolo_flash_dev-1.0.9}/.env.example +1 -1
- freesolo_flash_dev-1.0.9/AB_PLAN.md +86 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/PKG-INFO +3 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/README.md +2 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/__init__.py +1 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/training_doc.py +12 -9
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cost/analytical.py +16 -5
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cost/spec.py +8 -9
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cost/types.py +15 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/diagnostics.py +11 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/vram.py +104 -24
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/gpu_setup.py +74 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/heartbeat.py +93 -27
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/hf.py +101 -5
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd.py +39 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_vllm.py +120 -20
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rl.py +110 -62
- freesolo_flash_dev-1.0.9/flash/engine/worker/rollout_samples.py +148 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/sft.py +122 -61
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/lora_rank.py +66 -15
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_poll.py +55 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/jobs.py +32 -10
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/endpoints.py +10 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/api.py +8 -4
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/runner/__init__.py +47 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/runner/lifecycle.py +7 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/schema/__init__.py +4 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/schema/fields.py +10 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/serve/deploy.py +1 -1
- freesolo_flash_dev-1.0.9/flash/serve/preflight.py +117 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/routes/runs.py +3 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/routes/serving.py +26 -10
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/spec.py +34 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_allocator.py +85 -65
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_async_uploads.py +55 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_b200_rollout_opt.py +7 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_chalk_kernels.py +1 -6
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_estimate.py +17 -14
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_help.py +1 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_estimate.py +31 -6
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_flash_worker.py +9 -10
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_gpus.py +2 -3
- freesolo_flash_dev-1.0.9/tests/test_grpo_cudagraphs.py +101 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_grpo_params.py +76 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_jobs.py +156 -0
- freesolo_flash_dev-1.0.9/tests/test_multi_gpu_gate.py +83 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_opd.py +47 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_opd_vllm.py +191 -3
- freesolo_flash_dev-1.0.9/tests/test_rollout_samples.py +606 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_api.py +279 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_envs_coverage.py +63 -2
- freesolo_flash_dev-1.0.9/tests/test_sft_chunked_nll.py +292 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_spec_and_validation.py +51 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_train_context_preflight.py +24 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vast_api.py +12 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_init_heartbeat.py +34 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/catalog.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_multimodal.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_grpo_perturn.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_image_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_per_turn_reward.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_request_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_sft_activation_shaping.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_sft_tokenize_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_single_turn_multimodal_grpo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.9}/tests/test_worker_thinking.py +0 -0
|
@@ -31,4 +31,4 @@ FREESOLO_BASE_URL=http://backend:8000
|
|
|
31
31
|
# Modal app that serves every adapter on one GPU per base model, scaling to zero when
|
|
32
32
|
# idle). `flash deploy`/`undeploy`/`chat` register/deregister/chat against it. Defaults
|
|
33
33
|
# to the hosted Modal URL when unset.
|
|
34
|
-
FREESOLO_SERVING_URL=https://
|
|
34
|
+
FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# OPD rollout-memory utilization A/B plan
|
|
2
|
+
|
|
3
|
+
## Question
|
|
4
|
+
|
|
5
|
+
Does sizing the colocated vLLM executor from measured post-load free VRAM, while reserving the modeled OPD training peak and allocator margin, eliminate the Qwen3.5 4B negative-KV startup failure and improve rollout throughput without changing training behavior or causing an OOM?
|
|
6
|
+
|
|
7
|
+
No paid GPU run is part of this change.
|
|
8
|
+
|
|
9
|
+
## Frozen comparison
|
|
10
|
+
|
|
11
|
+
- control: `origin/dev` at `8fdfc858`
|
|
12
|
+
- treatment: branch `perf/opd-rollout-util`
|
|
13
|
+
- hardware: one H100 80 GB for each arm, from the same RunPod GPU type and image
|
|
14
|
+
- launch both arms concurrently on separate matched GPUs
|
|
15
|
+
- use the same base checkpoint, dataset revision, environment revision, seed, teacher, token budget, batch order, and worker image inputs
|
|
16
|
+
- use immutable commits for both arms
|
|
17
|
+
|
|
18
|
+
An H100 80 GB is the smallest card for this A/B. The reported failure is a large-card utilization defect, with roughly 60 GB left idle and a negative-KV startup failure for Qwen3.5 4B. An RTX 4090 24 GB does not have enough residual memory to expose the intended utilization increase and therefore cannot demonstrate the optimization.
|
|
19
|
+
|
|
20
|
+
## OPD configuration
|
|
21
|
+
|
|
22
|
+
Use the existing reproducing environment and dataset with this training shape held constant:
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
model = "Qwen/Qwen3.5-4B"
|
|
26
|
+
algorithm = "opd"
|
|
27
|
+
seed = 42
|
|
28
|
+
|
|
29
|
+
[environment]
|
|
30
|
+
id = "<frozen-reproduction-environment>"
|
|
31
|
+
|
|
32
|
+
[train]
|
|
33
|
+
epochs = 1
|
|
34
|
+
batch_size = 8
|
|
35
|
+
group_size = 1
|
|
36
|
+
max_context_tokens = 1536
|
|
37
|
+
max_completion_tokens = 512
|
|
38
|
+
lora_rank = 32
|
|
39
|
+
teacher_model = "glm-5.2"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the same bounded number of optimizer steps in both arms. Do not tune either arm after launch.
|
|
43
|
+
|
|
44
|
+
## Measurements
|
|
45
|
+
|
|
46
|
+
Record at vLLM initialization and for every rollout step:
|
|
47
|
+
|
|
48
|
+
1. measured free and total VRAM immediately before vLLM construction
|
|
49
|
+
2. modeled OPD training peak reserve, allocator margin, rollout budget, and final `gpu_memory_utilization`
|
|
50
|
+
3. vLLM executor budget in GiB and available KV-cache budget or cache blocks
|
|
51
|
+
4. rollout batch size and generated tokens per second
|
|
52
|
+
5. peak allocated and peak reserved VRAM across the OPD loss forward/backward
|
|
53
|
+
6. initialization outcome, negative-KV/cache-block errors, CUDA OOMs, and worker health
|
|
54
|
+
7. per-step OPD loss, reward or task metric already emitted by the environment, and completed optimizer steps
|
|
55
|
+
|
|
56
|
+
## Gates
|
|
57
|
+
|
|
58
|
+
### Feasibility and memory safety
|
|
59
|
+
|
|
60
|
+
- treatment must initialize vLLM and complete every planned optimizer step with zero negative-KV/cache-block failures and zero CUDA OOMs
|
|
61
|
+
- treatment executor budget must never exceed measured free VRAM minus the logged training reserve and allocator margin
|
|
62
|
+
- treatment loss forward/backward peak must retain at least the allocator margin after accounting for the executor budget
|
|
63
|
+
- if the control reproduces the startup crash, treatment must complete the same configuration; otherwise the A/B is inconclusive on crash recovery but can still evaluate utilization and throughput
|
|
64
|
+
|
|
65
|
+
### Utilization and throughput
|
|
66
|
+
|
|
67
|
+
- direction: treatment `gpu_memory_utilization` and KV budget must be higher than control on the H100
|
|
68
|
+
- threshold: treatment executor budget must increase by at least 16 GiB or utilization by at least 0.20 absolute
|
|
69
|
+
- direction: treatment rollout generated tokens per second must be no worse than control
|
|
70
|
+
- threshold: when both arms complete, treatment median steady-state rollout throughput must improve by at least 10 percent; if control crashes, report treatment throughput without claiming a relative speedup
|
|
71
|
+
|
|
72
|
+
### Training parity
|
|
73
|
+
|
|
74
|
+
- both arms must consume the same prompts and completion-token budget and complete the same optimizer-step count
|
|
75
|
+
- per-checkpoint OPD loss curves must remain within normal deterministic/runtime noise, with no sustained treatment regression above 2 percent relative to control
|
|
76
|
+
- final task metric must not regress by more than 1 percentage point absolute
|
|
77
|
+
- any change in gradients, optimizer state, sampled inputs, or training math invalidates the A/B
|
|
78
|
+
|
|
79
|
+
## Decision
|
|
80
|
+
|
|
81
|
+
Accept the optimization only if the treatment passes all memory-safety and training-parity gates and either:
|
|
82
|
+
|
|
83
|
+
1. recovers the exact control startup failure, or
|
|
84
|
+
2. meets both the executor-budget and throughput thresholds when both arms complete.
|
|
85
|
+
|
|
86
|
+
Reject or revise if treatment OOMs, violates the protected-memory budget, changes the training curve beyond the parity threshold, or fails to increase the H100 rollout budget materially.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
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
|
|
@@ -186,12 +186,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
|
|
|
186
186
|
with a hint to deploy first.
|
|
187
187
|
|
|
188
188
|
Operators can also call the serving backend directly after the adapter is registered.
|
|
189
|
-
The default serving backend is `https://
|
|
189
|
+
The default serving backend is `https://serve.freesolo.co`, and
|
|
190
190
|
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
191
191
|
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
192
192
|
|
|
193
193
|
```bash
|
|
194
|
-
export FREESOLO_SERVING_URL=https://
|
|
194
|
+
export FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
195
195
|
|
|
196
196
|
curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
|
|
197
197
|
-H "Content-Type: application/json" \
|
|
@@ -129,12 +129,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
|
|
|
129
129
|
with a hint to deploy first.
|
|
130
130
|
|
|
131
131
|
Operators can also call the serving backend directly after the adapter is registered.
|
|
132
|
-
The default serving backend is `https://
|
|
132
|
+
The default serving backend is `https://serve.freesolo.co`, and
|
|
133
133
|
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
134
134
|
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
export FREESOLO_SERVING_URL=https://
|
|
137
|
+
export FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
138
138
|
|
|
139
139
|
curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
|
|
140
140
|
-H "Content-Type: application/json" \
|
|
@@ -162,7 +162,7 @@ class _FlashParser(_ThemedParser):
|
|
|
162
162
|
f"train after publishing: `{CLI_NAME} env push --name my-env .`, "
|
|
163
163
|
f"then `{CLI_NAME} train configs/sft.toml`",
|
|
164
164
|
f"any command in depth: `{CLI_NAME} <command> --help`",
|
|
165
|
-
"docs: https://freesolo.co
|
|
165
|
+
"docs: https://docs.freesolo.co",
|
|
166
166
|
]
|
|
167
167
|
return render.help_page(
|
|
168
168
|
"managed LoRA post-training", usage, _HELP_GROUPS, _HELP_OPTIONS, footers
|
|
@@ -8,7 +8,7 @@ TRAINING_MD = r"""# TRAINING.md — how to actually improve a model with Flash
|
|
|
8
8
|
> `flash env setup` dropped this file next to your `environment.py` and `configs/`.
|
|
9
9
|
> It is the playbook Freesolo's own training agents follow to turn a *finished*
|
|
10
10
|
> run into a model that *measurably improved*. The mechanics live in the hosted
|
|
11
|
-
> docs (https://freesolo.co
|
|
11
|
+
> docs (https://docs.freesolo.co); this file is the judgment that sits on top of them.
|
|
12
12
|
|
|
13
13
|
A run that reaches `done` is **not** the same as a run that worked. Submitting a run
|
|
14
14
|
is not a result. The whole job is to design the learning signal, read what the run
|
|
@@ -277,13 +277,16 @@ spending another GPU run:
|
|
|
277
277
|
- **Judge the trend, not a single number.** The proof of training is the curve:
|
|
278
278
|
loss falling (SFT) or `reward` rising over steps (GRPO). Record the base/early
|
|
279
279
|
value and the final value. A flat or noisy trend with no improvement is not success.
|
|
280
|
-
- **Read the model's outputs, not just the metrics.** A rising reward
|
|
281
|
-
reward-hacking or a degenerate output the
|
|
282
|
-
establish that the model got better.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
- **Read the model's outputs, not just the metrics.** A rising reward (or falling loss)
|
|
281
|
+
can come from reward-hacking or a degenerate output the metric still credits — metrics
|
|
282
|
+
alone never establish that the model got better. For GRPO and OPD, `flash log` surfaces a
|
|
283
|
+
handful of full (untruncated) sample completions at the heartbeat cadence — GRPO shows
|
|
284
|
+
each completion's reward, OPD its distillation loss — with the first sample-bearing update
|
|
285
|
+
forced through, so you can catch skipped reasoning or a parroted prompt placeholder by
|
|
286
|
+
step 1-2. These are bounded diagnostics, not every rollout or a held-out
|
|
287
|
+
evaluation, so still **deploy the adapter and probe it**: `flash deploy <run-id>` then
|
|
288
|
+
`flash chat <run-id> -m "..."` on at least a few real inputs, including ones it should
|
|
289
|
+
get wrong.
|
|
287
290
|
|
|
288
291
|
```bash
|
|
289
292
|
flash status <run-id> # state + accrued cost
|
|
@@ -710,5 +713,5 @@ flash export --adapter-id <run-id> --repository <you>/<repo> # export final ada
|
|
|
710
713
|
flash export --adapter-id <run-id>/step-N --repository <you>/<repo> # export a checkpoint
|
|
711
714
|
```
|
|
712
715
|
|
|
713
|
-
See the full reference at https://freesolo.co
|
|
716
|
+
See the full reference at https://docs.freesolo.co.
|
|
714
717
|
"""
|
|
@@ -166,7 +166,9 @@ def seconds_per_step(config: RunConfig, gpu: str) -> float:
|
|
|
166
166
|
# sequence (see _opd_step_shape), not completion-only, or long-prompt opd is underquoted.
|
|
167
167
|
completions, seq_tokens = _opd_step_shape(n)
|
|
168
168
|
gen_s = (GRPO_GEN_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (peak * MFU_DECODE)
|
|
169
|
-
update_s = (OPD_UPDATE_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (
|
|
169
|
+
update_s = (OPD_UPDATE_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (
|
|
170
|
+
peak * update_mfu
|
|
171
|
+
)
|
|
170
172
|
teacher_lat = teacher_seconds_per_completion()
|
|
171
173
|
# run_opd's primary path scores a step's completions CONCURRENTLY over Fireworks with a fan-out
|
|
172
174
|
# cap of the step's OWN completion count (prompts_per_step * group_size, opd.py Phase 2), so
|
|
@@ -191,6 +193,7 @@ def seconds_per_step(config: RunConfig, gpu: str) -> float:
|
|
|
191
193
|
) # ceil: a partial wave still costs one latency
|
|
192
194
|
return overhead + gen_s + reward_s + update_s
|
|
193
195
|
|
|
196
|
+
|
|
194
197
|
def sft_seconds_for_tokens(config: RunConfig, gpu: str, train_tokens: float) -> float:
|
|
195
198
|
"""SFT steady-state wall time for an actual token count on ``gpu``."""
|
|
196
199
|
n = config.normalized()
|
|
@@ -276,7 +279,11 @@ def _notes(
|
|
|
276
279
|
def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S) -> CostEstimate:
|
|
277
280
|
"""Deterministic pre-flight cost calculation."""
|
|
278
281
|
# Billing cap: mirror the runner's max(60, max_wall_seconds) floor so a sub-60s cap isn't underpriced.
|
|
279
|
-
cap_s =
|
|
282
|
+
cap_s = (
|
|
283
|
+
max(60.0, float(config.max_wall_seconds))
|
|
284
|
+
if config.max_wall_seconds is not None
|
|
285
|
+
else wall_cap_s
|
|
286
|
+
)
|
|
280
287
|
# Vast market duration filter: price against offers that outlast the run, using the SAME semantics
|
|
281
288
|
# ``usable_offers`` applies at LAUNCH (not the 60s-floored billing cap_s) — a non-positive wall means
|
|
282
289
|
# NO filter, a positive one is floored at 60s by usable_offers itself:
|
|
@@ -331,7 +338,9 @@ def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S)
|
|
|
331
338
|
compile_s = compile_seconds(config, gpu)
|
|
332
339
|
raw_train = compile_s + config.steps * sps + required_save_s
|
|
333
340
|
if not config.is_grpo and config.train_tokens is not None:
|
|
334
|
-
raw_train =
|
|
341
|
+
raw_train = (
|
|
342
|
+
compile_s + sft_seconds_for_tokens(config, gpu, config.train_tokens) + required_save_s
|
|
343
|
+
)
|
|
335
344
|
sps = raw_train / config.steps
|
|
336
345
|
|
|
337
346
|
# The cap is on total elapsed wall; setup is reported but not billed, so only training
|
|
@@ -367,9 +376,11 @@ def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S)
|
|
|
367
376
|
train_seconds=train,
|
|
368
377
|
wall_clock_seconds=wall,
|
|
369
378
|
wall_capped=wall_capped,
|
|
379
|
+
gpu_count=config.gpu_count,
|
|
370
380
|
# total_usd is the customer gpu charge. the platform-owned teacher spend is itemized
|
|
371
|
-
# only as a diagnostic and is not passed through to the customer.
|
|
372
|
-
|
|
381
|
+
# only as a diagnostic and is not passed through to the customer. an n-card job occupies
|
|
382
|
+
# n cards for the billed training wall, so the charge scales linearly with gpu_count.
|
|
383
|
+
total_usd=train / 3600.0 * hourly * config.gpu_count,
|
|
373
384
|
teacher_api_usd=teacher_api_usd,
|
|
374
385
|
notes=_notes(config, raw_train, wall_capped, cap_s),
|
|
375
386
|
)
|
|
@@ -88,19 +88,17 @@ def _on_policy_prompts_per_step(spec, examples: int) -> int:
|
|
|
88
88
|
def _sft_realized_batch(spec) -> int:
|
|
89
89
|
from flash.catalog import resolve_vocab_size
|
|
90
90
|
from flash.engine.recipe import RECIPE
|
|
91
|
-
from flash.engine.vram import
|
|
91
|
+
from flash.engine.vram import sft_chunked_nll_enabled, sft_realized_batch
|
|
92
92
|
|
|
93
93
|
t = spec.train
|
|
94
94
|
requested_batch = int(t.batch_size) if t.batch_size is not None else RECIPE.sft.effective_batch
|
|
95
95
|
sft_seq = _sft_seq_len(spec)
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
resolve_params_b(spec.model, revision=spec.model_revision), sft_seq
|
|
103
|
-
)
|
|
96
|
+
# mirror the worker's validated chunked-nll model set so pricing uses the same realized batch.
|
|
97
|
+
# every allowlisted model is a gdn hybrid; at packable context lengths the worker may force
|
|
98
|
+
# per-device=1, so price the requested batch rather than the larger chunked-nll rounded batch.
|
|
99
|
+
sft_fused = sft_chunked_nll_enabled(spec.model)
|
|
100
|
+
if sft_fused and sft_seq <= 16_384:
|
|
101
|
+
return requested_batch
|
|
104
102
|
return sft_realized_batch(
|
|
105
103
|
requested_batch,
|
|
106
104
|
seq_len=sft_seq,
|
|
@@ -167,6 +165,7 @@ def runconfig_from_spec(spec) -> RunConfig:
|
|
|
167
165
|
exact_type=g.exact_type,
|
|
168
166
|
model_revision=spec.model_revision,
|
|
169
167
|
disk_gb=float(getattr(g, "disk_gb", 0.0) or 0.0),
|
|
168
|
+
gpu_count=g.count,
|
|
170
169
|
max_wall_seconds=g.max_wall_seconds,
|
|
171
170
|
environment=spec.environment.id or None,
|
|
172
171
|
save_at_steps=t.save_at_steps,
|
|
@@ -46,6 +46,9 @@ class RunConfig:
|
|
|
46
46
|
# Spec gpu.disk_gb, carried so an exact-auto quote allocates at the run's real disk floor (parity
|
|
47
47
|
# with the launch allocate call), keeping the persisted quote aligned with the pinned hardware.
|
|
48
48
|
disk_gb: float = 0.0
|
|
49
|
+
# Spec gpu.count: cards the job occupies. total cost scales linearly with it (n cards for the
|
|
50
|
+
# billed training wall); 1 = the historical single-gpu quote.
|
|
51
|
+
gpu_count: int = 1
|
|
49
52
|
|
|
50
53
|
def __post_init__(self) -> None:
|
|
51
54
|
object.__setattr__(self, "method", normalize_algorithm(self.method))
|
|
@@ -71,6 +74,14 @@ class RunConfig:
|
|
|
71
74
|
object.__setattr__(self, "model_revision", self.model_revision.strip())
|
|
72
75
|
if self.steps < 1:
|
|
73
76
|
raise ValueError(f"steps must be >= 1, got {self.steps}")
|
|
77
|
+
if isinstance(self.gpu_count, bool) or not isinstance(self.gpu_count, int):
|
|
78
|
+
raise TypeError("gpu_count must be an integer")
|
|
79
|
+
if self.gpu_count < 1:
|
|
80
|
+
raise ValueError(f"gpu_count must be >= 1, got {self.gpu_count}")
|
|
81
|
+
# upper bound mirrors GpuSpec's 1..8 so a direct RunConfig(gpu_count=...) cannot price a
|
|
82
|
+
# count the spec layer would reject.
|
|
83
|
+
if self.gpu_count > 8:
|
|
84
|
+
raise ValueError(f"gpu_count must be <= 8, got {self.gpu_count}")
|
|
74
85
|
# Reject 0/negative positive-only knobs (bogus quote). max_wall_seconds is NOT here: the
|
|
75
86
|
# runner floors it to max(60, ...) and estimate_cost mirrors that, so a non-positive cap
|
|
76
87
|
# is accepted (floored to 60s), not rejected.
|
|
@@ -173,6 +184,8 @@ class CostEstimate:
|
|
|
173
184
|
wall_clock_seconds: float
|
|
174
185
|
wall_capped: bool
|
|
175
186
|
total_usd: float
|
|
187
|
+
# cards the job occupies; total_usd already reflects n-card billing. 1 = single-gpu quote.
|
|
188
|
+
gpu_count: int = 1
|
|
176
189
|
# opd only: external fireworks teacher token spend (0.0 for sft/grpo). billed by fireworks
|
|
177
190
|
# to the platform-managed teacher key (users don't supply one), tracked separately from the
|
|
178
191
|
# platform-billed gpu charge, so it is not part of total_usd and is shown as its own itemized
|
|
@@ -192,9 +205,9 @@ class CostEstimate:
|
|
|
192
205
|
"""Multi-line itemized breakdown for CLI output."""
|
|
193
206
|
lines = [
|
|
194
207
|
f"Run : {self.model_id} [{self.method.upper()}, {self.steps} steps]",
|
|
195
|
-
f"GPU : {self.gpu} "
|
|
208
|
+
f"GPU : {f'{self.gpu_count}x ' if self.gpu_count > 1 else ''}{self.gpu} "
|
|
196
209
|
f"({self.gpu_vram_gb} GB; run needs >= {self.required_vram_gb} GB) "
|
|
197
|
-
f"@ ${self.gpu_hourly_usd:.2f}/hr",
|
|
210
|
+
f"@ ${self.gpu_hourly_usd:.2f}/hr{' per card' if self.gpu_count > 1 else ''}",
|
|
198
211
|
f"Setup : {self.setup_seconds / 60:.1f} min (cold start: boot + deps + model load"
|
|
199
212
|
+ (" + vLLM init" if self.method == "grpo" else "")
|
|
200
213
|
+ "; not billed)",
|
|
@@ -39,3 +39,14 @@ def sanitize_diagnostic(value: Any, *, limit: int = 2000) -> str:
|
|
|
39
39
|
text = _BEARER_RE.sub("Bearer <redacted>", text)
|
|
40
40
|
text = _SECRET_KEY_RE.sub(lambda match: f"{match.group(1)}{match.group(2)}<redacted>", text)
|
|
41
41
|
return text[: max(0, int(limit))]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def neutralize_control_chars(value: Any) -> str:
|
|
45
|
+
"""Escape terminal control characters while preserving newlines as separators."""
|
|
46
|
+
text = str(value)
|
|
47
|
+
return "".join(
|
|
48
|
+
char
|
|
49
|
+
if char == "\n" or 0x20 <= ord(char) < 0x7F or ord(char) >= 0xA0
|
|
50
|
+
else f"\\x{ord(char):02x}"
|
|
51
|
+
for char in text
|
|
52
|
+
)
|
|
@@ -27,6 +27,9 @@ _BYTES_PER_PARAM = {
|
|
|
27
27
|
_BASE_OVERHEAD_GB = 4.0
|
|
28
28
|
_ACT_COEF = 0.12
|
|
29
29
|
_SFT_PER_DEVICE_BS_DEFAULT = 4
|
|
30
|
+
_VOCAB_DEFAULT = 248_320
|
|
31
|
+
_OPD_TRAINING_RESERVE_MULTIPLIER = 1.15
|
|
32
|
+
_OPD_ALLOCATOR_MARGIN_MAX_GB = 6.0
|
|
30
33
|
OPD_CE_CHUNK_SIZE = 64
|
|
31
34
|
_OPD_CE_PEAK_BYTES_PER_LOGIT = 16
|
|
32
35
|
|
|
@@ -126,6 +129,75 @@ def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size:
|
|
|
126
129
|
return max(1, min(total, default))
|
|
127
130
|
|
|
128
131
|
|
|
132
|
+
def opd_training_peak_gb(
|
|
133
|
+
params_b: float,
|
|
134
|
+
seq_len: int,
|
|
135
|
+
*,
|
|
136
|
+
max_tokens: int | None = None,
|
|
137
|
+
prompts_per_step: int = 1,
|
|
138
|
+
group_size: int = 1,
|
|
139
|
+
thinking: bool = False,
|
|
140
|
+
vocab: int = _VOCAB_DEFAULT,
|
|
141
|
+
active_params_b: float | None = None,
|
|
142
|
+
) -> float:
|
|
143
|
+
"""Additional OPD loss-forward/backward memory above the resident student model."""
|
|
144
|
+
eff_b = float(active_params_b) if active_params_b else float(params_b)
|
|
145
|
+
width = math.sqrt(max(eff_b, 0.1))
|
|
146
|
+
loss_mb = opd_loss_microbatch(params_b, prompts_per_step, group_size)
|
|
147
|
+
activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
|
|
148
|
+
completion = opd_completion_len(max_tokens, thinking)
|
|
149
|
+
# the backward peak holds both full-sequence bf16 logits and fp32 completion rows plus gradients.
|
|
150
|
+
dense_logits = 2 * loss_mb * (seq_len * 2 + completion * 4) * vocab / 1e9
|
|
151
|
+
return activations + dense_logits
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def opd_training_reserve_gb(
|
|
155
|
+
params_b: float,
|
|
156
|
+
seq_len: int,
|
|
157
|
+
*,
|
|
158
|
+
max_tokens: int | None = None,
|
|
159
|
+
prompts_per_step: int = 1,
|
|
160
|
+
group_size: int = 1,
|
|
161
|
+
thinking: bool = False,
|
|
162
|
+
vocab: int = _VOCAB_DEFAULT,
|
|
163
|
+
active_params_b: float | None = None,
|
|
164
|
+
) -> float:
|
|
165
|
+
"""OPD loss-forward/backward peak with the runtime safety multiplier."""
|
|
166
|
+
return _OPD_TRAINING_RESERVE_MULTIPLIER * opd_training_peak_gb(
|
|
167
|
+
params_b,
|
|
168
|
+
seq_len,
|
|
169
|
+
max_tokens=max_tokens,
|
|
170
|
+
prompts_per_step=prompts_per_step,
|
|
171
|
+
group_size=group_size,
|
|
172
|
+
thinking=thinking,
|
|
173
|
+
vocab=vocab,
|
|
174
|
+
active_params_b=active_params_b,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def opd_allocator_margin_gb(total_vram_gb: float) -> float:
|
|
179
|
+
"""Allocator fragmentation slack protected by the OPD startup budget."""
|
|
180
|
+
return max(2.0, min(_OPD_ALLOCATOR_MARGIN_MAX_GB, float(total_vram_gb) * 0.05))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def opd_post_init_reserve_gb(params_b: float, lora_rank: int) -> float:
|
|
184
|
+
"""Pending LoRA gradient, AdamW state, and persistent post-init growth above model residency.
|
|
185
|
+
|
|
186
|
+
OPD trains all linear layers with bf16 LoRA parameters. PyTorch AdamW lazily allocates two
|
|
187
|
+
parameter-dtype moment tensors on the first step, so each estimated trainable parameter needs
|
|
188
|
+
2 bytes for its pending gradient plus 4 bytes for optimizer state. Use total model parameters for
|
|
189
|
+
the rank-linear geometry so MoE experts are not undercounted, then reserve the measured 35B-class
|
|
190
|
+
first-generation and post-init growth with a size-scaled 12 GB ceiling.
|
|
191
|
+
"""
|
|
192
|
+
model_params_b = max(0.1, float(params_b))
|
|
193
|
+
rank = max(1, int(lora_rank))
|
|
194
|
+
trainable_params_b = (rank / 16.0) * (0.05 + model_params_b / 150.0)
|
|
195
|
+
gradient_gb = trainable_params_b * 2.0
|
|
196
|
+
adamw_state_gb = trainable_params_b * 4.0
|
|
197
|
+
persistent_growth_gb = max(1.0, min(12.0, model_params_b * 0.35))
|
|
198
|
+
return gradient_gb + adamw_state_gb + persistent_growth_gb
|
|
199
|
+
|
|
200
|
+
|
|
129
201
|
def _resident_kv_gb(
|
|
130
202
|
params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
|
|
131
203
|
) -> float:
|
|
@@ -224,21 +296,30 @@ _VLLM_COLOCATE_FLOOR_GB = 28.0
|
|
|
224
296
|
# init time. Keep 2B+ OPD off <=40 GB classes so the allocator picks an 80 GB-class card instead of a
|
|
225
297
|
# consumer GPU that passes the aggregate estimate but fails the vLLM free-memory preflight.
|
|
226
298
|
_OPD_VLLM_COLOCATE_FLOOR_GB = 41.0
|
|
227
|
-
_VOCAB_DEFAULT = 248_320
|
|
228
299
|
_LOGITS_BUDGET_GB = 6.0
|
|
229
300
|
# 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
|
|
230
301
|
_SFT_LOGITS_BYTES_PER_ELEM = 16.0
|
|
231
|
-
|
|
232
|
-
#
|
|
302
|
+
_SFT_CHUNKED_NLL_TOKENS = 256
|
|
303
|
+
# shared thresholds for the independent liger and worker memory gates.
|
|
233
304
|
_LIGER_MIN_PARAMS_B = 3.0
|
|
234
305
|
_LIGER_LONG_CTX_TOKENS = 2048
|
|
235
306
|
|
|
307
|
+
# trl 1.6 chunked_nll is validated against these model families. other models keep plain nll
|
|
308
|
+
# until their output-head and backbone traversal are covered by the same parity tests.
|
|
309
|
+
_SFT_CHUNKED_NLL_MODELS = frozenset(
|
|
310
|
+
{
|
|
311
|
+
"Qwen/Qwen3.5-0.8B",
|
|
312
|
+
"Qwen/Qwen3.5-2B",
|
|
313
|
+
"Qwen/Qwen3.5-4B",
|
|
314
|
+
"Qwen/Qwen3.5-9B",
|
|
315
|
+
"Qwen/Qwen3.6-35B-A3B",
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
|
|
236
319
|
|
|
237
|
-
def
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
return True
|
|
241
|
-
return (params_b or 0.0) >= _LIGER_MIN_PARAMS_B
|
|
320
|
+
def sft_chunked_nll_enabled(model_id: str) -> bool:
|
|
321
|
+
"""whether the sft worker uses trl's dense-logit-free chunked nll path."""
|
|
322
|
+
return model_id in _SFT_CHUNKED_NLL_MODELS
|
|
242
323
|
|
|
243
324
|
|
|
244
325
|
def sft_logits_per_device_cap(seq_len: int, vocab: int) -> int:
|
|
@@ -384,14 +465,15 @@ def estimate_vram_gb(
|
|
|
384
465
|
dense_image_logits = (seq_len * 4 + completion * 8) * vocab / 1e9
|
|
385
466
|
logits = max(chunked_logits, dense_image_logits)
|
|
386
467
|
return base + rollout + activations + logits
|
|
387
|
-
#
|
|
388
|
-
#
|
|
389
|
-
# comparisons; the default must mirror the worker.
|
|
468
|
+
# direct callers default to the conservative plain-nll estimate. model_required_vram_gb passes
|
|
469
|
+
# the worker's validated chunked-nll decision explicitly when it knows the model identity.
|
|
390
470
|
fused = False if sft_fused_ce is None else bool(sft_fused_ce)
|
|
391
471
|
pd = sft_per_device(batch_size, seq_len=seq_len, vocab=vocab, fused=fused)
|
|
392
472
|
activations = _ACT_COEF * pd * (seq_len / 1024.0) * width
|
|
393
|
-
#
|
|
394
|
-
|
|
473
|
+
# plain nll retains every sequence position. chunked nll retains at most one 256-token vocab
|
|
474
|
+
# projection at a time, independent of micro-batch and context once the chunk is full.
|
|
475
|
+
projected_tokens = min(pd * seq_len, _SFT_CHUNKED_NLL_TOKENS) if fused else pd * seq_len
|
|
476
|
+
logits = projected_tokens * vocab * _SFT_LOGITS_BYTES_PER_ELEM / 1e9
|
|
395
477
|
return base + activations + logits
|
|
396
478
|
|
|
397
479
|
|
|
@@ -465,10 +547,10 @@ def sft_gc_off_peak_gb(
|
|
|
465
547
|
lora_rank: int = 32,
|
|
466
548
|
quant: str = "bf16",
|
|
467
549
|
) -> float:
|
|
468
|
-
"""Estimated peak VRAM (GB) for a
|
|
469
|
-
resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
|
|
470
|
-
|
|
471
|
-
|
|
550
|
+
"""Estimated peak VRAM (GB) for a dense-logit-free LoRA SFT step with gradient checkpointing OFF:
|
|
551
|
+
the resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
|
|
552
|
+
Chunked or fused CE is assumed, so there is no ``[B, T, vocab]`` logits term (the thing that made
|
|
553
|
+
GC-off impossible at a 248k vocab). Unknown architecture dims -> ``inf`` (caller keeps GC on).
|
|
472
554
|
|
|
473
555
|
MoE: the activation backbone scales with the model's real ``hidden`` x ``num_layers`` (geometry),
|
|
474
556
|
NOT params_b -- the ~3B-active expert FFN is already folded into ``_GC_OFF_ACT_K``. ``weights``
|
|
@@ -497,7 +579,7 @@ def sft_grad_checkpoint_can_disable(
|
|
|
497
579
|
quant: str = "bf16",
|
|
498
580
|
margin_gb: float = 18.0,
|
|
499
581
|
) -> bool:
|
|
500
|
-
"""True when a
|
|
582
|
+
"""True when a dense-logit-free LoRA SFT step fits a ``card_vram_gb`` card WITHOUT gradient checkpointing,
|
|
501
583
|
so GC -- a ~+33% recompute tax on every step -- can be turned off for the speed win.
|
|
502
584
|
|
|
503
585
|
Conservative by construction: an unknown card / unknown architecture dims, or a peak that doesn't
|
|
@@ -716,12 +798,10 @@ def model_required_vram_gb(
|
|
|
716
798
|
vllm_concurrency = (
|
|
717
799
|
opd_rollout_concurrency(batch_size, group_size) if is_opd else group_size
|
|
718
800
|
)
|
|
719
|
-
#
|
|
720
|
-
#
|
|
721
|
-
#
|
|
722
|
-
|
|
723
|
-
# routes to consumer cards and OOMs on the first backward.
|
|
724
|
-
sft_fused_ce = None if is_grpo else False
|
|
801
|
+
# trl chunked_nll removes ignored positions before the lm head and projects valid tokens in
|
|
802
|
+
# bounded chunks. size validated qwen sft jobs without a dense [batch, seq, vocab] term; models
|
|
803
|
+
# outside that validated set keep the conservative plain-nll estimate and micro-batch cap.
|
|
804
|
+
sft_fused_ce = None if is_grpo else sft_chunked_nll_enabled(model_id)
|
|
725
805
|
if info is not None:
|
|
726
806
|
params_b = info.params_b
|
|
727
807
|
if model_revision:
|
|
@@ -109,11 +109,56 @@ def finalize_alloc_conf_for_sleep() -> None:
|
|
|
109
109
|
print("[alloc] auto-conf skipped:", e)
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
def _is_cudagraph_capture_failure(exc: BaseException) -> bool:
|
|
113
|
+
markers = (
|
|
114
|
+
"capture_model",
|
|
115
|
+
"cuda graph",
|
|
116
|
+
"cudagraph",
|
|
117
|
+
"graph capture",
|
|
118
|
+
)
|
|
119
|
+
current: BaseException | None = exc
|
|
120
|
+
seen: set[int] = set()
|
|
121
|
+
while current is not None and id(current) not in seen:
|
|
122
|
+
seen.add(id(current))
|
|
123
|
+
if any(marker in f"{type(current).__name__}: {current}".lower() for marker in markers):
|
|
124
|
+
return True
|
|
125
|
+
traceback = current.__traceback__
|
|
126
|
+
while traceback is not None:
|
|
127
|
+
frame = traceback.tb_frame
|
|
128
|
+
location = f"{frame.f_code.co_filename}:{frame.f_code.co_name}".lower()
|
|
129
|
+
if any(marker in location for marker in markers):
|
|
130
|
+
return True
|
|
131
|
+
traceback = traceback.tb_next
|
|
132
|
+
current = current.__cause__ or current.__context__
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _release_failed_cudagraph_capture(exc: BaseException) -> None:
|
|
137
|
+
current: BaseException | None = exc
|
|
138
|
+
seen: set[int] = set()
|
|
139
|
+
while current is not None and id(current) not in seen:
|
|
140
|
+
seen.add(id(current))
|
|
141
|
+
next_exc = current.__cause__ or current.__context__
|
|
142
|
+
current.__traceback__ = None
|
|
143
|
+
current = next_exc
|
|
144
|
+
try:
|
|
145
|
+
import gc
|
|
146
|
+
|
|
147
|
+
gc.collect()
|
|
148
|
+
import torch
|
|
149
|
+
|
|
150
|
+
if torch.cuda.is_available():
|
|
151
|
+
torch.cuda.empty_cache()
|
|
152
|
+
except Exception as cleanup_exc:
|
|
153
|
+
print(f"[rl][warn] cuda graph fallback cleanup failed: {cleanup_exc}")
|
|
154
|
+
|
|
155
|
+
|
|
112
156
|
def patch_trl_colocate_llm_kwargs(
|
|
113
157
|
*,
|
|
114
158
|
kv_cache_dtype: str | None = None,
|
|
115
159
|
max_num_batched_tokens: int | None = None,
|
|
116
160
|
enforce_eager: bool | None = None,
|
|
161
|
+
compilation_config: dict | None = None,
|
|
117
162
|
attention_backend: str | None = None,
|
|
118
163
|
mm_encoder_attn_backend: str | None = None,
|
|
119
164
|
reasoning_parser: str | None = None,
|
|
@@ -144,6 +189,8 @@ def patch_trl_colocate_llm_kwargs(
|
|
|
144
189
|
* ``enforce_eager=True``: run the rollout engine in pure eager mode (no torch.compile, no CUDA-graph
|
|
145
190
|
capture) — the SUPPORTED replacement for the removed ``VLLM_TORCH_COMPILE_LEVEL=0`` env. Dodges
|
|
146
191
|
the vLLM 0.19.1 aot_compile (Ampere sm86) + Triton slot-mapping (graph-capture) crashes off B200.
|
|
192
|
+
* ``compilation_config``: select a narrower vLLM compilation profile, such as decode-only CUDA
|
|
193
|
+
graphs with torch.compile/AOT disabled.
|
|
147
194
|
* ``attention_backend="FLASHINFER"|"TRITON_ATTN"``: pin a PTX-independent decoder attention backend
|
|
148
195
|
(consumer-Blackwell sm120) — the SUPPORTED replacement for the removed ``VLLM_ATTENTION_BACKEND``
|
|
149
196
|
env. ``EngineArgs`` coerces the bare member name through ``AttentionConfig.validate_backend_before``.
|
|
@@ -171,6 +218,8 @@ def patch_trl_colocate_llm_kwargs(
|
|
|
171
218
|
new_overrides["max_num_batched_tokens"] = int(max_num_batched_tokens)
|
|
172
219
|
if enforce_eager is not None:
|
|
173
220
|
new_overrides["enforce_eager"] = bool(enforce_eager)
|
|
221
|
+
if compilation_config is not None:
|
|
222
|
+
new_overrides["compilation_config"] = dict(compilation_config)
|
|
174
223
|
if attention_backend is not None:
|
|
175
224
|
new_overrides["attention_backend"] = attention_backend
|
|
176
225
|
if mm_encoder_attn_backend is not None:
|
|
@@ -194,7 +243,9 @@ def patch_trl_colocate_llm_kwargs(
|
|
|
194
243
|
_vg._flash_llm_overrides = overrides
|
|
195
244
|
overrides.update(new_overrides)
|
|
196
245
|
if getattr(_vg, "_flash_llm_kwargs_patched", False):
|
|
197
|
-
print(
|
|
246
|
+
print(
|
|
247
|
+
f"[rl] colocate vLLM LLM kwargs extended (wrapper already installed): {new_overrides}"
|
|
248
|
+
)
|
|
198
249
|
return True
|
|
199
250
|
# TRL binds the module-global ``LLM`` only under ``if is_vllm_available():`` (it's the symbol the
|
|
200
251
|
# colocate ``self.llm = LLM(...)`` references). If vLLM isn't importable here there is nothing to
|
|
@@ -205,11 +256,31 @@ def patch_trl_colocate_llm_kwargs(
|
|
|
205
256
|
return False
|
|
206
257
|
|
|
207
258
|
def _patched_LLM(*args, **kwargs):
|
|
208
|
-
#
|
|
259
|
+
# override trl's hardcoded/absent values with ours (the kwargs trl/env can't set). read the
|
|
209
260
|
# accumulated dict live so kwargs registered by later calls are applied too.
|
|
210
261
|
kwargs.update(_vg._flash_llm_overrides)
|
|
211
262
|
print(f"[rl] colocate vLLM LLM(...) kwargs override applied: {_vg._flash_llm_overrides}")
|
|
212
|
-
|
|
263
|
+
compilation = kwargs.get("compilation_config") or {}
|
|
264
|
+
decode_graphs = (
|
|
265
|
+
not kwargs.get("enforce_eager", False)
|
|
266
|
+
and compilation.get("cudagraph_mode") == "FULL_DECODE_ONLY"
|
|
267
|
+
)
|
|
268
|
+
try:
|
|
269
|
+
return _orig_LLM(*args, **kwargs)
|
|
270
|
+
except Exception as capture_exc:
|
|
271
|
+
if not decode_graphs or not _is_cudagraph_capture_failure(capture_exc):
|
|
272
|
+
raise
|
|
273
|
+
_release_failed_cudagraph_capture(capture_exc)
|
|
274
|
+
eager_kwargs = dict(kwargs)
|
|
275
|
+
eager_kwargs["enforce_eager"] = True
|
|
276
|
+
eager_kwargs.pop("compilation_config", None)
|
|
277
|
+
_vg._flash_llm_overrides["enforce_eager"] = True
|
|
278
|
+
_vg._flash_llm_overrides.pop("compilation_config", None)
|
|
279
|
+
print(
|
|
280
|
+
f"[rl][warn] decode-only cuda graph capture failed ({capture_exc}); "
|
|
281
|
+
"retrying colocate vLLM initialization once in eager mode"
|
|
282
|
+
)
|
|
283
|
+
return _orig_LLM(*args, **eager_kwargs)
|
|
213
284
|
|
|
214
285
|
_vg.LLM = _patched_LLM
|
|
215
286
|
_vg._flash_llm_kwargs_patched = True
|