freesolo-flash-dev 1.0.7__tar.gz → 1.0.9__tar.gz

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