freesolo-flash-dev 0.2.47__tar.gz → 0.2.49__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/PKG-INFO +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/commands.py +7 -5
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/training_doc.py +22 -19
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/analytical.py +2 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/spec.py +67 -17
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/types.py +5 -5
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/recipe.py +4 -5
- freesolo_flash_dev-0.2.49/flash/engine/steps.py +18 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/vram.py +73 -20
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/grpo.py +2 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd.py +269 -252
- freesolo_flash_dev-0.2.49/flash/engine/worker/opd_vllm.py +274 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/lifecycle.py +13 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/memory.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/rl.py +24 -9
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/lora_rank.py +8 -7
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_worker.py +0 -4
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/schema/__init__.py +22 -23
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/spec.py +4 -6
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/pyproject.toml +2 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_agent_flash_cli_contract.py +2 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_algorithms.py +4 -4
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_allocator.py +456 -61
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_b200_rollout_opt.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_backend_jobspec_contract.py +7 -5
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_billing_retry.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_charge_pricing.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_checkpoints.py +2 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_commands.py +8 -6
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_errors.py +5 -3
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_estimate.py +33 -17
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_managed.py +2 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_client_server_integration.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_config_overrides.py +9 -5
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_analytical.py +27 -14
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_publish.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_rate_limit_resolve.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flash_mvp.py +4 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flash_worker.py +8 -8
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_gpus.py +7 -6
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_params.py +66 -29
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_sleep_gate.py +4 -4
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_jobs.py +72 -17
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_lora_rank_preflight.py +2 -3
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_oom_escalate_gpu.py +10 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_opd.py +375 -82
- freesolo_flash_dev-0.2.49/tests/test_opd_vllm.py +184 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_open_model_policy.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_orchestrator_flash.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runmgmt.py +2 -2
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_api.py +13 -13
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_billing.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_spec_and_validation.py +11 -8
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_train_context_preflight.py +19 -14
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_weight_cache.py +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_dryrun.py +13 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/uv.lock +1 -1
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.env.example +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/Dockerfile +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/README.md +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_channel.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/catalog.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/config.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/http.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/deps.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/app.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/conftest.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_client.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_export.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_version.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_recombine.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_thinking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.49
|
|
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
|
|
@@ -447,11 +447,11 @@ def cmd_env_setup(args) -> int:
|
|
|
447
447
|
'# secrets = ["SERPAPI_API_KEY"]\n\n'
|
|
448
448
|
)
|
|
449
449
|
# `thinking = true` opts the run into reasoning mode. Reasoning shares the generation budget with
|
|
450
|
-
# the answer, so GRPO also gets a raised
|
|
451
|
-
# off, keeping the default scaffold byte-for-byte identical.
|
|
450
|
+
# the answer, so GRPO also gets a raised completion budget. These strings are empty when reasoning
|
|
451
|
+
# is off, keeping the default scaffold byte-for-byte identical.
|
|
452
452
|
thinking_line = "thinking = true\n" if reasoning else ""
|
|
453
453
|
rl_reasoning_train = (
|
|
454
|
-
"
|
|
454
|
+
"max_completion_tokens = 2048 # reasoning shares this budget with the answer; raised so it isn't truncated\n"
|
|
455
455
|
if reasoning
|
|
456
456
|
else ""
|
|
457
457
|
)
|
|
@@ -469,7 +469,8 @@ def cmd_env_setup(args) -> int:
|
|
|
469
469
|
"\n"
|
|
470
470
|
f"{env_comment}"
|
|
471
471
|
"[train]\n"
|
|
472
|
-
"
|
|
472
|
+
"epochs = 1\n"
|
|
473
|
+
"max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
|
|
473
474
|
f"{rl_reasoning_train}"
|
|
474
475
|
"lora_rank = 32\n"
|
|
475
476
|
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
@@ -511,7 +512,8 @@ def cmd_env_setup(args) -> int:
|
|
|
511
512
|
"[environment]\n"
|
|
512
513
|
'id = ""\n\n'
|
|
513
514
|
"[train]\n"
|
|
514
|
-
"
|
|
515
|
+
"epochs = 1\n"
|
|
516
|
+
"max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
|
|
515
517
|
"lora_rank = 32\n"
|
|
516
518
|
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
517
519
|
"# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
|
|
@@ -103,7 +103,7 @@ id = "your-org/my-env" # the id printed by `flash env push`
|
|
|
103
103
|
# never stored in the spec
|
|
104
104
|
|
|
105
105
|
[train]
|
|
106
|
-
epochs = 1 #
|
|
106
|
+
epochs = 1 # one pass over the retained train rows
|
|
107
107
|
max_examples = 2 # rows to train on (the starter dataset has 2)
|
|
108
108
|
lora_rank = 32
|
|
109
109
|
lora_alpha = 64
|
|
@@ -114,7 +114,7 @@ GPU and HF artifacts are **fully managed** — do not pick `gpu.type` or set
|
|
|
114
114
|
`train.hf_repo`; the allocator picks the cheapest validated managed GPU class that fits,
|
|
115
115
|
and run artifacts are stored in a private environment-scoped repo with content-addressed
|
|
116
116
|
Flash code snapshots. Compose or tweak configs without editing files: `--config
|
|
117
|
-
extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.
|
|
117
|
+
extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.epochs=3`).
|
|
118
118
|
|
|
119
119
|
### 4. Submit
|
|
120
120
|
|
|
@@ -201,20 +201,20 @@ spending another GPU run:
|
|
|
201
201
|
| --- | --- | --- |
|
|
202
202
|
| Environment id is blank or stale | `flash train --dry-run` fails, or the worker uses old reward/data | Run `flash env push --name my-env .` after every environment/data edit and paste the returned id into every config you submit. |
|
|
203
203
|
| Local-only env path in config | Config validation says there is no local path mode | Publish first, then use the returned slug in `[environment] id`. `flash train` only runs published env ids, not local paths. |
|
|
204
|
-
| Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `
|
|
204
|
+
| Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `epochs`, `group_size`, `max_completion_tokens`, `temperature`, `max_context_tokens`, LoRA, and other training knobs under `[train]`. |
|
|
205
205
|
| Trying to pin managed infrastructure | `gpu.type`, `train.hf_repo`, or `model_policy` changes do not do what you expected | Treat GPU choice, model policy, and the run artifact repo as managed. Tune the model, algorithm, environment, and `[train]` knobs instead. |
|
|
206
206
|
| Secrets are not available on the worker | Reward code works locally but remote logs show missing API keys or auth failures | List secret names under `[environment] secrets = [...]`, export those env vars locally before submit, or put them in local `.env` / `.env.local`. Never put secret values in `[worker_env]` or hard-code them in the config. |
|
|
207
207
|
| Wrong model / thinking setting | Config validation fails, or chat behavior does not match the run | Use `flash models`; set `thinking = true` only for supported models. Thinking is a training-time/run-level choice and serving preserves that parity, so `flash chat` does not expose an override flag. |
|
|
208
208
|
| Thinking reward grades the wrong text | Rewards accidentally score hidden reasoning, or ignore reasoning you meant to inspect | By default, score the answer text. In thinking mode the response object is still string-compatible, but also exposes `.completion`, `.thinking`, and `.raw` when a reward intentionally needs those fields. |
|
|
209
209
|
| All-zero or flat GRPO reward | `reward_mean` stays near 0 and outputs do not improve | Make the reward dense: give partial credit for parse/format/execution/correctness tiers, and log a separate clean `success` metric. Do not keep rerunning an all-zero reward. |
|
|
210
210
|
| Reward rises but behavior is worse | Short, templated, malformed, or reward-hacked outputs score well | Deploy the adapter and probe real examples. Add hard validity gates before judge calls, penalize degenerate shortcuts, and judge the outcome rather than the surface string. |
|
|
211
|
-
| Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `
|
|
212
|
-
| Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash log <run-id>`, reduce footprint (`
|
|
211
|
+
| Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_completion_tokens` for GRPO/OPD rollouts or `max_context_tokens` for total prompt+completion context only after seeing truncation. Oversizing them by default just burns memory/cost. |
|
|
212
|
+
| Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash log <run-id>`, reduce footprint (`max_context_tokens`, `max_completion_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
|
|
213
213
|
| Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash log <run-id> --follow` to reattach, `flash log <run-id>` for the console/error output, or `flash cancel <run-id>` if you intentionally want to stop it. |
|
|
214
214
|
| Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id>/step-N`, and compare with held-out probes before exporting or relying on the final adapter. |
|
|
215
215
|
| Export fails before upload | CLI says no HuggingFace token | Pass `flash export --api-key hf_...`, or set `HF_TOKEN` in your shell, `.env`, or `.env.local`. Exports are private unless you pass `--public`. |
|
|
216
216
|
| SFT loss improves but quality does not | Train loss falls while held-out behavior stalls or degrades | Keep a held-out split outside training. Deploy and score that split; if quality drops, reduce epochs or improve data instead of adding more passes. |
|
|
217
|
-
| Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`,
|
|
217
|
+
| Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, keep `epochs` and `max_examples` small for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
|
|
218
218
|
|
|
219
219
|
---
|
|
220
220
|
|
|
@@ -335,8 +335,8 @@ Pick SFT when you already have good answers and want the model to imitate them.
|
|
|
335
335
|
split the run never trains on, then **deploy the adapter and score it on that split**
|
|
336
336
|
(`flash deploy` + `flash chat`). If held-out quality stalls or drops while train loss
|
|
337
337
|
keeps falling, reduce `epochs` or add more data — not more passes.
|
|
338
|
-
- **Start `
|
|
339
|
-
`
|
|
338
|
+
- **Start `max_context_tokens` small and grow it on evidence.** Begin from the smallest
|
|
339
|
+
`max_context_tokens` that plausibly fits prompt + completion, and only raise it when you see
|
|
340
340
|
truncation (outputs cut off mid-thought, degraded loss). A bigger context just costs
|
|
341
341
|
more.
|
|
342
342
|
- **For Qwen3.5 thinking multi-turn SFT, put reasoning only in the final assistant
|
|
@@ -370,7 +370,9 @@ lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effec
|
|
|
370
370
|
lora_alpha = 32
|
|
371
371
|
```
|
|
372
372
|
|
|
373
|
-
SFT
|
|
373
|
+
SFT, GRPO, and OPD all accept **epoch-driven** configs (`epochs`). For GRPO/OPD,
|
|
374
|
+
an epoch is one pass over the retained prompt pool after `max_examples` and prompt-budget filtering;
|
|
375
|
+
optimizer-step counts are derived from those epochs.
|
|
374
376
|
|
|
375
377
|
---
|
|
376
378
|
|
|
@@ -379,8 +381,8 @@ SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
|
|
|
379
381
|
Pick distillation when a much stronger **teacher** model can grade your student's work
|
|
380
382
|
token-by-token. The student samples on-policy (like GRPO), the managed GLM 5.2 teacher scores
|
|
381
383
|
each of *its own* completions, and a dense per-token loss teaches the student to match the teacher —
|
|
382
|
-
far more sample-efficient than reward-based RL and with no reward to design. It
|
|
383
|
-
|
|
384
|
+
far more sample-efficient than reward-based RL and with no reward to design. It supports `epochs`
|
|
385
|
+
like SFT/GRPO and produces a LoRA served exactly like SFT.
|
|
384
386
|
|
|
385
387
|
- **No teacher key or model override to set up.** The GLM 5.2 teacher and its Fireworks key are platform-managed: the
|
|
386
388
|
service supplies its own key to every opd run, so there is nothing to export or declare — an opd
|
|
@@ -397,7 +399,7 @@ far more sample-efficient than reward-based RL and with no reward to design. It
|
|
|
397
399
|
(driving `step_episode` / observations just like GRPO) and distils EVERY assistant turn against the
|
|
398
400
|
teacher, each conditioned on the transcript up to that turn — the episode's total reverse-KL over
|
|
399
401
|
the student's generated tokens is the sum of its per-turn reverse-KLs. Env/observation tokens are
|
|
400
|
-
never distilled (they're context, not the student's output). Set `[train]
|
|
402
|
+
never distilled (they're context, not the student's output). Set `[train] max_context_tokens` to bound the
|
|
401
403
|
transcript; the teacher must cover it (GLM-5.2's context far exceeds the default budget).
|
|
402
404
|
- **Judge it like SFT.** Distillation logs a falling per-token loss; a low loss alone is not proof.
|
|
403
405
|
Keep a held-out split, `flash deploy` the adapter, and score it — confirm the student actually
|
|
@@ -412,7 +414,8 @@ algorithm = "opd"
|
|
|
412
414
|
id = "your-org/my-env"
|
|
413
415
|
|
|
414
416
|
[train]
|
|
415
|
-
|
|
417
|
+
epochs = 1
|
|
418
|
+
max_examples = 2
|
|
416
419
|
lora_rank = 32
|
|
417
420
|
# kl_penalty_coef = 1.0 # reverse-KL scale
|
|
418
421
|
```
|
|
@@ -427,7 +430,7 @@ in a sensible value, so only override with a reason.
|
|
|
427
430
|
| Knob | Convention |
|
|
428
431
|
| --- | --- |
|
|
429
432
|
| `group_size` | Completions sampled per prompt (default 8). More = more signal and more cost; drop to 4 to trim cost. The group needs *within-group variance* for an advantage to exist. |
|
|
430
|
-
| `
|
|
433
|
+
| `max_completion_tokens` | Completion budget per rollout. Size it to the expected output length — too small silently truncates good answers and poisons the reward; too large just costs more. |
|
|
431
434
|
| `temperature` | Rollout sampling temperature. Keep it near 1.0 for GRPO — too low collapses diversity (and the model can collapse within a few steps); raise it to widen exploration against uniform-reward groups. |
|
|
432
435
|
| `kl_penalty_coef` | Keeps the trained model from drifting too far from the base. Raise it to anchor against entropy collapse; lower it for more freedom to move. |
|
|
433
436
|
| `thinking_length_penalty_coef` | Per-reasoning-token reward deduction — curb overthinking, but watch it doesn't push the model into terse degeneracy. |
|
|
@@ -449,7 +452,7 @@ rollouts score somewhere between all-fail and all-pass — is where GRPO has the
|
|
|
449
452
|
signal.
|
|
450
453
|
|
|
451
454
|
- If nearly every prompt is solved (most groups score ~1.0): **increase difficulty** —
|
|
452
|
-
harder prompts, tighter format/reward, more
|
|
455
|
+
harder prompts, tighter format/reward, more epochs, or more data.
|
|
453
456
|
- If nearly nothing is solved (most groups score ~0.0): **decrease difficulty** —
|
|
454
457
|
easier or few-shot prompts, a more lenient (denser) reward, or warm-start with SFT.
|
|
455
458
|
- In between: good signal — keep iterating at this difficulty.
|
|
@@ -468,7 +471,7 @@ targeted fix rather than leaning on the reward gate to slowly select against it.
|
|
|
468
471
|
| --- | --- | --- |
|
|
469
472
|
| Repetition / looping collapse | the same phrase repeats until truncation | repetition or length penalty; lower `temperature` |
|
|
470
473
|
| Overthinking / verbose reasoning | reasoning eats the whole token budget | `thinking_length_penalty_coef`; tighten the prompt |
|
|
471
|
-
|
|
|
474
|
+
| Completion truncation | answers cut off mid-thought | raise `max_completion_tokens` / `max_context_tokens` |
|
|
472
475
|
| Unparsed / over-escaped output | reward can't read the answer | robust parser; return `0.0` on parse fail; format gate |
|
|
473
476
|
| Wrapper / markdown around structured output | prose around the JSON/answer | a format gate; `stop_sequences` |
|
|
474
477
|
| Uniform-reward groups | every rollout in a group scores the same → no gradient | shape the reward for partial credit; raise `temperature` |
|
|
@@ -500,8 +503,8 @@ on a beyond-noise improvement.
|
|
|
500
503
|
|
|
501
504
|
## Scale the evidence
|
|
502
505
|
|
|
503
|
-
- **A smoke test is not proof.** A single-digit
|
|
504
|
-
of rollouts only validates the wiring. Scale `
|
|
506
|
+
- **A smoke test is not proof.** A single-digit derived step count, a tiny dataset, or a handful
|
|
507
|
+
of rollouts only validates the wiring. Scale `epochs`, the dataset size,
|
|
505
508
|
and `group_size` to the model and the data you actually have before you trust a
|
|
506
509
|
result. Don't cite budget alone as the reason for an underpowered run.
|
|
507
510
|
- **Use the data you have.** Deliberately assign every usable row to training or to a
|
|
@@ -513,7 +516,7 @@ on a beyond-noise improvement.
|
|
|
513
516
|
## Treat crashes as infra, not model size
|
|
514
517
|
|
|
515
518
|
> A CUDA / OOM / vLLM / kernel / infrastructure error is an **infrastructure** problem, not a
|
|
516
|
-
> sign the model is too big. Lower `
|
|
519
|
+
> sign the model is too big. Lower `max_context_tokens`, `max_completion_tokens`, or `group_size` to shrink
|
|
517
520
|
> the run's footprint and let the allocator retry onto the next fitting GPU class — do
|
|
518
521
|
> **not** switch to a smaller model to make a crash disappear. That silently destroys
|
|
519
522
|
> quality.
|
|
@@ -73,11 +73,11 @@ def _fmt_duration(seconds: float) -> str:
|
|
|
73
73
|
|
|
74
74
|
def setup_seconds(config: RunConfig) -> float:
|
|
75
75
|
"""Cold-start wall time before the first optimizer step: container boot + deps + model load
|
|
76
|
-
(a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for
|
|
76
|
+
(a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for rollouts.
|
|
77
77
|
This elapsed setup time is reported but not included in customer-facing cost."""
|
|
78
78
|
model_load = MODEL_LOAD_BASE_S + download_weight_gb(config.model_id) / DOWNLOAD_RATE_GBPS
|
|
79
79
|
s = WORKER_BOOT_S + DEPS_INSTALL_S + model_load
|
|
80
|
-
if config.
|
|
80
|
+
if config.has_rollout:
|
|
81
81
|
s += VLLM_INIT_S
|
|
82
82
|
return s
|
|
83
83
|
|
|
@@ -10,6 +10,7 @@ import math
|
|
|
10
10
|
from flash.catalog import samples_on_policy
|
|
11
11
|
from flash.cost.analytical import estimate_cost
|
|
12
12
|
from flash.cost.types import CostEstimate, RunConfig
|
|
13
|
+
from flash.engine.steps import on_policy_steps
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def _sft_epochs(spec) -> int:
|
|
@@ -19,13 +20,21 @@ def _sft_epochs(spec) -> int:
|
|
|
19
20
|
return int(t.epochs) if t.epochs is not None else RECIPE.sft.num_epochs
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def _on_policy_epochs(spec) -> int:
|
|
24
|
+
from flash.engine.recipe import RECIPE
|
|
25
|
+
|
|
26
|
+
t = spec.train
|
|
27
|
+
default = RECIPE.rl.num_epochs if spec.algorithm == "grpo" else RECIPE.opd.num_epochs
|
|
28
|
+
return int(t.epochs) if t.epochs is not None else default
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
def _sft_seq_len(spec) -> int:
|
|
23
32
|
from flash.engine.recipe import RECIPE
|
|
24
33
|
|
|
25
34
|
t = spec.train
|
|
26
35
|
return (
|
|
27
|
-
int(t.
|
|
28
|
-
if t.
|
|
36
|
+
int(t.max_context_tokens)
|
|
37
|
+
if t.max_context_tokens is not None
|
|
29
38
|
else (RECIPE.sft.max_seq_len_thinking if spec.thinking else RECIPE.sft.max_seq_len)
|
|
30
39
|
)
|
|
31
40
|
|
|
@@ -41,6 +50,43 @@ def _sft_example_count(spec) -> int:
|
|
|
41
50
|
)
|
|
42
51
|
|
|
43
52
|
|
|
53
|
+
def _on_policy_example_count(spec) -> int:
|
|
54
|
+
t = spec.train
|
|
55
|
+
pinned_examples = int(t.max_examples) if t.max_examples else 0
|
|
56
|
+
if pinned_examples > 0:
|
|
57
|
+
return pinned_examples
|
|
58
|
+
env_examples = _env_max_examples(spec)
|
|
59
|
+
if env_examples > 0:
|
|
60
|
+
return env_examples
|
|
61
|
+
return _on_policy_requested_prompts_per_step(spec)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _env_max_examples(spec) -> int:
|
|
65
|
+
params = getattr(getattr(spec, "environment", None), "params", {}) or {}
|
|
66
|
+
if not isinstance(params, dict):
|
|
67
|
+
return 0
|
|
68
|
+
try:
|
|
69
|
+
value = int(params.get("max_examples") or 0)
|
|
70
|
+
except (TypeError, ValueError):
|
|
71
|
+
return 0
|
|
72
|
+
return max(0, value)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _on_policy_requested_prompts_per_step(spec) -> int:
|
|
76
|
+
from flash.engine.recipe import RECIPE
|
|
77
|
+
|
|
78
|
+
t = spec.train
|
|
79
|
+
default = (
|
|
80
|
+
RECIPE.rl.prompts_per_step if spec.algorithm == "grpo" else RECIPE.opd.prompts_per_step
|
|
81
|
+
)
|
|
82
|
+
return max(1, int(t.batch_size) if t.batch_size is not None else default)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _on_policy_prompts_per_step(spec, examples: int) -> int:
|
|
86
|
+
requested = _on_policy_requested_prompts_per_step(spec)
|
|
87
|
+
return max(1, min(requested, max(1, int(examples))))
|
|
88
|
+
|
|
89
|
+
|
|
44
90
|
def _sft_realized_batch(spec) -> int:
|
|
45
91
|
from flash.catalog import vocab_size_for
|
|
46
92
|
from flash.engine.recipe import RECIPE
|
|
@@ -68,22 +114,26 @@ def _sft_steps_from_examples(spec, examples: int, *, apply_cap: bool) -> int:
|
|
|
68
114
|
|
|
69
115
|
|
|
70
116
|
def spec_steps(spec) -> int:
|
|
71
|
-
"""Per-seed optimizer steps implied by a train spec (mirrors the worker).
|
|
72
|
-
(else recipe default). SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by
|
|
73
|
-
``max_steps``, where ``num_examples`` must be pinned by ``max_examples``."""
|
|
74
|
-
from flash.engine.recipe import RECIPE
|
|
117
|
+
"""Per-seed optimizer steps implied by a train spec (mirrors the worker).
|
|
75
118
|
|
|
76
|
-
|
|
119
|
+
SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by ``max_steps``. GRPO/OPD:
|
|
120
|
+
``epochs`` means passes over ``max_examples`` rows when pinned, otherwise the estimate uses one
|
|
121
|
+
prompt batch per epoch.
|
|
122
|
+
"""
|
|
77
123
|
if spec.algorithm == "grpo":
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
examples = _on_policy_example_count(spec)
|
|
125
|
+
return on_policy_steps(
|
|
126
|
+
epochs=_on_policy_epochs(spec),
|
|
127
|
+
prompt_count=examples,
|
|
128
|
+
prompts_per_step=_on_policy_prompts_per_step(spec, examples),
|
|
129
|
+
)
|
|
81
130
|
if spec.algorithm == "opd":
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
131
|
+
examples = _on_policy_example_count(spec)
|
|
132
|
+
return on_policy_steps(
|
|
133
|
+
epochs=_on_policy_epochs(spec),
|
|
134
|
+
prompt_count=examples,
|
|
135
|
+
prompts_per_step=_on_policy_prompts_per_step(spec, examples),
|
|
136
|
+
)
|
|
87
137
|
# max_examples is a CAP; 0 (like None) means "no cap" (worker trains the full dataset), so
|
|
88
138
|
# don't let max_examples=0 price a single step.
|
|
89
139
|
return _sft_steps_from_examples(spec, _sft_example_count(spec), apply_cap=True)
|
|
@@ -101,8 +151,8 @@ def runconfig_from_spec(spec) -> RunConfig:
|
|
|
101
151
|
model_id=spec.model,
|
|
102
152
|
method=spec.algorithm,
|
|
103
153
|
steps=spec_steps(spec),
|
|
104
|
-
seq_len=t.
|
|
105
|
-
completion_len=t.
|
|
154
|
+
seq_len=t.max_context_tokens,
|
|
155
|
+
completion_len=t.max_completion_tokens if has_rollout else None,
|
|
106
156
|
batch_size=t.batch_size,
|
|
107
157
|
group_size=t.group_size if has_rollout else None,
|
|
108
158
|
lora_rank=t.lora_rank,
|
|
@@ -17,10 +17,10 @@ class RunConfig:
|
|
|
17
17
|
method: str # "sft" | "grpo" | "opd"
|
|
18
18
|
steps: int
|
|
19
19
|
|
|
20
|
-
# Engine context length (forwarded as [train].
|
|
20
|
+
# Engine context length (forwarded as [train].max_context_tokens, NOT prompt length). When unset the
|
|
21
21
|
# GRPO default mirrors the worker's max(1024, max_prompt_len + completion); see normalized().
|
|
22
22
|
seq_len: int | None = None
|
|
23
|
-
completion_len: int | None = None # GRPO only (
|
|
23
|
+
completion_len: int | None = None # GRPO/OPD only (max_completion_tokens)
|
|
24
24
|
batch_size: int | None = None # SFT effective batch / GRPO prompts_per_step
|
|
25
25
|
group_size: int | None = None # GRPO completions per prompt (G)
|
|
26
26
|
lora_rank: int | None = None
|
|
@@ -82,7 +82,7 @@ class RunConfig:
|
|
|
82
82
|
comp = self.completion_len
|
|
83
83
|
if comp is None:
|
|
84
84
|
comp = rc.max_completion_len_thinking if self.thinking else rc.max_completion_len
|
|
85
|
-
# Explicit pin wins; else mirror the allocator's rollout sizing of an unset
|
|
85
|
+
# Explicit pin wins; else mirror the allocator's rollout sizing of an unset context:
|
|
86
86
|
# max(1024, max_prompt_len + completion), not bare max_prompt_len (which under-sizes).
|
|
87
87
|
seq = (
|
|
88
88
|
self.seq_len
|
|
@@ -116,9 +116,9 @@ class RunConfig:
|
|
|
116
116
|
if self.batch_size is not None:
|
|
117
117
|
knobs["batch_size"] = self.batch_size
|
|
118
118
|
if n.seq_len is not None:
|
|
119
|
-
knobs["
|
|
119
|
+
knobs["max_context_tokens"] = n.seq_len
|
|
120
120
|
if n.completion_len is not None:
|
|
121
|
-
knobs["
|
|
121
|
+
knobs["max_completion_tokens"] = n.completion_len
|
|
122
122
|
if n.group_size is not None:
|
|
123
123
|
knobs["group_size"] = n.group_size
|
|
124
124
|
return knobs
|
|
@@ -36,11 +36,11 @@ class RLConfig:
|
|
|
36
36
|
learning_rate: float = 1e-5
|
|
37
37
|
max_prompt_len: int = 2048
|
|
38
38
|
max_completion_len: int = 320
|
|
39
|
-
# 320 is too short for <think> blocks; overridable via [train].
|
|
39
|
+
# 320 is too short for <think> blocks; overridable via [train].max_completion_tokens.
|
|
40
40
|
max_completion_len_thinking: int = 1536
|
|
41
41
|
prompts_per_step: int = 64
|
|
42
42
|
group_size: int = 8
|
|
43
|
-
|
|
43
|
+
num_epochs: int = 1
|
|
44
44
|
sampling_temperature: float = 1.0
|
|
45
45
|
sampling_top_p: float = 1.0
|
|
46
46
|
|
|
@@ -54,17 +54,16 @@ class OPDConfig:
|
|
|
54
54
|
# platform-managed and intentionally not configurable from [train].
|
|
55
55
|
teacher_model: str = "accounts/fireworks/models/glm-5p2"
|
|
56
56
|
teacher_base_url: str = "https://api.fireworks.ai/inference/v1"
|
|
57
|
-
# OPD is step-driven like GRPO (on-policy sampling), not epoch-driven like SFT.
|
|
58
|
-
num_steps: int = 100
|
|
59
57
|
learning_rate: float = 1e-5
|
|
60
58
|
max_prompt_len: int = 1024
|
|
61
59
|
max_completion_len: int = 512
|
|
62
|
-
# 512 is short for <think> traces; overridable via [train].
|
|
60
|
+
# 512 is short for <think> traces; overridable via [train].max_completion_tokens.
|
|
63
61
|
max_completion_len_thinking: int = 1536
|
|
64
62
|
prompts_per_step: int = 8
|
|
65
63
|
# Student samples per prompt. 1 is enough for a direct KD loss (no group-relative baseline
|
|
66
64
|
# as in GRPO); raise for more teacher-scored coverage per prompt at higher cost.
|
|
67
65
|
group_size: int = 1
|
|
66
|
+
num_epochs: int = 1
|
|
68
67
|
sampling_temperature: float = 1.0
|
|
69
68
|
sampling_top_p: float = 1.0
|
|
70
69
|
# Reverse-KL coefficient for the groupwise reverse-KL loss; scales the per-span
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Training-step derivation shared by worker and cost-estimate paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def on_policy_steps(
|
|
9
|
+
*,
|
|
10
|
+
epochs: int,
|
|
11
|
+
prompt_count: int,
|
|
12
|
+
prompts_per_step: int,
|
|
13
|
+
) -> int:
|
|
14
|
+
"""Resolve GRPO/OPD optimizer steps from full passes over the retained prompt pool."""
|
|
15
|
+
prompt_count = int(prompt_count)
|
|
16
|
+
if prompt_count <= 0:
|
|
17
|
+
raise ValueError("cannot derive epoch-based steps without at least one retained prompt")
|
|
18
|
+
return max(1, math.ceil(prompt_count * int(epochs) / max(1, int(prompts_per_step))))
|
|
@@ -96,6 +96,20 @@ def opd_rollout_seq_len(
|
|
|
96
96
|
return int(max_length or max(1024, RECIPE.opd.max_prompt_len + completion))
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def opd_rollout_concurrency(prompts_per_step: int = 1, group_size: int = 1) -> int:
|
|
100
|
+
"""Concurrent student generations in one OPD vLLM rollout batch."""
|
|
101
|
+
|
|
102
|
+
def _positive_int(value: object, default: int) -> int:
|
|
103
|
+
try:
|
|
104
|
+
if isinstance(value, bool):
|
|
105
|
+
return default
|
|
106
|
+
return max(1, int(value))
|
|
107
|
+
except (TypeError, ValueError):
|
|
108
|
+
return default
|
|
109
|
+
|
|
110
|
+
return _positive_int(prompts_per_step, 1) * _positive_int(group_size, 1)
|
|
111
|
+
|
|
112
|
+
|
|
99
113
|
def _resident_kv_gb(
|
|
100
114
|
params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
|
|
101
115
|
) -> float:
|
|
@@ -188,6 +202,12 @@ def colocate_kv_util(
|
|
|
188
202
|
_TRAIN_COEF = 0.27
|
|
189
203
|
# Small-model colocated GRPO floor: 0.8B OOMs 20 GB; 2B OOMs 24 GB -> both need 32 GB tier.
|
|
190
204
|
_VLLM_COLOCATE_FLOOR_GB = 28.0
|
|
205
|
+
# OPD builds its resident vLLM engine after the HF/PEFT student is already loaded. A real
|
|
206
|
+
# Qwen3.5-2B OPD run with vLLM failed vLLM startup on a 32 GB RTX 5090 despite the raw equation
|
|
207
|
+
# estimating ~28 GB with headroom, because vLLM requires the requested executor budget to be free at
|
|
208
|
+
# init time. Keep 2B+ OPD off <=40 GB classes so the allocator picks an 80 GB-class card instead of a
|
|
209
|
+
# consumer GPU that passes the aggregate estimate but fails the vLLM free-memory preflight.
|
|
210
|
+
_OPD_VLLM_COLOCATE_FLOOR_GB = 41.0
|
|
191
211
|
_VOCAB_DEFAULT = 248_320
|
|
192
212
|
_LOGITS_BUDGET_GB = 6.0
|
|
193
213
|
# 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
|
|
@@ -328,6 +348,14 @@ def estimate_vram_gb(
|
|
|
328
348
|
train = activations + logits
|
|
329
349
|
return base + (max(rollout, train) if sleep_offload else rollout + train)
|
|
330
350
|
if is_opd:
|
|
351
|
+
# OPD always samples the student through a resident colocated vLLM engine, while the HF/PEFT
|
|
352
|
+
# trainer model stays resident for the GKD loss forward/backward. Budget the second bf16
|
|
353
|
+
# weight copy plus a viable KV pool through the training step. ``use_vllm`` is intentionally
|
|
354
|
+
# ignored for OPD; it only exists for legacy GRPO sizing callers.
|
|
355
|
+
rollout_concurrency = opd_rollout_concurrency(batch_size, group_size)
|
|
356
|
+
rollout = weights + max(
|
|
357
|
+
_KV_CAP, _resident_kv_gb(eff_b, seq_len, rollout_concurrency, fp8_kv=fp8_kv)
|
|
358
|
+
)
|
|
331
359
|
# opd's gkd loss forward materializes DENSE logits — there is NO fused cross-entropy: the
|
|
332
360
|
# student forward yields full-sequence bf16 logits [seq, vocab], then the completion rows are
|
|
333
361
|
# gathered in fp32 [completion, vocab] for the logsumexp. The SFT fused path budgets ZERO
|
|
@@ -357,8 +385,11 @@ def estimate_vram_gb(
|
|
|
357
385
|
logits_fwd = (seq_len * 2 + completion * 4) * vocab
|
|
358
386
|
logits_bwd = (seq_len * 2 + completion * 4) * vocab
|
|
359
387
|
logits = (logits_fwd + logits_bwd) / 1e9
|
|
360
|
-
return base + activations + logits
|
|
361
|
-
fused
|
|
388
|
+
return base + rollout + activations + logits
|
|
389
|
+
# Actual TRL SFT keeps fused CE disabled (see worker/sft.py), so dense logits materialize even
|
|
390
|
+
# for long-context / >=3B models. Callers can pass sft_fused_ce=True only for theoretical
|
|
391
|
+
# comparisons; the default must mirror the worker.
|
|
392
|
+
fused = False if sft_fused_ce is None else bool(sft_fused_ce)
|
|
362
393
|
pd = sft_per_device(batch_size, seq_len=seq_len, vocab=vocab, fused=fused)
|
|
363
394
|
activations = _ACT_COEF * pd * (seq_len / 1024.0) * width
|
|
364
395
|
# Don't clamp to budget: pd=1 is irreducible and the logits can exceed the budget at near-2048 ctx.
|
|
@@ -516,7 +547,7 @@ def model_required_vram_gb(
|
|
|
516
547
|
except (TypeError, ValueError):
|
|
517
548
|
return default
|
|
518
549
|
|
|
519
|
-
max_tokens = _pos_int(_g(train, "
|
|
550
|
+
max_tokens = _pos_int(_g(train, "max_completion_tokens"), None)
|
|
520
551
|
_algo = (algorithm or "").lower()
|
|
521
552
|
if _algo in ("grpo", "rl"):
|
|
522
553
|
_default_len = grpo_rollout_seq_len(0, max_tokens, thinking)
|
|
@@ -525,10 +556,18 @@ def model_required_vram_gb(
|
|
|
525
556
|
_default_len = opd_rollout_seq_len(0, max_tokens, thinking)
|
|
526
557
|
else:
|
|
527
558
|
_default_len = 1024
|
|
528
|
-
seq_len = _pos_int(_g(train, "
|
|
559
|
+
seq_len = _pos_int(_g(train, "max_context_tokens"), _default_len)
|
|
529
560
|
lora_rank = _pos_int(_g(train, "lora_rank"), 32)
|
|
530
|
-
|
|
531
|
-
|
|
561
|
+
if _algo == "opd":
|
|
562
|
+
from flash.engine.recipe import RECIPE
|
|
563
|
+
|
|
564
|
+
batch_size_default = int(RECIPE.opd.prompts_per_step)
|
|
565
|
+
group_size_default = int(RECIPE.opd.group_size)
|
|
566
|
+
else:
|
|
567
|
+
batch_size_default = _sft_per_device_bs()
|
|
568
|
+
group_size_default = 8
|
|
569
|
+
group_size = _pos_int(_g(train, "group_size"), group_size_default)
|
|
570
|
+
batch_size = _pos_int(_g(train, "batch_size"), batch_size_default)
|
|
532
571
|
|
|
533
572
|
def _need(
|
|
534
573
|
params_b: float,
|
|
@@ -557,17 +596,21 @@ def model_required_vram_gb(
|
|
|
557
596
|
return math.ceil(est * headroom)
|
|
558
597
|
|
|
559
598
|
from flash.catalog import MODELS, vocab_size_for
|
|
560
|
-
from flash.engine.chalk_kernels import chalk_supports_fused_ce_model
|
|
561
599
|
|
|
562
600
|
info = MODELS.get(model_id)
|
|
563
601
|
model_vocab = vocab_size_for(model_id)
|
|
564
|
-
is_grpo =
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
and chalk_supports_fused_ce_model(model_id)
|
|
602
|
+
is_grpo = _algo in ("grpo", "rl")
|
|
603
|
+
is_opd = _algo == "opd"
|
|
604
|
+
is_vllm_rollout = is_grpo or is_opd
|
|
605
|
+
vllm_concurrency = (
|
|
606
|
+
opd_rollout_concurrency(batch_size, group_size) if is_opd else group_size
|
|
570
607
|
)
|
|
608
|
+
# Worker parity: TRL SFT deliberately calls install_chalk_kernels(..., fused_ce=False), because
|
|
609
|
+
# SFTTrainer.compute_loss reads outputs.logits and crashes when fused CE returns logits=None. So
|
|
610
|
+
# every SFT run materializes dense logits, even for long context / >=3B models where the old
|
|
611
|
+
# allocator assumed a fused CE path. Size SFT with fused CE OFF up front or long-context Qwen SFT
|
|
612
|
+
# routes to consumer cards and OOMs on the first backward.
|
|
613
|
+
sft_fused_ce = None if is_grpo else False
|
|
571
614
|
if info is not None:
|
|
572
615
|
params_b = info.params_b # curated, authoritative (required field) — no string parsing
|
|
573
616
|
quant = getattr(info, "quant", "bf16") or "bf16"
|
|
@@ -620,29 +663,39 @@ def model_required_vram_gb(
|
|
|
620
663
|
else:
|
|
621
664
|
floor += grpo_seq_escalation_gb(active_b or params_b, seq_len)
|
|
622
665
|
need = max(need, floor)
|
|
623
|
-
if
|
|
666
|
+
if is_vllm_rollout and use_vllm:
|
|
624
667
|
floor_gb = 24 if (params_b or 0.0) <= 1.0 else int(_VLLM_COLOCATE_FLOOR_GB)
|
|
668
|
+
if is_opd and (params_b or 0.0) >= 2.0:
|
|
669
|
+
floor_gb = max(floor_gb, int(_OPD_VLLM_COLOCATE_FLOOR_GB))
|
|
625
670
|
need = max(need, floor_gb)
|
|
626
671
|
# vLLM KV-cache init preflight: the card must leave a viable cache-block pool
|
|
627
672
|
# under the colocate utilization cap, or the engine dies at init ("No available
|
|
628
673
|
# memory for the cache blocks") on a card the training-peak estimate accepted.
|
|
629
674
|
need = max(
|
|
630
675
|
need,
|
|
631
|
-
grpo_kv_floor_gb(
|
|
676
|
+
grpo_kv_floor_gb(
|
|
677
|
+
params_b or 4.0,
|
|
678
|
+
seq_len,
|
|
679
|
+
vllm_concurrency,
|
|
680
|
+
active_params_b=active_b,
|
|
681
|
+
),
|
|
632
682
|
)
|
|
633
683
|
return need
|
|
634
684
|
params_b = fetch_hf_params_b(model_id)
|
|
635
685
|
if params_b is None:
|
|
636
686
|
return 24
|
|
637
687
|
# Size the uncataloged (model_policy="allow") fallback with the ACTUAL algorithm, not a hardcoded
|
|
638
|
-
# "grpo".
|
|
639
|
-
#
|
|
640
|
-
#
|
|
641
|
-
# _need; do the same here (codex[bot]). The grpo-only escalations below stay gated on is_grpo.
|
|
688
|
+
# "grpo". The cataloged path above already threads `algorithm` through _need; do the same here so
|
|
689
|
+
# open-model OPD uses its own dense-logit + colocated-vLLM estimator. The grpo-only sequence
|
|
690
|
+
# escalation stays gated on is_grpo.
|
|
642
691
|
need = _need(params_b, algorithm, vocab=model_vocab)
|
|
643
692
|
if is_grpo:
|
|
644
693
|
need += grpo_seq_escalation_gb(params_b, seq_len)
|
|
645
|
-
|
|
694
|
+
if is_vllm_rollout:
|
|
695
|
+
floor_gb = 24 if params_b <= 1.0 else int(_VLLM_COLOCATE_FLOOR_GB)
|
|
696
|
+
if is_opd and params_b >= 2.0:
|
|
697
|
+
floor_gb = max(floor_gb, int(_OPD_VLLM_COLOCATE_FLOOR_GB))
|
|
698
|
+
need = max(need, floor_gb, grpo_kv_floor_gb(params_b, seq_len, vllm_concurrency))
|
|
646
699
|
return need
|
|
647
700
|
|
|
648
701
|
|
|
@@ -137,7 +137,7 @@ def grpo_overrides() -> dict:
|
|
|
137
137
|
cfg = {
|
|
138
138
|
"group_size": train.group_size,
|
|
139
139
|
"temperature": train.temperature,
|
|
140
|
-
"max_tokens": train.
|
|
140
|
+
"max_tokens": train.max_completion_tokens,
|
|
141
141
|
"kl_penalty_coef": train.kl_penalty_coef,
|
|
142
142
|
"advantage_clip": train.advantage_clip,
|
|
143
143
|
"thinking_length_penalty_coef": train.thinking_length_penalty_coef,
|
|
@@ -173,7 +173,7 @@ def resolve_grpo_sleep_mode() -> tuple[bool, int, float, bool]:
|
|
|
173
173
|
spec = _w.JOB_SPEC
|
|
174
174
|
train = spec.train if spec else None
|
|
175
175
|
model_id = spec.model if spec else ""
|
|
176
|
-
ctx = int(train.
|
|
176
|
+
ctx = int(train.max_context_tokens if train and train.max_context_tokens else 0)
|
|
177
177
|
gcfg = _w.grpo_overrides()
|
|
178
178
|
group_size = int(gcfg.get("group_size") or RECIPE.rl.group_size)
|
|
179
179
|
lora_rank = int(train.lora_rank) if train and train.lora_rank else 32
|