freesolo-flash-dev 1.0.8__tar.gz → 1.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.env.example +1 -1
- freesolo_flash_dev-1.0.9/AB_PLAN.md +86 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/PKG-INFO +3 -3
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/README.md +2 -2
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/__init__.py +1 -1
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/training_doc.py +2 -2
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/vram.py +72 -1
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/hf.py +101 -5
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd.py +7 -1
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_vllm.py +120 -20
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/lora_rank.py +5 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/jobs.py +24 -5
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/__init__.py +11 -2
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/deploy.py +1 -1
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/preflight.py +1 -5
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_async_uploads.py +55 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_help.py +1 -1
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_jobs.py +66 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_vllm.py +191 -3
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_api.py +26 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/uv.lock +1 -1
- freesolo_flash_dev-1.0.8/AB_PLAN.md +0 -45
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/catalog.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_multimodal.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/schema/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_cudagraphs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_perturn.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multi_gpu_gate.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_image_rollout.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_per_turn_reward.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_request_policy.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_activation_shaping.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_chunked_nll.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_tokenize_cache.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_single_turn_multimodal_grpo.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_thinking.py +0 -0
|
@@ -31,4 +31,4 @@ FREESOLO_BASE_URL=http://backend:8000
|
|
|
31
31
|
# Modal app that serves every adapter on one GPU per base model, scaling to zero when
|
|
32
32
|
# idle). `flash deploy`/`undeploy`/`chat` register/deregister/chat against it. Defaults
|
|
33
33
|
# to the hosted Modal URL when unset.
|
|
34
|
-
FREESOLO_SERVING_URL=https://
|
|
34
|
+
FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# OPD rollout-memory utilization A/B plan
|
|
2
|
+
|
|
3
|
+
## Question
|
|
4
|
+
|
|
5
|
+
Does sizing the colocated vLLM executor from measured post-load free VRAM, while reserving the modeled OPD training peak and allocator margin, eliminate the Qwen3.5 4B negative-KV startup failure and improve rollout throughput without changing training behavior or causing an OOM?
|
|
6
|
+
|
|
7
|
+
No paid GPU run is part of this change.
|
|
8
|
+
|
|
9
|
+
## Frozen comparison
|
|
10
|
+
|
|
11
|
+
- control: `origin/dev` at `8fdfc858`
|
|
12
|
+
- treatment: branch `perf/opd-rollout-util`
|
|
13
|
+
- hardware: one H100 80 GB for each arm, from the same RunPod GPU type and image
|
|
14
|
+
- launch both arms concurrently on separate matched GPUs
|
|
15
|
+
- use the same base checkpoint, dataset revision, environment revision, seed, teacher, token budget, batch order, and worker image inputs
|
|
16
|
+
- use immutable commits for both arms
|
|
17
|
+
|
|
18
|
+
An H100 80 GB is the smallest card for this A/B. The reported failure is a large-card utilization defect, with roughly 60 GB left idle and a negative-KV startup failure for Qwen3.5 4B. An RTX 4090 24 GB does not have enough residual memory to expose the intended utilization increase and therefore cannot demonstrate the optimization.
|
|
19
|
+
|
|
20
|
+
## OPD configuration
|
|
21
|
+
|
|
22
|
+
Use the existing reproducing environment and dataset with this training shape held constant:
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
model = "Qwen/Qwen3.5-4B"
|
|
26
|
+
algorithm = "opd"
|
|
27
|
+
seed = 42
|
|
28
|
+
|
|
29
|
+
[environment]
|
|
30
|
+
id = "<frozen-reproduction-environment>"
|
|
31
|
+
|
|
32
|
+
[train]
|
|
33
|
+
epochs = 1
|
|
34
|
+
batch_size = 8
|
|
35
|
+
group_size = 1
|
|
36
|
+
max_context_tokens = 1536
|
|
37
|
+
max_completion_tokens = 512
|
|
38
|
+
lora_rank = 32
|
|
39
|
+
teacher_model = "glm-5.2"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the same bounded number of optimizer steps in both arms. Do not tune either arm after launch.
|
|
43
|
+
|
|
44
|
+
## Measurements
|
|
45
|
+
|
|
46
|
+
Record at vLLM initialization and for every rollout step:
|
|
47
|
+
|
|
48
|
+
1. measured free and total VRAM immediately before vLLM construction
|
|
49
|
+
2. modeled OPD training peak reserve, allocator margin, rollout budget, and final `gpu_memory_utilization`
|
|
50
|
+
3. vLLM executor budget in GiB and available KV-cache budget or cache blocks
|
|
51
|
+
4. rollout batch size and generated tokens per second
|
|
52
|
+
5. peak allocated and peak reserved VRAM across the OPD loss forward/backward
|
|
53
|
+
6. initialization outcome, negative-KV/cache-block errors, CUDA OOMs, and worker health
|
|
54
|
+
7. per-step OPD loss, reward or task metric already emitted by the environment, and completed optimizer steps
|
|
55
|
+
|
|
56
|
+
## Gates
|
|
57
|
+
|
|
58
|
+
### Feasibility and memory safety
|
|
59
|
+
|
|
60
|
+
- treatment must initialize vLLM and complete every planned optimizer step with zero negative-KV/cache-block failures and zero CUDA OOMs
|
|
61
|
+
- treatment executor budget must never exceed measured free VRAM minus the logged training reserve and allocator margin
|
|
62
|
+
- treatment loss forward/backward peak must retain at least the allocator margin after accounting for the executor budget
|
|
63
|
+
- if the control reproduces the startup crash, treatment must complete the same configuration; otherwise the A/B is inconclusive on crash recovery but can still evaluate utilization and throughput
|
|
64
|
+
|
|
65
|
+
### Utilization and throughput
|
|
66
|
+
|
|
67
|
+
- direction: treatment `gpu_memory_utilization` and KV budget must be higher than control on the H100
|
|
68
|
+
- threshold: treatment executor budget must increase by at least 16 GiB or utilization by at least 0.20 absolute
|
|
69
|
+
- direction: treatment rollout generated tokens per second must be no worse than control
|
|
70
|
+
- threshold: when both arms complete, treatment median steady-state rollout throughput must improve by at least 10 percent; if control crashes, report treatment throughput without claiming a relative speedup
|
|
71
|
+
|
|
72
|
+
### Training parity
|
|
73
|
+
|
|
74
|
+
- both arms must consume the same prompts and completion-token budget and complete the same optimizer-step count
|
|
75
|
+
- per-checkpoint OPD loss curves must remain within normal deterministic/runtime noise, with no sustained treatment regression above 2 percent relative to control
|
|
76
|
+
- final task metric must not regress by more than 1 percentage point absolute
|
|
77
|
+
- any change in gradients, optimizer state, sampled inputs, or training math invalidates the A/B
|
|
78
|
+
|
|
79
|
+
## Decision
|
|
80
|
+
|
|
81
|
+
Accept the optimization only if the treatment passes all memory-safety and training-parity gates and either:
|
|
82
|
+
|
|
83
|
+
1. recovers the exact control startup failure, or
|
|
84
|
+
2. meets both the executor-budget and throughput thresholds when both arms complete.
|
|
85
|
+
|
|
86
|
+
Reject or revise if treatment OOMs, violates the protected-memory budget, changes the training curve beyond the parity threshold, or fails to increase the H100 rollout budget materially.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI
|
|
5
5
|
Project-URL: Homepage, https://github.com/freesolo-co/flash
|
|
6
6
|
Project-URL: Repository, https://github.com/freesolo-co/flash
|
|
@@ -186,12 +186,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
|
|
|
186
186
|
with a hint to deploy first.
|
|
187
187
|
|
|
188
188
|
Operators can also call the serving backend directly after the adapter is registered.
|
|
189
|
-
The default serving backend is `https://
|
|
189
|
+
The default serving backend is `https://serve.freesolo.co`, and
|
|
190
190
|
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
191
191
|
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
192
192
|
|
|
193
193
|
```bash
|
|
194
|
-
export FREESOLO_SERVING_URL=https://
|
|
194
|
+
export FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
195
195
|
|
|
196
196
|
curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
|
|
197
197
|
-H "Content-Type: application/json" \
|
|
@@ -129,12 +129,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
|
|
|
129
129
|
with a hint to deploy first.
|
|
130
130
|
|
|
131
131
|
Operators can also call the serving backend directly after the adapter is registered.
|
|
132
|
-
The default serving backend is `https://
|
|
132
|
+
The default serving backend is `https://serve.freesolo.co`, and
|
|
133
133
|
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
134
134
|
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
export FREESOLO_SERVING_URL=https://
|
|
137
|
+
export FREESOLO_SERVING_URL=https://serve.freesolo.co
|
|
138
138
|
|
|
139
139
|
curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
|
|
140
140
|
-H "Content-Type: application/json" \
|
|
@@ -162,7 +162,7 @@ class _FlashParser(_ThemedParser):
|
|
|
162
162
|
f"train after publishing: `{CLI_NAME} env push --name my-env .`, "
|
|
163
163
|
f"then `{CLI_NAME} train configs/sft.toml`",
|
|
164
164
|
f"any command in depth: `{CLI_NAME} <command> --help`",
|
|
165
|
-
"docs: https://freesolo.co
|
|
165
|
+
"docs: https://docs.freesolo.co",
|
|
166
166
|
]
|
|
167
167
|
return render.help_page(
|
|
168
168
|
"managed LoRA post-training", usage, _HELP_GROUPS, _HELP_OPTIONS, footers
|
|
@@ -8,7 +8,7 @@ TRAINING_MD = r"""# TRAINING.md — how to actually improve a model with Flash
|
|
|
8
8
|
> `flash env setup` dropped this file next to your `environment.py` and `configs/`.
|
|
9
9
|
> It is the playbook Freesolo's own training agents follow to turn a *finished*
|
|
10
10
|
> run into a model that *measurably improved*. The mechanics live in the hosted
|
|
11
|
-
> docs (https://freesolo.co
|
|
11
|
+
> docs (https://docs.freesolo.co); this file is the judgment that sits on top of them.
|
|
12
12
|
|
|
13
13
|
A run that reaches `done` is **not** the same as a run that worked. Submitting a run
|
|
14
14
|
is not a result. The whole job is to design the learning signal, read what the run
|
|
@@ -713,5 +713,5 @@ flash export --adapter-id <run-id> --repository <you>/<repo> # export final ada
|
|
|
713
713
|
flash export --adapter-id <run-id>/step-N --repository <you>/<repo> # export a checkpoint
|
|
714
714
|
```
|
|
715
715
|
|
|
716
|
-
See the full reference at https://freesolo.co
|
|
716
|
+
See the full reference at https://docs.freesolo.co.
|
|
717
717
|
"""
|
|
@@ -27,6 +27,9 @@ _BYTES_PER_PARAM = {
|
|
|
27
27
|
_BASE_OVERHEAD_GB = 4.0
|
|
28
28
|
_ACT_COEF = 0.12
|
|
29
29
|
_SFT_PER_DEVICE_BS_DEFAULT = 4
|
|
30
|
+
_VOCAB_DEFAULT = 248_320
|
|
31
|
+
_OPD_TRAINING_RESERVE_MULTIPLIER = 1.15
|
|
32
|
+
_OPD_ALLOCATOR_MARGIN_MAX_GB = 6.0
|
|
30
33
|
OPD_CE_CHUNK_SIZE = 64
|
|
31
34
|
_OPD_CE_PEAK_BYTES_PER_LOGIT = 16
|
|
32
35
|
|
|
@@ -126,6 +129,75 @@ def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size:
|
|
|
126
129
|
return max(1, min(total, default))
|
|
127
130
|
|
|
128
131
|
|
|
132
|
+
def opd_training_peak_gb(
|
|
133
|
+
params_b: float,
|
|
134
|
+
seq_len: int,
|
|
135
|
+
*,
|
|
136
|
+
max_tokens: int | None = None,
|
|
137
|
+
prompts_per_step: int = 1,
|
|
138
|
+
group_size: int = 1,
|
|
139
|
+
thinking: bool = False,
|
|
140
|
+
vocab: int = _VOCAB_DEFAULT,
|
|
141
|
+
active_params_b: float | None = None,
|
|
142
|
+
) -> float:
|
|
143
|
+
"""Additional OPD loss-forward/backward memory above the resident student model."""
|
|
144
|
+
eff_b = float(active_params_b) if active_params_b else float(params_b)
|
|
145
|
+
width = math.sqrt(max(eff_b, 0.1))
|
|
146
|
+
loss_mb = opd_loss_microbatch(params_b, prompts_per_step, group_size)
|
|
147
|
+
activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
|
|
148
|
+
completion = opd_completion_len(max_tokens, thinking)
|
|
149
|
+
# the backward peak holds both full-sequence bf16 logits and fp32 completion rows plus gradients.
|
|
150
|
+
dense_logits = 2 * loss_mb * (seq_len * 2 + completion * 4) * vocab / 1e9
|
|
151
|
+
return activations + dense_logits
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def opd_training_reserve_gb(
|
|
155
|
+
params_b: float,
|
|
156
|
+
seq_len: int,
|
|
157
|
+
*,
|
|
158
|
+
max_tokens: int | None = None,
|
|
159
|
+
prompts_per_step: int = 1,
|
|
160
|
+
group_size: int = 1,
|
|
161
|
+
thinking: bool = False,
|
|
162
|
+
vocab: int = _VOCAB_DEFAULT,
|
|
163
|
+
active_params_b: float | None = None,
|
|
164
|
+
) -> float:
|
|
165
|
+
"""OPD loss-forward/backward peak with the runtime safety multiplier."""
|
|
166
|
+
return _OPD_TRAINING_RESERVE_MULTIPLIER * opd_training_peak_gb(
|
|
167
|
+
params_b,
|
|
168
|
+
seq_len,
|
|
169
|
+
max_tokens=max_tokens,
|
|
170
|
+
prompts_per_step=prompts_per_step,
|
|
171
|
+
group_size=group_size,
|
|
172
|
+
thinking=thinking,
|
|
173
|
+
vocab=vocab,
|
|
174
|
+
active_params_b=active_params_b,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def opd_allocator_margin_gb(total_vram_gb: float) -> float:
|
|
179
|
+
"""Allocator fragmentation slack protected by the OPD startup budget."""
|
|
180
|
+
return max(2.0, min(_OPD_ALLOCATOR_MARGIN_MAX_GB, float(total_vram_gb) * 0.05))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def opd_post_init_reserve_gb(params_b: float, lora_rank: int) -> float:
|
|
184
|
+
"""Pending LoRA gradient, AdamW state, and persistent post-init growth above model residency.
|
|
185
|
+
|
|
186
|
+
OPD trains all linear layers with bf16 LoRA parameters. PyTorch AdamW lazily allocates two
|
|
187
|
+
parameter-dtype moment tensors on the first step, so each estimated trainable parameter needs
|
|
188
|
+
2 bytes for its pending gradient plus 4 bytes for optimizer state. Use total model parameters for
|
|
189
|
+
the rank-linear geometry so MoE experts are not undercounted, then reserve the measured 35B-class
|
|
190
|
+
first-generation and post-init growth with a size-scaled 12 GB ceiling.
|
|
191
|
+
"""
|
|
192
|
+
model_params_b = max(0.1, float(params_b))
|
|
193
|
+
rank = max(1, int(lora_rank))
|
|
194
|
+
trainable_params_b = (rank / 16.0) * (0.05 + model_params_b / 150.0)
|
|
195
|
+
gradient_gb = trainable_params_b * 2.0
|
|
196
|
+
adamw_state_gb = trainable_params_b * 4.0
|
|
197
|
+
persistent_growth_gb = max(1.0, min(12.0, model_params_b * 0.35))
|
|
198
|
+
return gradient_gb + adamw_state_gb + persistent_growth_gb
|
|
199
|
+
|
|
200
|
+
|
|
129
201
|
def _resident_kv_gb(
|
|
130
202
|
params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
|
|
131
203
|
) -> float:
|
|
@@ -224,7 +296,6 @@ _VLLM_COLOCATE_FLOOR_GB = 28.0
|
|
|
224
296
|
# init time. Keep 2B+ OPD off <=40 GB classes so the allocator picks an 80 GB-class card instead of a
|
|
225
297
|
# consumer GPU that passes the aggregate estimate but fails the vLLM free-memory preflight.
|
|
226
298
|
_OPD_VLLM_COLOCATE_FLOOR_GB = 41.0
|
|
227
|
-
_VOCAB_DEFAULT = 248_320
|
|
228
299
|
_LOGITS_BUDGET_GB = 6.0
|
|
229
300
|
# 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
|
|
230
301
|
_SFT_LOGITS_BYTES_PER_ELEM = 16.0
|
|
@@ -197,6 +197,7 @@ class _OptionalUpload:
|
|
|
197
197
|
label: str
|
|
198
198
|
staged_dir: str
|
|
199
199
|
run: Callable[[], None]
|
|
200
|
+
on_coalesce: Callable[[_OptionalUpload], None] | None = None
|
|
200
201
|
|
|
201
202
|
|
|
202
203
|
class _SingleSlotUploader:
|
|
@@ -209,12 +210,18 @@ class _SingleSlotUploader:
|
|
|
209
210
|
self._next_sequence = 0
|
|
210
211
|
self._thread: threading.Thread | None = None
|
|
211
212
|
|
|
212
|
-
def enqueue(
|
|
213
|
+
def enqueue(
|
|
214
|
+
self,
|
|
215
|
+
label: str,
|
|
216
|
+
staged_dir: str,
|
|
217
|
+
run: Callable[[], None],
|
|
218
|
+
on_coalesce: Callable[[_OptionalUpload], None] | None = None,
|
|
219
|
+
) -> None:
|
|
213
220
|
replaced: _OptionalUpload | None = None
|
|
214
221
|
thread: threading.Thread | None = None
|
|
215
222
|
with self._condition:
|
|
216
223
|
self._next_sequence += 1
|
|
217
|
-
task = _OptionalUpload(self._next_sequence, label, staged_dir, run)
|
|
224
|
+
task = _OptionalUpload(self._next_sequence, label, staged_dir, run, on_coalesce)
|
|
218
225
|
replaced, self._pending = self._pending, task
|
|
219
226
|
if self._thread is None:
|
|
220
227
|
thread = threading.Thread(
|
|
@@ -229,7 +236,11 @@ class _SingleSlotUploader:
|
|
|
229
236
|
f"[upload] coalesced optional {replaced.label} into newer {label} "
|
|
230
237
|
f"(sequence {task.sequence})"
|
|
231
238
|
)
|
|
232
|
-
|
|
239
|
+
if replaced.on_coalesce is not None:
|
|
240
|
+
# the coalesce hook takes over cleanup of the replaced staged tree.
|
|
241
|
+
replaced.on_coalesce(replaced)
|
|
242
|
+
else:
|
|
243
|
+
shutil.rmtree(replaced.staged_dir, ignore_errors=True)
|
|
233
244
|
if thread is not None:
|
|
234
245
|
thread.start()
|
|
235
246
|
|
|
@@ -264,8 +275,68 @@ class _SingleSlotUploader:
|
|
|
264
275
|
return True
|
|
265
276
|
|
|
266
277
|
|
|
278
|
+
class _FifoUploader:
|
|
279
|
+
"""run uploads one at a time in FIFO order, never dropping a pending task.
|
|
280
|
+
|
|
281
|
+
unlike _SingleSlotUploader this keeps every enqueued upload, so per-step artifacts that must
|
|
282
|
+
each land (deployable adapters) are not coalesced away by a newer save.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
def __init__(self) -> None:
|
|
286
|
+
self._condition = threading.Condition()
|
|
287
|
+
self._in_flight: _OptionalUpload | None = None
|
|
288
|
+
self._pending: list[_OptionalUpload] = []
|
|
289
|
+
self._next_sequence = 0
|
|
290
|
+
self._thread: threading.Thread | None = None
|
|
291
|
+
|
|
292
|
+
def enqueue(self, label: str, staged_dir: str, run: Callable[[], None]) -> None:
|
|
293
|
+
thread: threading.Thread | None = None
|
|
294
|
+
with self._condition:
|
|
295
|
+
self._next_sequence += 1
|
|
296
|
+
self._pending.append(_OptionalUpload(self._next_sequence, label, staged_dir, run))
|
|
297
|
+
if self._thread is None:
|
|
298
|
+
thread = threading.Thread(
|
|
299
|
+
target=self._drain, name="flash-deployable-uploader", daemon=True
|
|
300
|
+
)
|
|
301
|
+
self._thread = thread
|
|
302
|
+
self._condition.notify_all()
|
|
303
|
+
if thread is not None:
|
|
304
|
+
thread.start()
|
|
305
|
+
|
|
306
|
+
def _drain(self) -> None:
|
|
307
|
+
while True:
|
|
308
|
+
with self._condition:
|
|
309
|
+
if not self._pending:
|
|
310
|
+
self._thread = None
|
|
311
|
+
self._condition.notify_all()
|
|
312
|
+
return
|
|
313
|
+
task = self._pending.pop(0)
|
|
314
|
+
self._in_flight = task
|
|
315
|
+
try:
|
|
316
|
+
task.run()
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"deployable upload warn ({task.label}): {sanitize_diagnostic(e, limit=500)}")
|
|
319
|
+
finally:
|
|
320
|
+
shutil.rmtree(task.staged_dir, ignore_errors=True)
|
|
321
|
+
with self._condition:
|
|
322
|
+
self._in_flight = None
|
|
323
|
+
self._condition.notify_all()
|
|
324
|
+
|
|
325
|
+
def flush(self, timeout_s: float) -> bool:
|
|
326
|
+
"""wait for the in-flight and all pending FIFO uploads to finish."""
|
|
327
|
+
deadline = time.monotonic() + max(0.0, timeout_s)
|
|
328
|
+
with self._condition:
|
|
329
|
+
while self._in_flight is not None or self._pending:
|
|
330
|
+
remaining = deadline - time.monotonic()
|
|
331
|
+
if remaining <= 0:
|
|
332
|
+
return False
|
|
333
|
+
self._condition.wait(remaining)
|
|
334
|
+
return True
|
|
335
|
+
|
|
336
|
+
|
|
267
337
|
_OPTIONAL_CHECKPOINT_UPLOADER = _SingleSlotUploader()
|
|
268
338
|
_OPTIONAL_AUX_UPLOADER = _SingleSlotUploader()
|
|
339
|
+
_OPTIONAL_DEPLOYABLE_UPLOADER = _FifoUploader()
|
|
269
340
|
_DEBUG_UPLOAD_LOCK = threading.Lock()
|
|
270
341
|
|
|
271
342
|
|
|
@@ -331,8 +402,10 @@ def flush_optional_uploads(timeout_s: float = _OPTIONAL_UPLOAD_FLUSH_TIMEOUT_S)
|
|
|
331
402
|
started = time.monotonic()
|
|
332
403
|
checkpoints_flushed = _OPTIONAL_CHECKPOINT_UPLOADER.flush(timeout_s)
|
|
333
404
|
remaining = max(0.0, timeout_s - (time.monotonic() - started))
|
|
405
|
+
deployables_flushed = _OPTIONAL_DEPLOYABLE_UPLOADER.flush(remaining)
|
|
406
|
+
remaining = max(0.0, timeout_s - (time.monotonic() - started))
|
|
334
407
|
aux_flushed = _OPTIONAL_AUX_UPLOADER.flush(remaining)
|
|
335
|
-
return checkpoints_flushed and aux_flushed
|
|
408
|
+
return checkpoints_flushed and deployables_flushed and aux_flushed
|
|
336
409
|
|
|
337
410
|
|
|
338
411
|
def upload_debug_jsonl(name: str, rows: list[dict], *, keep_last: int = 200) -> None:
|
|
@@ -985,14 +1058,37 @@ def make_checkpoint_upload_callback(save_at_steps=()):
|
|
|
985
1058
|
ckpt_dir, f"checkpoint-{step}"
|
|
986
1059
|
)
|
|
987
1060
|
except Exception as e:
|
|
988
|
-
|
|
1061
|
+
# surface the miss explicitly rather than logging a soft warning and continuing as if
|
|
1062
|
+
# the periodic save reached hf.
|
|
1063
|
+
print(
|
|
1064
|
+
f"[ckpt] step {step} snapshot failed; step not published: "
|
|
1065
|
+
f"{sanitize_diagnostic(e, limit=500)}"
|
|
1066
|
+
)
|
|
989
1067
|
return
|
|
1068
|
+
|
|
1069
|
+
def _publish_coalesced_deployable(
|
|
1070
|
+
replaced: _OptionalUpload,
|
|
1071
|
+
step: int = step,
|
|
1072
|
+
staged_checkpoint: str = staged_checkpoint,
|
|
1073
|
+
) -> None:
|
|
1074
|
+
# a newer optional save coalesced this resume checkpoint away; still publish this step's
|
|
1075
|
+
# small durable deployable through the non-coalescing fifo path (which owns the staged
|
|
1076
|
+
# tree cleanup) so per-step deployables are never dropped.
|
|
1077
|
+
_OPTIONAL_DEPLOYABLE_UPLOADER.enqueue(
|
|
1078
|
+
f"coalesced deployable step {step}",
|
|
1079
|
+
replaced.staged_dir,
|
|
1080
|
+
lambda: publish_deployable_checkpoint(
|
|
1081
|
+
staged_checkpoint, step, _provenance_ready=True, _emit_heartbeat=False
|
|
1082
|
+
),
|
|
1083
|
+
)
|
|
1084
|
+
|
|
990
1085
|
_OPTIONAL_CHECKPOINT_UPLOADER.enqueue(
|
|
991
1086
|
f"checkpoint step {step}",
|
|
992
1087
|
staged_dir,
|
|
993
1088
|
lambda: _upload(
|
|
994
1089
|
step, staged_checkpoint, provenance_ready=True, emit_heartbeat=False
|
|
995
1090
|
),
|
|
1091
|
+
on_coalesce=_publish_coalesced_deployable,
|
|
996
1092
|
)
|
|
997
1093
|
|
|
998
1094
|
class _CheckpointUpload(TrainerCallback):
|
|
@@ -819,10 +819,16 @@ def run_opd():
|
|
|
819
819
|
|
|
820
820
|
# resolve the model/tokenizer halt set once for vllm sampling and termination classification.
|
|
821
821
|
generation_eos_ids = _generation_eos_ids(model, tok)
|
|
822
|
-
vllm_kwargs = _opd_vllm_kwargs(model_id, knobs, seq_cap)
|
|
823
822
|
lora_rank = _opd_lora_rank(
|
|
824
823
|
model, getattr(_w.JOB_SPEC.train, "lora_rank", 32) if _w.JOB_SPEC else 32
|
|
825
824
|
)
|
|
825
|
+
vllm_kwargs = _opd_vllm_kwargs(
|
|
826
|
+
model_id,
|
|
827
|
+
knobs,
|
|
828
|
+
seq_cap,
|
|
829
|
+
prompts_per_step=prompts_per_step,
|
|
830
|
+
lora_rank=lora_rank,
|
|
831
|
+
)
|
|
826
832
|
print(
|
|
827
833
|
f"[opd] rollout backend: colocated vLLM model={rollout_model_source} "
|
|
828
834
|
f"ctx={seq_cap} lora_rank={lora_rank} util={vllm_kwargs['gpu_memory_utilization']:.2f} "
|
|
@@ -92,7 +92,7 @@ def _startup_oom_error(
|
|
|
92
92
|
free_gb: float,
|
|
93
93
|
total_gb: float,
|
|
94
94
|
requested_util: float,
|
|
95
|
-
|
|
95
|
+
reserve_gb: float,
|
|
96
96
|
rollout_batch_size: int,
|
|
97
97
|
) -> BaseException:
|
|
98
98
|
requested_gb = float(requested_util) * float(total_gb)
|
|
@@ -100,8 +100,8 @@ def _startup_oom_error(
|
|
|
100
100
|
"Free memory on device cuda:0 "
|
|
101
101
|
f"({free_gb:.2f}/{total_gb:.2f} GiB) on startup is less than desired GPU "
|
|
102
102
|
f"memory utilization ({requested_util:.6f} -> {requested_gb:.2f} GiB) "
|
|
103
|
-
f"with {
|
|
104
|
-
f"to {rollout_batch_size}. Retry on a larger GPU."
|
|
103
|
+
f"with {reserve_gb:.2f} GiB reserved for the OPD training peak and allocator "
|
|
104
|
+
f"after reducing OPD rollout_batch_size to {rollout_batch_size}. Retry on a larger GPU."
|
|
105
105
|
)
|
|
106
106
|
try:
|
|
107
107
|
import torch
|
|
@@ -118,7 +118,34 @@ def _decode_only_compilation_config() -> dict[str, Any]:
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
def
|
|
121
|
+
def _sizing_lora_rank(knobs: Any, default: int = 32) -> int:
|
|
122
|
+
rank = getattr(knobs, "lora_rank", None)
|
|
123
|
+
if rank is None:
|
|
124
|
+
try:
|
|
125
|
+
from flash.engine.worker._pkg import W as _w
|
|
126
|
+
|
|
127
|
+
train = getattr(getattr(_w, "JOB_SPEC", None), "train", None)
|
|
128
|
+
rank = getattr(train, "lora_rank", None)
|
|
129
|
+
except Exception:
|
|
130
|
+
rank = None
|
|
131
|
+
try:
|
|
132
|
+
return max(1, int(rank if rank is not None else default))
|
|
133
|
+
except (TypeError, ValueError):
|
|
134
|
+
return default
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _rollout_uplift_validated(params_b: float | None, lora_rank: int) -> bool:
|
|
138
|
+
return params_b is not None and float(params_b) <= 4.7 and int(lora_rank) <= 32
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def opd_vllm_kwargs(
|
|
142
|
+
model_id: str,
|
|
143
|
+
knobs: Any,
|
|
144
|
+
seq_cap: int,
|
|
145
|
+
*,
|
|
146
|
+
prompts_per_step: int | None = None,
|
|
147
|
+
lora_rank: int | None = None,
|
|
148
|
+
) -> dict[str, Any]:
|
|
122
149
|
"""Direct vLLM LLM(...) kwargs mirroring the GRPO colocate rollout tuning."""
|
|
123
150
|
kwargs: dict[str, Any] = {
|
|
124
151
|
"gpu_memory_utilization": 0.10,
|
|
@@ -155,18 +182,32 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
|
|
|
155
182
|
|
|
156
183
|
if card_gb > 0:
|
|
157
184
|
try:
|
|
158
|
-
from flash.catalog import MODELS
|
|
185
|
+
from flash.catalog import MODELS, vocab_size_for
|
|
159
186
|
from flash.engine.vram import (
|
|
160
187
|
colocate_kv_util,
|
|
188
|
+
opd_allocator_margin_gb,
|
|
189
|
+
opd_post_init_reserve_gb,
|
|
161
190
|
opd_rollout_concurrency,
|
|
191
|
+
opd_training_reserve_gb,
|
|
162
192
|
resolve_params_b,
|
|
163
193
|
)
|
|
164
194
|
|
|
165
195
|
info = MODELS.get(model_id)
|
|
166
196
|
params_b = resolve_params_b(model_id)
|
|
197
|
+
sizing_params_b = float(params_b or 1.0)
|
|
198
|
+
resolved_lora_rank = (
|
|
199
|
+
_sizing_lora_rank(knobs)
|
|
200
|
+
if lora_rank is None
|
|
201
|
+
else max(1, int(lora_rank))
|
|
202
|
+
)
|
|
203
|
+
resolved_prompts_per_step = (
|
|
204
|
+
getattr(knobs, "prompts_per_step", 1)
|
|
205
|
+
if prompts_per_step is None
|
|
206
|
+
else prompts_per_step
|
|
207
|
+
)
|
|
167
208
|
active_b = float(getattr(info, "active_params_b", 0.0) or 0.0) if info else 0.0
|
|
168
209
|
target_concurrency = opd_rollout_concurrency(
|
|
169
|
-
|
|
210
|
+
resolved_prompts_per_step,
|
|
170
211
|
getattr(knobs, "group_size", 1),
|
|
171
212
|
)
|
|
172
213
|
rollout_concurrency = target_concurrency
|
|
@@ -184,12 +225,26 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
|
|
|
184
225
|
|
|
185
226
|
util = _util_for(rollout_concurrency)
|
|
186
227
|
if free_gb > 0:
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
228
|
+
# free memory is measured after the student and adapter are resident. protect the
|
|
229
|
+
# modeled loss forward/backward peak plus allocator slack, then give the remaining
|
|
230
|
+
# memory to the rollout executor instead of leaving it idle behind the generic cap.
|
|
231
|
+
training_reserve_gb = opd_training_reserve_gb(
|
|
232
|
+
sizing_params_b,
|
|
233
|
+
int(seq_cap),
|
|
234
|
+
max_tokens=getattr(knobs, "max_completion", None),
|
|
235
|
+
prompts_per_step=resolved_prompts_per_step,
|
|
236
|
+
group_size=getattr(knobs, "group_size", 1),
|
|
237
|
+
vocab=vocab_size_for(model_id),
|
|
238
|
+
active_params_b=active_b,
|
|
239
|
+
)
|
|
240
|
+
post_init_reserve_gb = opd_post_init_reserve_gb(
|
|
241
|
+
sizing_params_b, resolved_lora_rank
|
|
242
|
+
)
|
|
243
|
+
allocator_margin_gb = opd_allocator_margin_gb(card_gb)
|
|
244
|
+
protected_gb = training_reserve_gb + post_init_reserve_gb + allocator_margin_gb
|
|
245
|
+
rollout_budget_gb = max(0.0, free_gb - protected_gb)
|
|
246
|
+
max_startup_util = rollout_budget_gb / max(1.0, card_gb)
|
|
247
|
+
lean_trimmed = False
|
|
193
248
|
while rollout_concurrency > 1 and util > max_startup_util:
|
|
194
249
|
rollout_concurrency -= 1
|
|
195
250
|
util = _util_for(rollout_concurrency)
|
|
@@ -198,16 +253,61 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
|
|
|
198
253
|
"[opd] reduced vLLM rollout batch "
|
|
199
254
|
f"{target_concurrency}->{rollout_concurrency} to fit startup memory "
|
|
200
255
|
f"(free={free_gb:.1f} GiB, request={util * card_gb:.1f} GiB, "
|
|
201
|
-
f"
|
|
256
|
+
f"protected={protected_gb:.1f} GiB)"
|
|
202
257
|
)
|
|
203
258
|
if util > max_startup_util:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
)
|
|
259
|
+
# concurrency bottomed out but the resident-KV floor (vLLM weight copy + _KV_CAP)
|
|
260
|
+
# keeps the minimal executor above the protected budget. before hard-OOMing, trim
|
|
261
|
+
# the allocator slack to the old ~1 GiB startup margin so configs that fit before
|
|
262
|
+
# the protected-budget change are not newly rejected; the modeled training peak and
|
|
263
|
+
# post-init reserve stay intact.
|
|
264
|
+
lean_protected_gb = training_reserve_gb + post_init_reserve_gb + 1.0
|
|
265
|
+
lean_startup_util = max(0.0, free_gb - lean_protected_gb) / max(1.0, card_gb)
|
|
266
|
+
if util <= lean_startup_util:
|
|
267
|
+
print(
|
|
268
|
+
"[opd] trimmed allocator margin "
|
|
269
|
+
f"{allocator_margin_gb:.1f}->1.0 GiB to fit rollout executor at "
|
|
270
|
+
f"concurrency {rollout_concurrency} "
|
|
271
|
+
f"(free={free_gb:.1f} GiB, request={util * card_gb:.1f} GiB)"
|
|
272
|
+
)
|
|
273
|
+
protected_gb = lean_protected_gb
|
|
274
|
+
max_startup_util = lean_startup_util
|
|
275
|
+
lean_trimmed = True
|
|
276
|
+
# the concurrency loop above bottomed out against the stricter full
|
|
277
|
+
# budget; re-seek how many sequences now fit under the relaxed lean
|
|
278
|
+
# ceiling so the lean path is not frozen at the reduced floor.
|
|
279
|
+
while (
|
|
280
|
+
rollout_concurrency < target_concurrency
|
|
281
|
+
and _util_for(rollout_concurrency + 1) <= max_startup_util
|
|
282
|
+
):
|
|
283
|
+
rollout_concurrency += 1
|
|
284
|
+
util = _util_for(rollout_concurrency)
|
|
285
|
+
else:
|
|
286
|
+
startup_oom = _startup_oom_error(
|
|
287
|
+
free_gb=free_gb,
|
|
288
|
+
total_gb=card_gb,
|
|
289
|
+
requested_util=util,
|
|
290
|
+
reserve_gb=protected_gb,
|
|
291
|
+
rollout_batch_size=rollout_concurrency,
|
|
292
|
+
)
|
|
293
|
+
if (
|
|
294
|
+
startup_oom is None
|
|
295
|
+
and not lean_trimmed
|
|
296
|
+
and _rollout_uplift_validated(params_b, resolved_lora_rank)
|
|
297
|
+
):
|
|
298
|
+
# 0.80 is a final ceiling for the validated qwen3.5-4b/rank-32 size class; larger
|
|
299
|
+
# or higher-rank models keep the existing colocate ceiling until gpu validation.
|
|
300
|
+
# skip uplift when we had to lean-trim: the lean budget only leaves ~1 GiB of
|
|
301
|
+
# headroom for the training peak, so spending it on the rollout executor risks OOM.
|
|
302
|
+
util = max(util, min(0.80, max_startup_util))
|
|
303
|
+
print(
|
|
304
|
+
"[opd] vLLM memory budget "
|
|
305
|
+
f"free={free_gb:.1f}/{card_gb:.1f} GiB "
|
|
306
|
+
f"training_reserve={training_reserve_gb:.1f} GiB "
|
|
307
|
+
f"post_init_reserve={post_init_reserve_gb:.1f} GiB "
|
|
308
|
+
f"allocator_margin={allocator_margin_gb:.1f} GiB "
|
|
309
|
+
f"rollout={util * card_gb:.1f} GiB util={util:.3f}"
|
|
310
|
+
)
|
|
211
311
|
kwargs["gpu_memory_utilization"] = util
|
|
212
312
|
kwargs["max_num_seqs"] = rollout_concurrency
|
|
213
313
|
kwargs["rollout_batch_size"] = rollout_concurrency
|
|
@@ -13,6 +13,11 @@ from typing import TYPE_CHECKING, Any
|
|
|
13
13
|
|
|
14
14
|
from flash.catalog import serving_context_cap, serving_lora_rank_cap
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
class ServingPreflightError(ValueError):
|
|
18
|
+
"""Serving-path preflight rejection. Homed here (dependency-light) so unstructured
|
|
19
|
+
preparation can raise/catch it without importing the heavy flash.serve.preflight module."""
|
|
20
|
+
|
|
16
21
|
if TYPE_CHECKING:
|
|
17
22
|
from flash.spec import JobSpec
|
|
18
23
|
|