freesolo-flash-dev 1.0.6__tar.gz → 1.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. freesolo_flash_dev-1.0.8/AB_PLAN.md +45 -0
  2. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/PKG-INFO +1 -1
  3. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/training_doc.py +10 -7
  4. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cost/analytical.py +19 -8
  5. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cost/facts.py +25 -0
  6. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cost/spec.py +8 -9
  7. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cost/types.py +15 -2
  8. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/diagnostics.py +11 -0
  9. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/vram.py +43 -50
  10. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/gpu_setup.py +74 -3
  11. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/heartbeat.py +93 -27
  12. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd.py +150 -12
  13. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/rl.py +110 -62
  14. freesolo_flash_dev-1.0.8/flash/engine/worker/rollout_samples.py +148 -0
  15. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/sft.py +122 -61
  16. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/lora_rank.py +61 -15
  17. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_poll.py +55 -0
  18. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/jobs.py +8 -5
  19. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/train/endpoints.py +10 -3
  20. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/api.py +8 -4
  21. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/runner/__init__.py +38 -2
  22. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/runner/lifecycle.py +7 -0
  23. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/schema/__init__.py +4 -0
  24. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/schema/fields.py +10 -1
  25. freesolo_flash_dev-1.0.8/flash/serve/preflight.py +121 -0
  26. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/routes/runs.py +3 -0
  27. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/routes/serving.py +26 -10
  28. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/spec.py +34 -0
  29. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/pyproject.toml +2 -2
  30. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_allocator.py +123 -72
  31. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_b200_rollout_opt.py +7 -0
  32. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_chalk_kernels.py +1 -6
  33. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_estimate.py +17 -14
  34. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_estimate.py +61 -6
  35. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_hardware.py +19 -0
  36. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_flash_worker.py +9 -10
  37. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_gpus.py +2 -3
  38. freesolo_flash_dev-1.0.8/tests/test_grpo_cudagraphs.py +101 -0
  39. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_grpo_params.py +76 -0
  40. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_jobs.py +90 -0
  41. freesolo_flash_dev-1.0.8/tests/test_multi_gpu_gate.py +83 -0
  42. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_opd.py +299 -60
  43. freesolo_flash_dev-1.0.8/tests/test_rollout_samples.py +606 -0
  44. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_api.py +253 -0
  45. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_envs_coverage.py +63 -2
  46. freesolo_flash_dev-1.0.8/tests/test_sft_chunked_nll.py +292 -0
  47. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_spec_and_validation.py +51 -0
  48. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_train_context_preflight.py +24 -1
  49. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vast_api.py +12 -3
  50. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_init_heartbeat.py +34 -0
  51. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/uv.lock +1 -1
  52. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.claude/skills/verify/SKILL.md +0 -0
  53. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.dockerignore +0 -0
  54. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.env.example +0 -0
  55. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/auto-rebake.yml +0 -0
  56. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/bake-kernel-cache.yml +0 -0
  57. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/ci.yml +0 -0
  58. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/main-source-guard.yml +0 -0
  59. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/notify-tests-repo.yml +0 -0
  60. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/publish-dev.yml +0 -0
  61. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/publish-image.yml +0 -0
  62. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/publish.yml +0 -0
  63. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/version-parity.yml +0 -0
  64. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.github/workflows/worker-image.yml +0 -0
  65. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/.gitignore +0 -0
  66. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/Dockerfile +0 -0
  67. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/Dockerfile.worker +0 -0
  68. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/LICENSE +0 -0
  69. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/README.md +0 -0
  70. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/build/kernel_cache/.gitignore +0 -0
  71. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/build/kernel_cache/.keep +0 -0
  72. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/Dockerfile.kernelcache +0 -0
  73. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/Dockerfile.kernelcache.relayer +0 -0
  74. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/bake_kernel_cache.py +0 -0
  75. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/bake_pod_entry.py +0 -0
  76. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/kernel_fingerprint.py +0 -0
  77. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/docker/make_rp_handler.py +0 -0
  78. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/__init__.py +0 -0
  79. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/_channel.py +0 -0
  80. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/_fileio.py +0 -0
  81. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/_logging.py +0 -0
  82. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/_update_check.py +0 -0
  83. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/adapter_artifacts.py +0 -0
  84. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/catalog.py +0 -0
  85. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/__init__.py +0 -0
  86. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/__main__.py +0 -0
  87. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/_tty.py +0 -0
  88. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/commands.py +0 -0
  89. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/env_setup.py +0 -0
  90. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/envpush.py +0 -0
  91. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cli/render.py +0 -0
  92. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/client/__init__.py +0 -0
  93. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/client/config.py +0 -0
  94. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/client/http.py +0 -0
  95. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/client/runtime_secrets.py +0 -0
  96. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/client/specs.py +0 -0
  97. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/cost/__init__.py +0 -0
  98. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/__init__.py +0 -0
  99. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/accounting.py +0 -0
  100. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/chalk_kernels.py +0 -0
  101. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/multiturn_reward_scoring.py +0 -0
  102. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/multiturn_rollout.py +0 -0
  103. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/recipe.py +0 -0
  104. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/steps.py +0 -0
  105. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/structured_outputs.py +0 -0
  106. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/__init__.py +0 -0
  107. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/__main__.py +0 -0
  108. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/_pkg.py +0 -0
  109. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/adapter.py +0 -0
  110. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/decoding.py +0 -0
  111. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/finalize.py +0 -0
  112. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo.py +0 -0
  113. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo_multimodal.py +0 -0
  114. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
  115. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/hf.py +0 -0
  116. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/kernel_warmup.py +0 -0
  117. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/lora.py +0 -0
  118. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd_gkd.py +0 -0
  119. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/opd_vllm.py +0 -0
  120. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/packing.py +0 -0
  121. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/__init__.py +0 -0
  122. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/attn.py +0 -0
  123. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/diagnostics.py +0 -0
  124. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/lifecycle.py +0 -0
  125. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/liger.py +0 -0
  126. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/loraplus.py +0 -0
  127. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/perf/memory.py +0 -0
  128. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/rng.py +0 -0
  129. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/teacher.py +0 -0
  130. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/tokenizer_align.py +0 -0
  131. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker/wandb_log.py +0 -0
  132. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/engine/worker_entrypoint.py +0 -0
  133. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/__init__.py +0 -0
  134. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/adapter.py +0 -0
  135. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/archive.py +0 -0
  136. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/archive_policy.py +0 -0
  137. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/base.py +0 -0
  138. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/loader.py +0 -0
  139. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/pull.py +0 -0
  140. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/envs/registry.py +0 -0
  141. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/multimodal.py +0 -0
  142. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/opd_retry_contract.py +0 -0
  143. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/__init__.py +0 -0
  144. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_auth.py +0 -0
  145. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_deadline.py +0 -0
  146. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_hf_artifacts.py +0 -0
  147. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_hf_retry.py +0 -0
  148. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_http.py +0 -0
  149. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_instance.py +0 -0
  150. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_bootstrap.py +0 -0
  151. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_poll.py +0 -0
  152. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_instance_provider.py +0 -0
  153. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/_worker.py +0 -0
  154. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/allocator.py +0 -0
  155. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/base.py +0 -0
  156. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/__init__.py +0 -0
  157. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/api.py +0 -0
  158. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/auth.py +0 -0
  159. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/gpus.py +0 -0
  160. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  161. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  162. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/preflight.py +0 -0
  163. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/lambdalabs/pricing.py +0 -0
  164. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/preflight.py +0 -0
  165. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/realized.py +0 -0
  166. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/__init__.py +0 -0
  167. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/api.py +0 -0
  168. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/auth.py +0 -0
  169. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/cost.py +0 -0
  170. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/gpus.py +0 -0
  171. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/keys.py +0 -0
  172. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/preflight.py +0 -0
  173. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/preload.py +0 -0
  174. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/pricing.py +0 -0
  175. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/slots.py +0 -0
  176. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/runpod/train/__init__.py +0 -0
  177. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/__init__.py +0 -0
  178. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/auth.py +0 -0
  179. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/jobs/__init__.py +0 -0
  180. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/jobs/builders.py +0 -0
  181. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/preflight.py +0 -0
  182. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/providers/vast/pricing.py +0 -0
  183. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/py.typed +0 -0
  184. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/runner/checkpoints.py +0 -0
  185. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/runner/deploy.py +0 -0
  186. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/runner/verified_revisions.py +0 -0
  187. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/serve/__init__.py +0 -0
  188. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/serve/deploy.py +0 -0
  189. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/serve/export.py +0 -0
  190. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/serve/pricing.py +0 -0
  191. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/serve/urls.py +0 -0
  192. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/__init__.py +0 -0
  193. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/__main__.py +0 -0
  194. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/_deps.py +0 -0
  195. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/_internal_client.py +0 -0
  196. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/_locks.py +0 -0
  197. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/_runtime.py +0 -0
  198. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/app.py +0 -0
  199. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/auth.py +0 -0
  200. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/billing.py +0 -0
  201. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/billing_retry.py +0 -0
  202. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/checkpoints.py +0 -0
  203. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/db.py +0 -0
  204. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/environment_registry.py +0 -0
  205. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/envs.py +0 -0
  206. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/reconcile.py +0 -0
  207. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/repo_cleanup.py +0 -0
  208. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/routes/__init__.py +0 -0
  209. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/routes/envs.py +0 -0
  210. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/routes/meta.py +0 -0
  211. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/flash/server/run_registry.py +0 -0
  212. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/infisical-entrypoint.sh +0 -0
  213. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/scripts/build_dev_dist.py +0 -0
  214. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/__init__.py +0 -0
  215. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/_helpers/__init__.py +0 -0
  216. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/_helpers/runner.py +0 -0
  217. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/_helpers/specs.py +0 -0
  218. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/_helpers/vast.py +0 -0
  219. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/conftest.py +0 -0
  220. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/fixtures/math_eval.jsonl +0 -0
  221. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/fixtures/math_train.jsonl +0 -0
  222. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/live/__init__.py +0 -0
  223. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/live/conftest.py +0 -0
  224. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/live/test_lambda_live.py +0 -0
  225. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/live/test_runpod_live.py +0 -0
  226. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/live/test_vast_live.py +0 -0
  227. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_accounting_coverage.py +0 -0
  228. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_agent_flash_cli_contract.py +0 -0
  229. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_algorithms.py +0 -0
  230. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_async_uploads.py +0 -0
  231. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_backend_jobspec_contract.py +0 -0
  232. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_base_model_provenance.py +0 -0
  233. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_billing_retry.py +0 -0
  234. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cancel_remote.py +0 -0
  235. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_catalog_consistency.py +0 -0
  236. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_charge_pricing.py +0 -0
  237. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_checkpoints.py +0 -0
  238. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_commands.py +0 -0
  239. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_commands_coverage.py +0 -0
  240. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_errors.py +0 -0
  241. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_help.py +0 -0
  242. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_main_coverage.py +0 -0
  243. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_managed.py +0 -0
  244. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_render_coverage.py +0 -0
  245. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cli_render_theme.py +0 -0
  246. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_client.py +0 -0
  247. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_client_server_integration.py +0 -0
  248. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_client_stream_reads.py +0 -0
  249. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_config_overrides.py +0 -0
  250. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_controlled_experiment_repairs.py +0 -0
  251. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_analytical.py +0 -0
  252. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_equation.py +0 -0
  253. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_models.py +0 -0
  254. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_cost_rewards.py +0 -0
  255. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_deploy_latency.py +0 -0
  256. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_dev_channel.py +0 -0
  257. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_disk_gb.py +0 -0
  258. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_endpoint_name.py +0 -0
  259. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_cache_evict.py +0 -0
  260. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_delete.py +0 -0
  261. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_download.py +0 -0
  262. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_download_api.py +0 -0
  263. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_no_install.py +0 -0
  264. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_publish.py +0 -0
  265. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_pull.py +0 -0
  266. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_coverage.py +0 -0
  267. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_loader_coverage.py +0 -0
  268. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_pull_managed_control_plane.py +0 -0
  269. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_push.py +0 -0
  270. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_env_rate_limit_resolve.py +0 -0
  271. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_envs_coverage.py +0 -0
  272. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_export.py +0 -0
  273. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_flash_mvp.py +0 -0
  274. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_flashinfer_cache_dirs.py +0 -0
  275. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_github_urlopen_retry.py +0 -0
  276. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_grpo_mask_aware.py +0 -0
  277. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_grpo_perturn.py +0 -0
  278. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_grpo_sleep_gate.py +0 -0
  279. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_health_liveness.py +0 -0
  280. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_hf_retry.py +0 -0
  281. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_idle_endpoint_reaper.py +0 -0
  282. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_instance_bootstrap_coverage.py +0 -0
  283. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_internal_client.py +0 -0
  284. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_kernel_cache.py +0 -0
  285. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_kernel_fingerprint.py +0 -0
  286. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_kv_util.py +0 -0
  287. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_lambda_api_coverage.py +0 -0
  288. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_lambda_runner.py +0 -0
  289. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_logging.py +0 -0
  290. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_login_perms.py +0 -0
  291. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_lora_rank_coverage.py +0 -0
  292. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_lora_rank_preflight.py +0 -0
  293. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_managed_hf_repo.py +0 -0
  294. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_metrics_schema_agent_contract.py +0 -0
  295. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_mig_guard.py +0 -0
  296. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multimodal_input_grads.py +0 -0
  297. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multimodal_training.py +0 -0
  298. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_image_rollout.py +0 -0
  299. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_per_turn_reward.py +0 -0
  300. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout.py +0 -0
  301. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_coverage.py +0 -0
  302. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_records.py +0 -0
  303. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_multiturn_rollout_request_policy.py +0 -0
  304. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_named_reward_metrics.py +0 -0
  305. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_oom_escalate_gpu.py +0 -0
  306. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_opd_full_state_resume.py +0 -0
  307. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_opd_resume_safety.py +0 -0
  308. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_opd_vllm.py +0 -0
  309. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_open_model_policy.py +0 -0
  310. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_orchestrator_flash.py +0 -0
  311. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_packing.py +0 -0
  312. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_packing_coverage.py +0 -0
  313. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_poll_helpers.py +0 -0
  314. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_preflight.py +0 -0
  315. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_provider_preflight_coverage.py +0 -0
  316. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_provider_routing.py +0 -0
  317. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_provider_teardown_robustness.py +0 -0
  318. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_providers_symmetry.py +0 -0
  319. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_realized_cost.py +0 -0
  320. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_repo_cleanup.py +0 -0
  321. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_resolve_params_b.py +0 -0
  322. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_resume_on_retry.py +0 -0
  323. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_runmgmt.py +0 -0
  324. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_runpod_api_delete.py +0 -0
  325. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_runpod_key_fingerprint.py +0 -0
  326. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_runpod_key_waterfall.py +0 -0
  327. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_runpod_slots.py +0 -0
  328. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_serve.py +0 -0
  329. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_app_coverage.py +0 -0
  330. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_auth_singleflight.py +0 -0
  331. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_billing.py +0 -0
  332. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_checkpoints_coverage.py +0 -0
  333. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_db.py +0 -0
  334. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_server_locks_coverage.py +0 -0
  335. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_serving_context_preflight.py +0 -0
  336. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_serving_contract.py +0 -0
  337. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_serving_pricing_coverage.py +0 -0
  338. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_serving_schema_coverage.py +0 -0
  339. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_sft_activation_shaping.py +0 -0
  340. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_sft_example_selection.py +0 -0
  341. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_sft_gc_off.py +0 -0
  342. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_sft_max_context.py +0 -0
  343. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_sft_tokenize_cache.py +0 -0
  344. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_single_turn_multimodal_grpo.py +0 -0
  345. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_structured_outputs.py +0 -0
  346. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_thinking_config.py +0 -0
  347. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_training_controls.py +0 -0
  348. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_update_check.py +0 -0
  349. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vast_api_coverage.py +0 -0
  350. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vast_offers.py +0 -0
  351. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vast_provider_coverage.py +0 -0
  352. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vast_runner.py +0 -0
  353. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_verified_revisions.py +0 -0
  354. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_verifiers.py +0 -0
  355. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_version.py +0 -0
  356. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  357. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_wandb_log_coverage.py +0 -0
  358. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_wandb_naming.py +0 -0
  359. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_warmstart_adapter_download.py +0 -0
  360. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_warmstart_cross_repo.py +0 -0
  361. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_weight_cache.py +0 -0
  362. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_dryrun.py +0 -0
  363. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_hardexit.py +0 -0
  364. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_image.py +0 -0
  365. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_stack.py +0 -0
  366. {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.8}/tests/test_worker_thinking.py +0 -0
@@ -0,0 +1,45 @@
1
+ # GRPO decode-only CUDA graph A/B plan
2
+
3
+ ## Objective
4
+
5
+ Measure whether decode-only CUDA graphs speed up the colocated GRPO rollout engine without changing training behavior. This plan does not authorize a paid run.
6
+
7
+ ## Frozen comparison
8
+
9
+ - Control: `origin/dev` at `8fdfc8580b9058d8c40289d4fa1a2e6e3b7bced2`. On RTX 5090/sm120 with vLLM 0.19.1, this selects `enforce_eager=True`.
10
+ - Treatment: the committed head of `perf/grpo-cudagraphs`. On the same GPU and vLLM version, this selects `enforce_eager=False` with `mode=0` and `cudagraph_mode=FULL_DECODE_ONLY`.
11
+ - Hardware: two separate, matched RunPod RTX 5090 workers from the same GPU offer, image, region, CPU/RAM class, and storage configuration. Do not substitute unmatched cards between arms.
12
+ - Model: `Qwen/Qwen3.5-0.8B`, which fits within an RTX 4090-class memory budget.
13
+ - Environment: the real `entropy-R1` hub environment, pinned to one immutable `freesolo-co/environment-hub` commit before launch.
14
+ - Seed: 42 for both arms.
15
+ - Budget: 30 optimizer updates after a two-update exact-path smoke. Use the same prompts per step, group size, maximum context, maximum completion length, LoRA settings, optimizer settings, reward implementation, dataset order, and checkpoint schedule.
16
+ - Concurrency: launch both paid arms together only after both two-update smokes pass. Do not reuse a worker across arms.
17
+
18
+ ## Preflight and token-parity gate
19
+
20
+ 1. Resolve and record the exact Flash commit, environment-hub commit, model revision, container digest, vLLM version, CUDA/driver versions, and RunPod machine identifiers for each arm.
21
+ 2. Run the same fixed prompt batch through both immutable rollout engines with greedy decoding and the same maximum token count.
22
+ 3. Require identical token IDs, finish reasons, and decoded text for every fixed prompt. Any difference is a correctness failure and stops the A/B.
23
+ 4. Run two GRPO updates per arm. Require successful graph capture or vLLM's eager fallback, finite loss/reward values, and no OOM before scaling to 30 updates.
24
+
25
+ ## Metrics
26
+
27
+ Measure rollout work separately from training work where telemetry permits.
28
+
29
+ - Primary performance: generated rollout tokens per rollout-engine second, excluding engine startup, graph capture, checkpointing, reward evaluation, and optimizer time.
30
+ - Secondary performance: median rollout wall time per update and end-to-end median step wall time. Report graph-capture startup time separately rather than amortizing it away.
31
+ - Correctness: fixed-prompt rollout token parity, reward curve, training loss curve, completion length, finish-reason distribution, and invalid/empty completion rate.
32
+ - Safety: peak allocated and reserved GPU memory, OOM count, graph-capture failures, eager-fallback events, worker restarts, and failed updates.
33
+
34
+ ## Decision thresholds
35
+
36
+ The expected direction is faster treatment decode with unchanged learning behavior.
37
+
38
+ - Performance works: treatment improves steady-state rollout tokens/second by at least 10% over control. A 10% to 40% improvement is the expected range. Confirm the direction with at least a 9% reduction in median rollout wall time over updates 3 through 30.
39
+ - End-to-end value: treatment reduces median total step wall time by at least 5%. Report a decode-only win below this threshold as technically real but operationally marginal.
40
+ - Reward parity: treatment reward AUC over the 30 matched updates must remain within 5% relative or 0.02 absolute of control, whichever tolerance is larger. There must be no sustained three-update reward regression larger than 0.05 absolute.
41
+ - Loss parity: treatment loss AUC must remain within 5% relative of control, with finite values at every update and no new divergence pattern.
42
+ - Rollout parity: the fixed greedy prompt batch must have exact token-ID parity. During stochastic training, completion length, finish-reason, and empty/invalid-rate differences must each remain within 5% relative or one sample, whichever is larger.
43
+ - Memory and reliability: no OOM, no worker restart, no failed update, and treatment peak reserved memory no more than 5% above control after graph capture.
44
+
45
+ The treatment passes only if all correctness and reliability gates pass and the primary performance threshold is met. If vLLM falls back to eager, report the fallback and classify the performance result as inconclusive rather than a decode-graph success.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 1.0.6
3
+ Version: 1.0.8
4
4
  Summary: Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI
5
5
  Project-URL: Homepage, https://github.com/freesolo-co/flash
6
6
  Project-URL: Repository, https://github.com/freesolo-co/flash
@@ -277,13 +277,16 @@ spending another GPU run:
277
277
  - **Judge the trend, not a single number.** The proof of training is the curve:
278
278
  loss falling (SFT) or `reward` rising over steps (GRPO). Record the base/early
279
279
  value and the final value. A flat or noisy trend with no improvement is not success.
280
- - **Read the model's outputs, not just the metrics.** A rising reward 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
@@ -14,8 +14,8 @@ from flash.providers.allocator import required_vram_gb, vram_headroom
14
14
  from .facts import (
15
15
  active_params_b,
16
16
  download_weight_gb,
17
+ effective_train_tflops,
17
18
  gpu_hourly_usd,
18
- gpu_tflops,
19
19
  gpu_vram_gb,
20
20
  model_quant,
21
21
  pick_gpu,
@@ -150,7 +150,7 @@ def compile_seconds(config: RunConfig, gpu: str) -> float:
150
150
  def seconds_per_step(config: RunConfig, gpu: str) -> float:
151
151
  """Steady-state wall time for one optimizer step on ``gpu``."""
152
152
  n = config.normalized()
153
- peak = gpu_tflops(gpu) * 1e12 # FLOP/s
153
+ peak = effective_train_tflops(gpu) * 1e12 # FLOP/s (realized training throughput; see facts)
154
154
  # An MoE's per-step wall scales with TOTAL params (routing + all-expert coordination + grouped
155
155
  # GEMM under-utilization), not the tiny active-param FLOPs; dense models keep active (== total).
156
156
  moe = _is_moe(n.model_id)
@@ -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()
@@ -198,7 +201,7 @@ def sft_seconds_for_tokens(config: RunConfig, gpu: str, train_tokens: float) ->
198
201
  moe = _is_moe(n.model_id)
199
202
  params = (total_params_b(n.model_id) if moe else active_params_b(n.model_id)) * 1e9
200
203
  mfu = MFU_SFT_TRAIN_MOE if moe else MFU_SFT_TRAIN
201
- peak = gpu_tflops(gpu) * 1e12
204
+ peak = effective_train_tflops(gpu) * 1e12
202
205
  flops = SFT_FLOPS_PER_TOKEN_PER_PARAM * params * train_tokens
203
206
  return flops / (peak * mfu)
204
207
 
@@ -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
  )
@@ -30,6 +30,31 @@ def gpu_tflops(name: str) -> float:
30
30
  return GPU_COMPUTE_TFLOPS.get(name, _DEFAULT_TFLOPS)
31
31
 
32
32
 
33
+ # realized TRAINING throughput sits well below peak when a class's training kernels don't reach it.
34
+ # b200 (sm100) has no arch-tuned training kernels yet and falls back to the same portable paths as
35
+ # h200 (sm90), so its 2.25 pflops dense-bf16 peak does not materialize for training -- realized
36
+ # throughput is h200-class (and frequently lower for rl/grpo). cap b200 at h200 so the analytical
37
+ # cost model does not rank it as faster/cheaper than h200 on peak flops alone (it is not, and is
38
+ # often slower). this is a conservative floor: it never prices b200 above h200-equivalent training
39
+ # time, so it cannot over-charge, and it removes the "b200 looks cheapest" inversion at the source.
40
+ # refine per-workload once real b200 training samples exist. the vram/serving paths keep the true
41
+ # peak via gpu_tflops; only the training-time model uses this.
42
+ _TRAIN_TFLOPS_CAP: dict[str, float] = {
43
+ "B200": 990.0, # h200-class realized training throughput, not the 2250 dense-bf16 peak
44
+ }
45
+
46
+
47
+ def effective_train_tflops(name: str) -> float:
48
+ """Realized bf16 TRAINING throughput for the analytical cost model.
49
+
50
+ Equal to ``gpu_tflops`` (peak) except where a class's training kernels fall short of peak: b200
51
+ training falls back to portable kernels, so it is capped at h200-class throughput rather than
52
+ its raw 2.25 pflops peak."""
53
+ peak = gpu_tflops(name)
54
+ cap = _TRAIN_TFLOPS_CAP.get(name)
55
+ return min(peak, cap) if cap is not None else peak
56
+
57
+
33
58
  def gpu_hourly_usd(
34
59
  name: str,
35
60
  provider: str | None = None,
@@ -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,8 @@ _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
+ OPD_CE_CHUNK_SIZE = 64
31
+ _OPD_CE_PEAK_BYTES_PER_LOGIT = 16
30
32
 
31
33
 
32
34
  def _sft_per_device_bs() -> int:
@@ -112,7 +114,7 @@ def opd_rollout_concurrency(prompts_per_step: int = 1, group_size: int = 1) -> i
112
114
 
113
115
 
114
116
  def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size: int = 1) -> int:
115
- """Loss microbatch size used by OPD's dense-logit GKD backward.
117
+ """Loss microbatch size used by OPD's GKD backward.
116
118
 
117
119
  Keep this in lockstep with worker.opd._opd_loss_microbatch_size without importing the worker from
118
120
  the sizing path. Small/medium catalog models run up to four loss samples per forward; 35B-class
@@ -226,17 +228,27 @@ _VOCAB_DEFAULT = 248_320
226
228
  _LOGITS_BUDGET_GB = 6.0
227
229
  # 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
228
230
  _SFT_LOGITS_BYTES_PER_ELEM = 16.0
229
- # Single source of truth for the SFT fused-CE gate. Keep the historical constant names because
230
- # engine.worker.perf and tests import them.
231
+ _SFT_CHUNKED_NLL_TOKENS = 256
232
+ # shared thresholds for the independent liger and worker memory gates.
231
233
  _LIGER_MIN_PARAMS_B = 3.0
232
234
  _LIGER_LONG_CTX_TOKENS = 2048
233
235
 
236
+ # trl 1.6 chunked_nll is validated against these model families. other models keep plain nll
237
+ # until their output-head and backbone traversal are covered by the same parity tests.
238
+ _SFT_CHUNKED_NLL_MODELS = frozenset(
239
+ {
240
+ "Qwen/Qwen3.5-0.8B",
241
+ "Qwen/Qwen3.5-2B",
242
+ "Qwen/Qwen3.5-4B",
243
+ "Qwen/Qwen3.5-9B",
244
+ "Qwen/Qwen3.6-35B-A3B",
245
+ }
246
+ )
234
247
 
235
- def sft_logits_fused(params_b: float | None, seq_len: int) -> bool:
236
- """True when the worker fuses SFT cross-entropy (>=3B model OR >=2048-token context)."""
237
- if seq_len >= _LIGER_LONG_CTX_TOKENS:
238
- return True
239
- return (params_b or 0.0) >= _LIGER_MIN_PARAMS_B
248
+
249
+ def sft_chunked_nll_enabled(model_id: str) -> bool:
250
+ """whether the sft worker uses trl's dense-logit-free chunked nll path."""
251
+ return model_id in _SFT_CHUNKED_NLL_MODELS
240
252
 
241
253
 
242
254
  def sft_logits_per_device_cap(seq_len: int, vocab: int) -> int:
@@ -370,44 +382,27 @@ def estimate_vram_gb(
370
382
  rollout = weights + max(
371
383
  _KV_CAP, _resident_kv_gb(eff_b, seq_len, rollout_concurrency, fp8_kv=fp8_kv)
372
384
  )
373
- # opd's gkd loss forward materializes DENSE logits there is NO fused cross-entropy: the
374
- # student forward yields full-sequence bf16 logits [seq, vocab], then the completion rows are
375
- # gathered in fp32 [completion, vocab] for the logsumexp. The SFT fused path budgets ZERO
376
- # vocab logits for >=3B models, so a long-completion opd job (e.g. max_tokens=8192) would be
377
- # sized for an under-capacity card and OOM.
378
- #
379
- # Mirror opd_rollout_seq_len / run_opd's completion resolution: explicit max_tokens, else the
380
- # OPD recipe default (thinking uses the longer max_completion_len_thinking). A GRPO-style
381
- # min(seq_len, 1024) fallback would UNDER-budget a thinking opd job (1536-token completions).
385
+ # text opd projects only completion-prediction hidden states and computes exact full-vocabulary
386
+ # ce in checkpointed chunks. image samples still use the dense full-sequence logits path and are
387
+ # processed one at a time. allocation does not know the dataset modality, so reserve the larger
388
+ # of one text ce chunk and one dense image loss forward/backward peak.
382
389
  completion = opd_completion_len(max_tokens, thinking)
383
- # run_opd backprops a small loss microbatch. Budget that actual dense-logit microbatch, not
384
- # the full rollout/teacher batch and not a single sequence.
385
390
  loss_mb = opd_loss_microbatch(params_b, batch_size, group_size)
386
391
  activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
387
- # Dense-logit peak spans BOTH the forward and the loss BACKWARD (OPD has no fused CE):
388
- # - forward: bf16 full-sequence logits [seq, vocab] (seq * 2)
389
- # fp32 completion rows [completion, vocab] for logsumexp, saved for backward
390
- # (completion * 4)
391
- # - backward: fp32 gradient of those completion rows [completion, vocab]
392
- # (completion * 4)
393
- # bf16 gradient scattered back into the full logits [seq, vocab] to reach lm_head
394
- # (seq * 2)
395
- # All four coexist at the backward peak. The old formula counted only the two FORWARD buffers,
396
- # so a long-completion / large-vocab (248k) opd job under-budgeted the loss backward by
397
- # ~(completion*4 + seq*2)*vocab bytes and could route to a GPU that OOMs in OPD loss backward
398
- # (codex[bot]). Mirror the SFT dense-logit sizing by budgeting the backward buffers too.
399
- logits_fwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
400
- logits_bwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
401
- logits = (logits_fwd + logits_bwd) / 1e9
392
+ ce_rows = min(OPD_CE_CHUNK_SIZE, loss_mb * completion)
393
+ chunked_logits = ce_rows * vocab * _OPD_CE_PEAK_BYTES_PER_LOGIT / 1e9
394
+ dense_image_logits = (seq_len * 4 + completion * 8) * vocab / 1e9
395
+ logits = max(chunked_logits, dense_image_logits)
402
396
  return base + rollout + activations + logits
403
- # Actual TRL SFT keeps fused CE disabled (see worker/sft.py), so dense logits materialize even
404
- # for long-context / >=3B models. Callers can pass sft_fused_ce=True only for theoretical
405
- # comparisons; the default must mirror the worker.
397
+ # direct callers default to the conservative plain-nll estimate. model_required_vram_gb passes
398
+ # the worker's validated chunked-nll decision explicitly when it knows the model identity.
406
399
  fused = False if sft_fused_ce is None else bool(sft_fused_ce)
407
400
  pd = sft_per_device(batch_size, seq_len=seq_len, vocab=vocab, fused=fused)
408
401
  activations = _ACT_COEF * pd * (seq_len / 1024.0) * width
409
- # Don't clamp to budget: pd=1 is irreducible and the logits can exceed the budget at near-2048 ctx.
410
- logits = 0.0 if fused else pd * seq_len * vocab * _SFT_LOGITS_BYTES_PER_ELEM / 1e9
402
+ # plain nll retains every sequence position. chunked nll retains at most one 256-token vocab
403
+ # projection at a time, independent of micro-batch and context once the chunk is full.
404
+ projected_tokens = min(pd * seq_len, _SFT_CHUNKED_NLL_TOKENS) if fused else pd * seq_len
405
+ logits = projected_tokens * vocab * _SFT_LOGITS_BYTES_PER_ELEM / 1e9
411
406
  return base + activations + logits
412
407
 
413
408
 
@@ -481,10 +476,10 @@ def sft_gc_off_peak_gb(
481
476
  lora_rank: int = 32,
482
477
  quant: str = "bf16",
483
478
  ) -> float:
484
- """Estimated peak VRAM (GB) for a FUSED-CE LoRA SFT step with gradient checkpointing OFF: the
485
- resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
486
- Fused CE (chalk FLCE) is assumed, so there is no ``[B, T, vocab]`` logits term (the thing that
487
- made GC-off impossible at a 248k vocab). Unknown architecture dims -> ``inf`` (caller keeps GC on).
479
+ """Estimated peak VRAM (GB) for a dense-logit-free LoRA SFT step with gradient checkpointing OFF:
480
+ the resident weights + optimizer/base + the no-recompute activations held across ALL ``num_layers``.
481
+ Chunked or fused CE is assumed, so there is no ``[B, T, vocab]`` logits term (the thing that made
482
+ GC-off impossible at a 248k vocab). Unknown architecture dims -> ``inf`` (caller keeps GC on).
488
483
 
489
484
  MoE: the activation backbone scales with the model's real ``hidden`` x ``num_layers`` (geometry),
490
485
  NOT params_b -- the ~3B-active expert FFN is already folded into ``_GC_OFF_ACT_K``. ``weights``
@@ -513,7 +508,7 @@ def sft_grad_checkpoint_can_disable(
513
508
  quant: str = "bf16",
514
509
  margin_gb: float = 18.0,
515
510
  ) -> bool:
516
- """True when a FUSED-CE LoRA SFT step fits a ``card_vram_gb`` card WITHOUT gradient checkpointing,
511
+ """True when a dense-logit-free LoRA SFT step fits a ``card_vram_gb`` card WITHOUT gradient checkpointing,
517
512
  so GC -- a ~+33% recompute tax on every step -- can be turned off for the speed win.
518
513
 
519
514
  Conservative by construction: an unknown card / unknown architecture dims, or a peak that doesn't
@@ -732,12 +727,10 @@ def model_required_vram_gb(
732
727
  vllm_concurrency = (
733
728
  opd_rollout_concurrency(batch_size, group_size) if is_opd else group_size
734
729
  )
735
- # Worker parity: TRL SFT deliberately calls install_chalk_kernels(..., fused_ce=False), because
736
- # SFTTrainer.compute_loss reads outputs.logits and crashes when fused CE returns logits=None. So
737
- # every SFT run materializes dense logits, even for long context / >=3B models where the old
738
- # allocator assumed a fused CE path. Size SFT with fused CE OFF up front or long-context Qwen SFT
739
- # routes to consumer cards and OOMs on the first backward.
740
- sft_fused_ce = None if is_grpo else False
730
+ # trl chunked_nll removes ignored positions before the lm head and projects valid tokens in
731
+ # bounded chunks. size validated qwen sft jobs without a dense [batch, seq, vocab] term; models
732
+ # outside that validated set keep the conservative plain-nll estimate and micro-batch cap.
733
+ sft_fused_ce = None if is_grpo else sft_chunked_nll_enabled(model_id)
741
734
  if info is not None:
742
735
  params_b = info.params_b
743
736
  if model_revision:
@@ -109,11 +109,56 @@ def finalize_alloc_conf_for_sleep() -> None:
109
109
  print("[alloc] auto-conf skipped:", e)
110
110
 
111
111
 
112
+ def _is_cudagraph_capture_failure(exc: BaseException) -> bool:
113
+ markers = (
114
+ "capture_model",
115
+ "cuda graph",
116
+ "cudagraph",
117
+ "graph capture",
118
+ )
119
+ current: BaseException | None = exc
120
+ seen: set[int] = set()
121
+ while current is not None and id(current) not in seen:
122
+ seen.add(id(current))
123
+ if any(marker in f"{type(current).__name__}: {current}".lower() for marker in markers):
124
+ return True
125
+ traceback = current.__traceback__
126
+ while traceback is not None:
127
+ frame = traceback.tb_frame
128
+ location = f"{frame.f_code.co_filename}:{frame.f_code.co_name}".lower()
129
+ if any(marker in location for marker in markers):
130
+ return True
131
+ traceback = traceback.tb_next
132
+ current = current.__cause__ or current.__context__
133
+ return False
134
+
135
+
136
+ def _release_failed_cudagraph_capture(exc: BaseException) -> None:
137
+ current: BaseException | None = exc
138
+ seen: set[int] = set()
139
+ while current is not None and id(current) not in seen:
140
+ seen.add(id(current))
141
+ next_exc = current.__cause__ or current.__context__
142
+ current.__traceback__ = None
143
+ current = next_exc
144
+ try:
145
+ import gc
146
+
147
+ gc.collect()
148
+ import torch
149
+
150
+ if torch.cuda.is_available():
151
+ torch.cuda.empty_cache()
152
+ except Exception as cleanup_exc:
153
+ print(f"[rl][warn] cuda graph fallback cleanup failed: {cleanup_exc}")
154
+
155
+
112
156
  def patch_trl_colocate_llm_kwargs(
113
157
  *,
114
158
  kv_cache_dtype: str | None = None,
115
159
  max_num_batched_tokens: int | None = None,
116
160
  enforce_eager: bool | None = None,
161
+ compilation_config: dict | None = None,
117
162
  attention_backend: str | None = None,
118
163
  mm_encoder_attn_backend: str | None = None,
119
164
  reasoning_parser: str | None = None,
@@ -144,6 +189,8 @@ def patch_trl_colocate_llm_kwargs(
144
189
  * ``enforce_eager=True``: run the rollout engine in pure eager mode (no torch.compile, no CUDA-graph
145
190
  capture) — the SUPPORTED replacement for the removed ``VLLM_TORCH_COMPILE_LEVEL=0`` env. Dodges
146
191
  the vLLM 0.19.1 aot_compile (Ampere sm86) + Triton slot-mapping (graph-capture) crashes off B200.
192
+ * ``compilation_config``: select a narrower vLLM compilation profile, such as decode-only CUDA
193
+ graphs with torch.compile/AOT disabled.
147
194
  * ``attention_backend="FLASHINFER"|"TRITON_ATTN"``: pin a PTX-independent decoder attention backend
148
195
  (consumer-Blackwell sm120) — the SUPPORTED replacement for the removed ``VLLM_ATTENTION_BACKEND``
149
196
  env. ``EngineArgs`` coerces the bare member name through ``AttentionConfig.validate_backend_before``.
@@ -171,6 +218,8 @@ def patch_trl_colocate_llm_kwargs(
171
218
  new_overrides["max_num_batched_tokens"] = int(max_num_batched_tokens)
172
219
  if enforce_eager is not None:
173
220
  new_overrides["enforce_eager"] = bool(enforce_eager)
221
+ if compilation_config is not None:
222
+ new_overrides["compilation_config"] = dict(compilation_config)
174
223
  if attention_backend is not None:
175
224
  new_overrides["attention_backend"] = attention_backend
176
225
  if mm_encoder_attn_backend is not None:
@@ -194,7 +243,9 @@ def patch_trl_colocate_llm_kwargs(
194
243
  _vg._flash_llm_overrides = overrides
195
244
  overrides.update(new_overrides)
196
245
  if getattr(_vg, "_flash_llm_kwargs_patched", False):
197
- print(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