freesolo-flash-dev 1.0.7__tar.gz → 1.0.8__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.8/AB_PLAN.md +45 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/training_doc.py +10 -7
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cost/analytical.py +16 -5
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cost/spec.py +8 -9
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cost/types.py +15 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/diagnostics.py +11 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/vram.py +32 -23
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/gpu_setup.py +74 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/heartbeat.py +93 -27
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd.py +32 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/rl.py +110 -62
- freesolo_flash_dev-1.0.8/flash/engine/worker/rollout_samples.py +148 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/sft.py +122 -61
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/lora_rank.py +61 -15
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_poll.py +55 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/jobs.py +8 -5
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/train/endpoints.py +10 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/api.py +8 -4
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/runner/__init__.py +38 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/runner/lifecycle.py +7 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/schema/__init__.py +4 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/schema/fields.py +10 -1
- freesolo_flash_dev-1.0.8/flash/serve/preflight.py +121 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/routes/runs.py +3 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/routes/serving.py +26 -10
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/spec.py +34 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_allocator.py +85 -65
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_b200_rollout_opt.py +7 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_chalk_kernels.py +1 -6
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_estimate.py +17 -14
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_estimate.py +31 -6
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_flash_worker.py +9 -10
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_gpus.py +2 -3
- freesolo_flash_dev-1.0.8/tests/test_grpo_cudagraphs.py +101 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_grpo_params.py +76 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_jobs.py +90 -0
- freesolo_flash_dev-1.0.8/tests/test_multi_gpu_gate.py +83 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_opd.py +47 -0
- freesolo_flash_dev-1.0.8/tests/test_rollout_samples.py +606 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_api.py +253 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_envs_coverage.py +63 -2
- freesolo_flash_dev-1.0.8/tests/test_sft_chunked_nll.py +292 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_spec_and_validation.py +51 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_train_context_preflight.py +24 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vast_api.py +12 -3
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_init_heartbeat.py +34 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.env.example +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/README.md +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/catalog.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo_multimodal.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd_vllm.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_async_uploads.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_grpo_perturn.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_image_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_per_turn_reward.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_request_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_opd_vllm.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_sft_activation_shaping.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_sft_tokenize_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_single_turn_multimodal_grpo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.7 → freesolo_flash_dev-1.0.8}/tests/test_worker_thinking.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# GRPO decode-only CUDA graph A/B plan
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Measure whether decode-only CUDA graphs speed up the colocated GRPO rollout engine without changing training behavior. This plan does not authorize a paid run.
|
|
6
|
+
|
|
7
|
+
## Frozen comparison
|
|
8
|
+
|
|
9
|
+
- Control: `origin/dev` at `8fdfc8580b9058d8c40289d4fa1a2e6e3b7bced2`. On RTX 5090/sm120 with vLLM 0.19.1, this selects `enforce_eager=True`.
|
|
10
|
+
- Treatment: the committed head of `perf/grpo-cudagraphs`. On the same GPU and vLLM version, this selects `enforce_eager=False` with `mode=0` and `cudagraph_mode=FULL_DECODE_ONLY`.
|
|
11
|
+
- Hardware: two separate, matched RunPod RTX 5090 workers from the same GPU offer, image, region, CPU/RAM class, and storage configuration. Do not substitute unmatched cards between arms.
|
|
12
|
+
- Model: `Qwen/Qwen3.5-0.8B`, which fits within an RTX 4090-class memory budget.
|
|
13
|
+
- Environment: the real `entropy-R1` hub environment, pinned to one immutable `freesolo-co/environment-hub` commit before launch.
|
|
14
|
+
- Seed: 42 for both arms.
|
|
15
|
+
- Budget: 30 optimizer updates after a two-update exact-path smoke. Use the same prompts per step, group size, maximum context, maximum completion length, LoRA settings, optimizer settings, reward implementation, dataset order, and checkpoint schedule.
|
|
16
|
+
- Concurrency: launch both paid arms together only after both two-update smokes pass. Do not reuse a worker across arms.
|
|
17
|
+
|
|
18
|
+
## Preflight and token-parity gate
|
|
19
|
+
|
|
20
|
+
1. Resolve and record the exact Flash commit, environment-hub commit, model revision, container digest, vLLM version, CUDA/driver versions, and RunPod machine identifiers for each arm.
|
|
21
|
+
2. Run the same fixed prompt batch through both immutable rollout engines with greedy decoding and the same maximum token count.
|
|
22
|
+
3. Require identical token IDs, finish reasons, and decoded text for every fixed prompt. Any difference is a correctness failure and stops the A/B.
|
|
23
|
+
4. Run two GRPO updates per arm. Require successful graph capture or vLLM's eager fallback, finite loss/reward values, and no OOM before scaling to 30 updates.
|
|
24
|
+
|
|
25
|
+
## Metrics
|
|
26
|
+
|
|
27
|
+
Measure rollout work separately from training work where telemetry permits.
|
|
28
|
+
|
|
29
|
+
- Primary performance: generated rollout tokens per rollout-engine second, excluding engine startup, graph capture, checkpointing, reward evaluation, and optimizer time.
|
|
30
|
+
- Secondary performance: median rollout wall time per update and end-to-end median step wall time. Report graph-capture startup time separately rather than amortizing it away.
|
|
31
|
+
- Correctness: fixed-prompt rollout token parity, reward curve, training loss curve, completion length, finish-reason distribution, and invalid/empty completion rate.
|
|
32
|
+
- Safety: peak allocated and reserved GPU memory, OOM count, graph-capture failures, eager-fallback events, worker restarts, and failed updates.
|
|
33
|
+
|
|
34
|
+
## Decision thresholds
|
|
35
|
+
|
|
36
|
+
The expected direction is faster treatment decode with unchanged learning behavior.
|
|
37
|
+
|
|
38
|
+
- Performance works: treatment improves steady-state rollout tokens/second by at least 10% over control. A 10% to 40% improvement is the expected range. Confirm the direction with at least a 9% reduction in median rollout wall time over updates 3 through 30.
|
|
39
|
+
- End-to-end value: treatment reduces median total step wall time by at least 5%. Report a decode-only win below this threshold as technically real but operationally marginal.
|
|
40
|
+
- Reward parity: treatment reward AUC over the 30 matched updates must remain within 5% relative or 0.02 absolute of control, whichever tolerance is larger. There must be no sustained three-update reward regression larger than 0.05 absolute.
|
|
41
|
+
- Loss parity: treatment loss AUC must remain within 5% relative of control, with finite values at every update and no new divergence pattern.
|
|
42
|
+
- Rollout parity: the fixed greedy prompt batch must have exact token-ID parity. During stochastic training, completion length, finish-reason, and empty/invalid-rate differences must each remain within 5% relative or one sample, whichever is larger.
|
|
43
|
+
- Memory and reliability: no OOM, no worker restart, no failed update, and treatment peak reserved memory no more than 5% above control after graph capture.
|
|
44
|
+
|
|
45
|
+
The treatment passes only if all correctness and reliability gates pass and the primary performance threshold is met. If vLLM falls back to eager, report the fallback and classify the performance result as inconclusive rather than a decode-graph success.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
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
|
|
@@ -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
|
|
@@ -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
|
+
)
|
|
@@ -228,17 +228,27 @@ _VOCAB_DEFAULT = 248_320
|
|
|
228
228
|
_LOGITS_BUDGET_GB = 6.0
|
|
229
229
|
# 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
|
|
230
230
|
_SFT_LOGITS_BYTES_PER_ELEM = 16.0
|
|
231
|
-
|
|
232
|
-
#
|
|
231
|
+
_SFT_CHUNKED_NLL_TOKENS = 256
|
|
232
|
+
# shared thresholds for the independent liger and worker memory gates.
|
|
233
233
|
_LIGER_MIN_PARAMS_B = 3.0
|
|
234
234
|
_LIGER_LONG_CTX_TOKENS = 2048
|
|
235
235
|
|
|
236
|
+
# trl 1.6 chunked_nll is validated against these model families. other models keep plain nll
|
|
237
|
+
# until their output-head and backbone traversal are covered by the same parity tests.
|
|
238
|
+
_SFT_CHUNKED_NLL_MODELS = frozenset(
|
|
239
|
+
{
|
|
240
|
+
"Qwen/Qwen3.5-0.8B",
|
|
241
|
+
"Qwen/Qwen3.5-2B",
|
|
242
|
+
"Qwen/Qwen3.5-4B",
|
|
243
|
+
"Qwen/Qwen3.5-9B",
|
|
244
|
+
"Qwen/Qwen3.6-35B-A3B",
|
|
245
|
+
}
|
|
246
|
+
)
|
|
236
247
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return (params_b or 0.0) >= _LIGER_MIN_PARAMS_B
|
|
248
|
+
|
|
249
|
+
def sft_chunked_nll_enabled(model_id: str) -> bool:
|
|
250
|
+
"""whether the sft worker uses trl's dense-logit-free chunked nll path."""
|
|
251
|
+
return model_id in _SFT_CHUNKED_NLL_MODELS
|
|
242
252
|
|
|
243
253
|
|
|
244
254
|
def sft_logits_per_device_cap(seq_len: int, vocab: int) -> int:
|
|
@@ -384,14 +394,15 @@ def estimate_vram_gb(
|
|
|
384
394
|
dense_image_logits = (seq_len * 4 + completion * 8) * vocab / 1e9
|
|
385
395
|
logits = max(chunked_logits, dense_image_logits)
|
|
386
396
|
return base + rollout + activations + logits
|
|
387
|
-
#
|
|
388
|
-
#
|
|
389
|
-
# comparisons; the default must mirror the worker.
|
|
397
|
+
# direct callers default to the conservative plain-nll estimate. model_required_vram_gb passes
|
|
398
|
+
# the worker's validated chunked-nll decision explicitly when it knows the model identity.
|
|
390
399
|
fused = False if sft_fused_ce is None else bool(sft_fused_ce)
|
|
391
400
|
pd = sft_per_device(batch_size, seq_len=seq_len, vocab=vocab, fused=fused)
|
|
392
401
|
activations = _ACT_COEF * pd * (seq_len / 1024.0) * width
|
|
393
|
-
#
|
|
394
|
-
|
|
402
|
+
# plain nll retains every sequence position. chunked nll retains at most one 256-token vocab
|
|
403
|
+
# projection at a time, independent of micro-batch and context once the chunk is full.
|
|
404
|
+
projected_tokens = min(pd * seq_len, _SFT_CHUNKED_NLL_TOKENS) if fused else pd * seq_len
|
|
405
|
+
logits = projected_tokens * vocab * _SFT_LOGITS_BYTES_PER_ELEM / 1e9
|
|
395
406
|
return base + activations + logits
|
|
396
407
|
|
|
397
408
|
|
|
@@ -465,10 +476,10 @@ def sft_gc_off_peak_gb(
|
|
|
465
476
|
lora_rank: int = 32,
|
|
466
477
|
quant: str = "bf16",
|
|
467
478
|
) -> float:
|
|
468
|
-
"""Estimated peak VRAM (GB) for a
|
|
469
|
-
resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
|
|
470
|
-
|
|
471
|
-
|
|
479
|
+
"""Estimated peak VRAM (GB) for a dense-logit-free LoRA SFT step with gradient checkpointing OFF:
|
|
480
|
+
the resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
|
|
481
|
+
Chunked or fused CE is assumed, so there is no ``[B, T, vocab]`` logits term (the thing that made
|
|
482
|
+
GC-off impossible at a 248k vocab). Unknown architecture dims -> ``inf`` (caller keeps GC on).
|
|
472
483
|
|
|
473
484
|
MoE: the activation backbone scales with the model's real ``hidden`` x ``num_layers`` (geometry),
|
|
474
485
|
NOT params_b -- the ~3B-active expert FFN is already folded into ``_GC_OFF_ACT_K``. ``weights``
|
|
@@ -497,7 +508,7 @@ def sft_grad_checkpoint_can_disable(
|
|
|
497
508
|
quant: str = "bf16",
|
|
498
509
|
margin_gb: float = 18.0,
|
|
499
510
|
) -> bool:
|
|
500
|
-
"""True when a
|
|
511
|
+
"""True when a dense-logit-free LoRA SFT step fits a ``card_vram_gb`` card WITHOUT gradient checkpointing,
|
|
501
512
|
so GC -- a ~+33% recompute tax on every step -- can be turned off for the speed win.
|
|
502
513
|
|
|
503
514
|
Conservative by construction: an unknown card / unknown architecture dims, or a peak that doesn't
|
|
@@ -716,12 +727,10 @@ def model_required_vram_gb(
|
|
|
716
727
|
vllm_concurrency = (
|
|
717
728
|
opd_rollout_concurrency(batch_size, group_size) if is_opd else group_size
|
|
718
729
|
)
|
|
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
|
|
730
|
+
# trl chunked_nll removes ignored positions before the lm head and projects valid tokens in
|
|
731
|
+
# bounded chunks. size validated qwen sft jobs without a dense [batch, seq, vocab] term; models
|
|
732
|
+
# outside that validated set keep the conservative plain-nll estimate and micro-batch cap.
|
|
733
|
+
sft_fused_ce = None if is_grpo else sft_chunked_nll_enabled(model_id)
|
|
725
734
|
if info is not None:
|
|
726
735
|
params_b = info.params_b
|
|
727
736
|
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
|
|
@@ -18,6 +18,7 @@ import time
|
|
|
18
18
|
|
|
19
19
|
from flash.engine.worker._pkg import W as _w
|
|
20
20
|
from flash.engine.worker.perf import gpu_diagnostics
|
|
21
|
+
from flash.engine.worker.rollout_samples import sanitize_rollout_text
|
|
21
22
|
|
|
22
23
|
# Setup-phase liveness stages: emitted from a 30s liveness thread WITH a progress callback during the
|
|
23
24
|
# cold download / model-load / split-scan phase, kept on the tighter setup-liveness upload cadence
|
|
@@ -151,26 +152,20 @@ def heartbeat(stage: str, *, liveness: bool = False, force: bool = False, **kw):
|
|
|
151
152
|
if stage in _HB_TIGHT_LIVENESS_STAGES:
|
|
152
153
|
interval_s = min(interval_s, _w._HB_SETUP_LIVENESS_INTERVAL_S)
|
|
153
154
|
upload_due = not throttled or (now - _w._HB_LAST_UPLOAD) >= interval_s
|
|
154
|
-
# ``force`` bypasses the per-stage throttle
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
# payload's ``step`` exceeds the last committed step) AND a per-force time floor measured from
|
|
160
|
-
# the last FORCED commit. The floor throttles only a SUB-FLOOR BURST of step advances (a
|
|
161
|
-
# tiny/fast OPD config landing many optimizer updates per minute, which unthrottled would blow
|
|
162
|
-
# the HF per-repo commit cap before the final adapter/DONE upload, codex[bot]); because the
|
|
163
|
-
# floor keys off the last forced commit -- not the last upload of any kind -- a force still
|
|
164
|
-
# punches through IMMEDIATELY after a liveness/mid-step ping stole the slot, and steps spaced
|
|
165
|
-
# farther apart than the floor (the normal teacher-round-trip-gated regime) each still commit
|
|
166
|
-
# exactly once, so a cancel there still bills the true latest step. The bounded cost is that a
|
|
167
|
-
# cancel DURING a sub-floor burst under-bills by up to one floor-window of steps -- acceptable
|
|
168
|
-
# (it favours the customer) and unavoidable once steps outrun the commit cap.
|
|
155
|
+
# ``force`` bypasses the per-stage throttle when this payload must be on record. it normally
|
|
156
|
+
# requires a step advance, but a sample-bearing payload may match the committed step because
|
|
157
|
+
# the liveness daemon can commit that step first without the samples. the per-force floor still
|
|
158
|
+
# coalesces fast bursts to protect the hf commit cap, while an unrelated liveness commit does
|
|
159
|
+
# not arm the floor and therefore cannot suppress the first sample-bearing payload.
|
|
169
160
|
if force and not upload_due:
|
|
170
161
|
fstep = kw.get("step")
|
|
162
|
+
has_samples = bool(kw.get("sampled_completions"))
|
|
163
|
+
force_step_due = isinstance(fstep, (int, float)) and (
|
|
164
|
+
fstep > _w._HB_LAST_COMMITTED_STEP
|
|
165
|
+
or (has_samples and fstep == _w._HB_LAST_COMMITTED_STEP)
|
|
166
|
+
)
|
|
171
167
|
if (
|
|
172
|
-
|
|
173
|
-
and fstep > _w._HB_LAST_COMMITTED_STEP
|
|
168
|
+
force_step_due
|
|
174
169
|
and (now - _w._HB_LAST_FORCED_UPLOAD) >= _w._HB_FORCE_MIN_INTERVAL_S
|
|
175
170
|
):
|
|
176
171
|
upload_due = True
|
|
@@ -193,6 +188,7 @@ def heartbeat(stage: str, *, liveness: bool = False, force: bool = False, **kw):
|
|
|
193
188
|
_committed_step = kw.get("step")
|
|
194
189
|
if isinstance(_committed_step, (int, float)) and _committed_step > _w._HB_LAST_COMMITTED_STEP:
|
|
195
190
|
_w._HB_LAST_COMMITTED_STEP = int(_committed_step)
|
|
191
|
+
payload_committed = False
|
|
196
192
|
if upload_due:
|
|
197
193
|
critical = _is_critical_stage(stage)
|
|
198
194
|
lock_timeout = _HB_CRITICAL_UPLOAD_LOCK_TIMEOUT_S if critical else _HB_UPLOAD_LOCK_TIMEOUT_S
|
|
@@ -210,19 +206,27 @@ def heartbeat(stage: str, *, liveness: bool = False, force: bool = False, **kw):
|
|
|
210
206
|
# ``is False`` (not falsy) so a mock/None never trips the rollback.
|
|
211
207
|
_rollback_throttle_slot(my_claim, prev_last_upload, prev_last_step, prev_last_forced)
|
|
212
208
|
print(f"HEARTBEAT upload failed; rolled back throttle slot for {stage}")
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
209
|
+
else:
|
|
210
|
+
payload_committed = True
|
|
211
|
+
if not liveness:
|
|
212
|
+
# this committed snapshot carried real progress; settle the progress-carry
|
|
213
|
+
# latch up to the seq captured when the snapshot was built (max: a concurrent
|
|
214
|
+
# newer real heartbeat that lost the upload race must stay pending).
|
|
215
|
+
with _HB_LOCK:
|
|
216
|
+
if my_progress_seq > _w._HB_PROGRESS_UPLOADED_SEQ:
|
|
217
|
+
_w._HB_PROGRESS_UPLOADED_SEQ = my_progress_seq
|
|
220
218
|
finally:
|
|
221
219
|
_HB_UPLOAD_LOCK.release()
|
|
222
220
|
else:
|
|
223
221
|
_rollback_throttle_slot(my_claim, prev_last_upload, prev_last_step, prev_last_forced)
|
|
224
222
|
print(f"HEARTBEAT upload-lock busy >{lock_timeout}s; skipping commit for {stage}")
|
|
225
|
-
|
|
223
|
+
console_payload = payload
|
|
224
|
+
if not payload_committed and payload.get("sampled_completions"):
|
|
225
|
+
console_payload = {
|
|
226
|
+
key: value for key, value in payload.items() if key != "sampled_completions"
|
|
227
|
+
}
|
|
228
|
+
print("HEARTBEAT", json.dumps(console_payload))
|
|
229
|
+
return payload_committed
|
|
226
230
|
|
|
227
231
|
|
|
228
232
|
def _maybe_attach_gpu_diag(payload: dict, last_gpu_diag_at: float, now: float) -> float:
|
|
@@ -290,7 +294,58 @@ def _bounded_reward_metrics(metrics) -> dict[str, float]:
|
|
|
290
294
|
return {n: surviving[n] for n in sorted(priority + rest)}
|
|
291
295
|
|
|
292
296
|
|
|
293
|
-
|
|
297
|
+
# Exactly three samples per heartbeat, always. Mirrors rollout_samples._SAMPLE_LIMIT.
|
|
298
|
+
_SAMPLE_LIMIT = 3
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _sampled_completion_scalar(sample: dict) -> tuple[str, float] | None:
|
|
302
|
+
"""Return the (key, finite value) of a sample's scalar: GRPO ``reward`` or OPD ``loss``."""
|
|
303
|
+
for key in ("reward", "loss"):
|
|
304
|
+
if key not in sample:
|
|
305
|
+
continue
|
|
306
|
+
try:
|
|
307
|
+
value = float(sample.get(key))
|
|
308
|
+
except (TypeError, ValueError):
|
|
309
|
+
return None
|
|
310
|
+
return (key, value) if math.isfinite(value) else None
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _bounded_sampled_completions(samples) -> list[dict]:
|
|
315
|
+
if not isinstance(samples, (list, tuple)):
|
|
316
|
+
return []
|
|
317
|
+
bounded: list[dict] = []
|
|
318
|
+
for sample in samples:
|
|
319
|
+
if not isinstance(sample, dict):
|
|
320
|
+
continue
|
|
321
|
+
prompt_tail = sample.get("prompt_tail")
|
|
322
|
+
completion = sample.get("completion")
|
|
323
|
+
if not isinstance(prompt_tail, str) or not isinstance(completion, str):
|
|
324
|
+
continue
|
|
325
|
+
scalar = _sampled_completion_scalar(sample)
|
|
326
|
+
if scalar is None:
|
|
327
|
+
continue
|
|
328
|
+
generated_at_step = sample.get("generated_at_step")
|
|
329
|
+
if generated_at_step is not None:
|
|
330
|
+
try:
|
|
331
|
+
generated_at_step = int(generated_at_step)
|
|
332
|
+
except (TypeError, ValueError):
|
|
333
|
+
continue
|
|
334
|
+
scalar_key, scalar_value = scalar
|
|
335
|
+
bounded.append(
|
|
336
|
+
{
|
|
337
|
+
"prompt_tail": sanitize_rollout_text(prompt_tail),
|
|
338
|
+
"completion": sanitize_rollout_text(completion),
|
|
339
|
+
scalar_key: scalar_value,
|
|
340
|
+
"generated_at_step": generated_at_step,
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
if len(bounded) >= _SAMPLE_LIMIT:
|
|
344
|
+
break
|
|
345
|
+
return bounded
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def make_reward_heartbeat_callback(reward_metrics=None, samples=None):
|
|
294
349
|
"""Return a TRL callback that streams per-step reward to the HF heartbeat channel."""
|
|
295
350
|
from transformers import TrainerCallback
|
|
296
351
|
|
|
@@ -298,6 +353,7 @@ def make_reward_heartbeat_callback(reward_metrics=None):
|
|
|
298
353
|
def __init__(self):
|
|
299
354
|
self.reward_history = []
|
|
300
355
|
self.last_gpu_diag_at = 0.0
|
|
356
|
+
self.sent_first_sample_heartbeat = False
|
|
301
357
|
self.latest_reward_metrics: dict[str, float] = {}
|
|
302
358
|
|
|
303
359
|
def latest_fields(self) -> dict:
|
|
@@ -326,9 +382,19 @@ def make_reward_heartbeat_callback(reward_metrics=None):
|
|
|
326
382
|
self.latest_reward_metrics = _bounded_reward_metrics(latest_metrics)
|
|
327
383
|
if self.latest_reward_metrics:
|
|
328
384
|
payload["reward_metrics"] = dict(self.latest_reward_metrics)
|
|
385
|
+
latest_samples = samples() if callable(samples) else samples
|
|
386
|
+
bounded_samples = _bounded_sampled_completions(latest_samples)
|
|
387
|
+
if bounded_samples:
|
|
388
|
+
payload["sampled_completions"] = bounded_samples
|
|
329
389
|
now = time.monotonic()
|
|
330
390
|
self.last_gpu_diag_at = _maybe_attach_gpu_diag(payload, self.last_gpu_diag_at, now)
|
|
331
|
-
|
|
391
|
+
force_first_samples = bool(bounded_samples) and not self.sent_first_sample_heartbeat
|
|
392
|
+
if force_first_samples:
|
|
393
|
+
committed = _w.heartbeat("rl_step", force=True, **payload)
|
|
394
|
+
if committed:
|
|
395
|
+
self.sent_first_sample_heartbeat = True
|
|
396
|
+
else:
|
|
397
|
+
_w.heartbeat("rl_step", **payload)
|
|
332
398
|
|
|
333
399
|
return _RewardHeartbeat()
|
|
334
400
|
|