freesolo-flash-dev 1.0.63__tar.gz → 1.0.65__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 (393) hide show
  1. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/rl_train.py +42 -11
  3. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/sft_train.py +41 -1
  4. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/pyproject.toml +2 -2
  5. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_rl_train.py +101 -3
  6. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_sft_train.py +200 -3
  7. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/uv.lock +1 -1
  8. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.dockerignore +0 -0
  9. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.env.example +0 -0
  10. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  13. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  14. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/dependabot.yml +0 -0
  15. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/auto-rebake.yml +0 -0
  16. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/bake-kernel-cache.yml +0 -0
  17. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/ci.yml +0 -0
  18. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/main-source-guard.yml +0 -0
  19. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/notify-tests-repo.yml +0 -0
  20. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/publish-dev.yml +0 -0
  21. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/publish-image.yml +0 -0
  22. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/publish.yml +0 -0
  23. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/version-parity.yml +0 -0
  24. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.github/workflows/worker-image.yml +0 -0
  25. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/.gitignore +0 -0
  26. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/CODE_OF_CONDUCT.md +0 -0
  27. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/CONTRIBUTING.md +0 -0
  28. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/Dockerfile +0 -0
  29. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/Dockerfile.worker +0 -0
  30. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/LICENSE +0 -0
  31. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/NOTICE +0 -0
  32. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/README.md +0 -0
  33. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/SECURITY.md +0 -0
  34. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/build/kernel_cache/.gitignore +0 -0
  35. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/build/kernel_cache/.keep +0 -0
  36. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/Dockerfile.kernelcache +0 -0
  37. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/Dockerfile.kernelcache.relayer +0 -0
  38. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/bake_kernel_cache.py +0 -0
  39. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/bake_pod_entry.py +0 -0
  40. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/kernel_fingerprint.py +0 -0
  41. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/docker/make_rp_handler.py +0 -0
  42. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/__init__.py +0 -0
  43. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/_channel.py +0 -0
  44. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/_fileio.py +0 -0
  45. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/_logging.py +0 -0
  46. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/_update_check.py +0 -0
  47. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/adapter_artifacts.py +0 -0
  48. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/catalog.py +0 -0
  49. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/TRAINING.md +0 -0
  50. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/__init__.py +0 -0
  51. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/__main__.py +0 -0
  52. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/_tty.py +0 -0
  53. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/commands.py +0 -0
  54. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/env_eval.py +0 -0
  55. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/env_setup.py +0 -0
  56. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/env_test.py +0 -0
  57. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/envpush.py +0 -0
  58. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/render.py +0 -0
  59. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/traces.py +0 -0
  60. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cli/training_doc.py +0 -0
  61. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/client/__init__.py +0 -0
  62. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/client/config.py +0 -0
  63. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/client/http.py +0 -0
  64. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/client/runtime_secrets.py +0 -0
  65. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/client/specs.py +0 -0
  66. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/__init__.py +0 -0
  67. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/analytical.py +0 -0
  68. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/colocation.py +0 -0
  69. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/facts.py +0 -0
  70. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/spec.py +0 -0
  71. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/cost/types.py +0 -0
  72. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/diagnostics.py +0 -0
  73. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/__init__.py +0 -0
  74. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/accounting.py +0 -0
  75. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/multiturn_reward_scoring.py +0 -0
  76. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/recipe.py +0 -0
  77. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/reward_profile.py +0 -0
  78. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/steps.py +0 -0
  79. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/structured_outputs.py +0 -0
  80. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/vram.py +0 -0
  81. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/__init__.py +0 -0
  82. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/__main__.py +0 -0
  83. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/_pkg.py +0 -0
  84. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/adapter.py +0 -0
  85. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/backend_common.py +0 -0
  86. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/decoding.py +0 -0
  87. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/finalize.py +0 -0
  88. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/grpo.py +0 -0
  89. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/grpo_multiturn.py +0 -0
  90. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/grpo_plugin.py +0 -0
  91. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/heartbeat.py +0 -0
  92. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/hf.py +0 -0
  93. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/kernel_warmup.py +0 -0
  94. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/lora.py +0 -0
  95. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/multiturn_glue.py +0 -0
  96. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd.py +0 -0
  97. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd_gkd.py +0 -0
  98. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd_multiturn.py +0 -0
  99. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd_plugin.py +0 -0
  100. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd_structured.py +0 -0
  101. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/opd_train.py +0 -0
  102. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/packing.py +0 -0
  103. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/__init__.py +0 -0
  104. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/attn.py +0 -0
  105. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/diagnostics.py +0 -0
  106. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/lifecycle.py +0 -0
  107. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/liger.py +0 -0
  108. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/loraplus.py +0 -0
  109. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/perf/memory.py +0 -0
  110. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/rl.py +0 -0
  111. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/rng.py +0 -0
  112. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/rollout_samples.py +0 -0
  113. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/sft.py +0 -0
  114. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/teacher.py +0 -0
  115. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/tokenizer_align.py +0 -0
  116. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker/wandb_log.py +0 -0
  117. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/engine/worker_entrypoint.py +0 -0
  118. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/__init__.py +0 -0
  119. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/adapter.py +0 -0
  120. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/archive.py +0 -0
  121. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/archive_policy.py +0 -0
  122. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/base.py +0 -0
  123. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/evaluations.py +0 -0
  124. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/loader.py +0 -0
  125. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/pull.py +0 -0
  126. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/envs/registry.py +0 -0
  127. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/lora_rank.py +0 -0
  128. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/multimodal.py +0 -0
  129. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/opd_retry_contract.py +0 -0
  130. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/opd_validation.py +0 -0
  131. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/__init__.py +0 -0
  132. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_auth.py +0 -0
  133. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_deadline.py +0 -0
  134. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_hf_artifacts.py +0 -0
  135. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_hf_retry.py +0 -0
  136. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_http.py +0 -0
  137. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_instance.py +0 -0
  138. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_instance_bootstrap.py +0 -0
  139. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_instance_poll.py +0 -0
  140. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_instance_provider.py +0 -0
  141. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_poll.py +0 -0
  142. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/_worker.py +0 -0
  143. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/allocator.py +0 -0
  144. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/base.py +0 -0
  145. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/__init__.py +0 -0
  146. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/api.py +0 -0
  147. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/auth.py +0 -0
  148. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/gpus.py +0 -0
  149. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  150. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  151. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/preflight.py +0 -0
  152. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/lambdalabs/pricing.py +0 -0
  153. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/preflight.py +0 -0
  154. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/realized.py +0 -0
  155. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/__init__.py +0 -0
  156. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/api.py +0 -0
  157. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/auth.py +0 -0
  158. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/cost.py +0 -0
  159. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/gpus.py +0 -0
  160. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/jobs.py +0 -0
  161. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/keys.py +0 -0
  162. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/preflight.py +0 -0
  163. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/pricing.py +0 -0
  164. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/slots.py +0 -0
  165. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/train/__init__.py +0 -0
  166. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/runpod/train/endpoints.py +0 -0
  167. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/__init__.py +0 -0
  168. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/api.py +0 -0
  169. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/auth.py +0 -0
  170. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/jobs/__init__.py +0 -0
  171. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/jobs/builders.py +0 -0
  172. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/preflight.py +0 -0
  173. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/vast/pricing.py +0 -0
  174. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/providers/weight_cache_preload.py +0 -0
  175. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/py.typed +0 -0
  176. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/runner/__init__.py +0 -0
  177. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/runner/checkpoints.py +0 -0
  178. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/runner/deploy.py +0 -0
  179. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/runner/lifecycle.py +0 -0
  180. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/runner/verified_revisions.py +0 -0
  181. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/schema/__init__.py +0 -0
  182. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/schema/fields.py +0 -0
  183. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/__init__.py +0 -0
  184. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/deploy.py +0 -0
  185. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/export.py +0 -0
  186. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/preflight.py +0 -0
  187. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/pricing.py +0 -0
  188. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/serve/urls.py +0 -0
  189. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/__init__.py +0 -0
  190. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/__main__.py +0 -0
  191. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/_deps.py +0 -0
  192. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/_internal_client.py +0 -0
  193. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/_locks.py +0 -0
  194. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/_runtime.py +0 -0
  195. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/app.py +0 -0
  196. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/auth.py +0 -0
  197. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/billing.py +0 -0
  198. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/billing_retry.py +0 -0
  199. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/checkpoints.py +0 -0
  200. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/db.py +0 -0
  201. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/environment_registry.py +0 -0
  202. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/envs.py +0 -0
  203. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/projects.py +0 -0
  204. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/reconcile.py +0 -0
  205. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/repo_cleanup.py +0 -0
  206. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/routes/__init__.py +0 -0
  207. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/routes/envs.py +0 -0
  208. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/routes/meta.py +0 -0
  209. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/routes/runs.py +0 -0
  210. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/routes/serving.py +0 -0
  211. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/server/run_registry.py +0 -0
  212. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/spec.py +0 -0
  213. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/flash/thinking.py +0 -0
  214. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/infisical-entrypoint.sh +0 -0
  215. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/scripts/build_dev_dist.py +0 -0
  216. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/scripts/scrub_history.sh +0 -0
  217. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/__init__.py +0 -0
  218. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/_helpers/__init__.py +0 -0
  219. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/_helpers/runner.py +0 -0
  220. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/_helpers/specs.py +0 -0
  221. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/_helpers/vast.py +0 -0
  222. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/conftest.py +0 -0
  223. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/fixtures/math_eval.jsonl +0 -0
  224. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/fixtures/math_train.jsonl +0 -0
  225. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/live/__init__.py +0 -0
  226. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/live/conftest.py +0 -0
  227. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/live/test_lambda_live.py +0 -0
  228. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/live/test_runpod_live.py +0 -0
  229. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/live/test_vast_live.py +0 -0
  230. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_accounting_coverage.py +0 -0
  231. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_agent_flash_cli_contract.py +0 -0
  232. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_algorithms.py +0 -0
  233. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_allocator.py +0 -0
  234. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_async_uploads.py +0 -0
  235. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_backend_common.py +0 -0
  236. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_backend_jobspec_contract.py +0 -0
  237. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_base_model_provenance.py +0 -0
  238. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_billing_retry.py +0 -0
  239. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cancel_remote.py +0 -0
  240. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_catalog_consistency.py +0 -0
  241. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_charge_pricing.py +0 -0
  242. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_checkpoints.py +0 -0
  243. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_commands.py +0 -0
  244. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_commands_coverage.py +0 -0
  245. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_credential_shadowing.py +0 -0
  246. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_errors.py +0 -0
  247. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_estimate.py +0 -0
  248. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_help.py +0 -0
  249. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_main_coverage.py +0 -0
  250. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_managed.py +0 -0
  251. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_render_coverage.py +0 -0
  252. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_render_theme.py +0 -0
  253. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_traces.py +0 -0
  254. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cli_wandb_warning.py +0 -0
  255. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_client.py +0 -0
  256. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_client_server_integration.py +0 -0
  257. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_client_stream_reads.py +0 -0
  258. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_colocation.py +0 -0
  259. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_config_overrides.py +0 -0
  260. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_controlled_experiment_repairs.py +0 -0
  261. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_analytical.py +0 -0
  262. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_equation.py +0 -0
  263. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_estimate.py +0 -0
  264. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_hardware.py +0 -0
  265. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_models.py +0 -0
  266. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_cost_rewards.py +0 -0
  267. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_deploy_latency.py +0 -0
  268. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_dev_channel.py +0 -0
  269. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_disk_gb.py +0 -0
  270. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_endpoint_name.py +0 -0
  271. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_cache_evict.py +0 -0
  272. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_delete.py +0 -0
  273. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_download.py +0 -0
  274. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_download_api.py +0 -0
  275. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_evaluations.py +0 -0
  276. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_no_install.py +0 -0
  277. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_publish.py +0 -0
  278. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_pull.py +0 -0
  279. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_pull_coverage.py +0 -0
  280. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_pull_loader_coverage.py +0 -0
  281. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_pull_managed_control_plane.py +0 -0
  282. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_push.py +0 -0
  283. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_rate_limit_resolve.py +0 -0
  284. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_ref_pinning.py +0 -0
  285. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_env_test.py +0 -0
  286. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_envs_coverage.py +0 -0
  287. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_export.py +0 -0
  288. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_flash_mvp.py +0 -0
  289. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_flash_worker.py +0 -0
  290. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_flashinfer_cache_dirs.py +0 -0
  291. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_github_urlopen_retry.py +0 -0
  292. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_gpu_type_single_field.py +0 -0
  293. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_gpus.py +0 -0
  294. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_grpo_mask_aware.py +0 -0
  295. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_grpo_params.py +0 -0
  296. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_grpo_resident_sizing.py +0 -0
  297. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_health_liveness.py +0 -0
  298. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_hf_retry.py +0 -0
  299. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_idle_endpoint_reaper.py +0 -0
  300. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_instance_bootstrap_coverage.py +0 -0
  301. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_internal_client.py +0 -0
  302. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_jobs.py +0 -0
  303. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_kernel_cache.py +0 -0
  304. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_kernel_fingerprint.py +0 -0
  305. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_kv_util.py +0 -0
  306. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_lambda_api_coverage.py +0 -0
  307. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_lambda_runner.py +0 -0
  308. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_logging.py +0 -0
  309. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_login_perms.py +0 -0
  310. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_lora_rank_coverage.py +0 -0
  311. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_lora_rank_preflight.py +0 -0
  312. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_managed_config_fields.py +0 -0
  313. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_managed_hf_repo.py +0 -0
  314. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_metrics_schema_agent_contract.py +0 -0
  315. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_mig_guard.py +0 -0
  316. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_multi_gpu_gate.py +0 -0
  317. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_multimodal_input_grads.py +0 -0
  318. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_multimodal_training.py +0 -0
  319. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_named_reward_metrics.py +0 -0
  320. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_oom_escalate_gpu.py +0 -0
  321. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_opd.py +0 -0
  322. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_opd_resume_safety.py +0 -0
  323. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_opd_train.py +0 -0
  324. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_open_model_policy.py +0 -0
  325. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_orchestrator_flash.py +0 -0
  326. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_packing.py +0 -0
  327. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_packing_coverage.py +0 -0
  328. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_poll_helpers.py +0 -0
  329. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_preflight.py +0 -0
  330. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_projects.py +0 -0
  331. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_provider_preflight_coverage.py +0 -0
  332. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_provider_routing.py +0 -0
  333. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_provider_teardown_robustness.py +0 -0
  334. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_providers_symmetry.py +0 -0
  335. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_realized_cost.py +0 -0
  336. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_repo_cleanup.py +0 -0
  337. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_resolve_params_b.py +0 -0
  338. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_resume_on_retry.py +0 -0
  339. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_reward_profile.py +0 -0
  340. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_rollout_samples.py +0 -0
  341. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_runmgmt.py +0 -0
  342. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_runpod_api_delete.py +0 -0
  343. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_runpod_key_fingerprint.py +0 -0
  344. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_runpod_key_waterfall.py +0 -0
  345. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_runpod_slots.py +0 -0
  346. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serve.py +0 -0
  347. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serve_reasoning_content.py +0 -0
  348. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_api.py +0 -0
  349. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_app_coverage.py +0 -0
  350. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_auth_singleflight.py +0 -0
  351. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_billing.py +0 -0
  352. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_checkpoints_coverage.py +0 -0
  353. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_db.py +0 -0
  354. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_envs_coverage.py +0 -0
  355. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_server_locks_coverage.py +0 -0
  356. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serving_context_preflight.py +0 -0
  357. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serving_contract.py +0 -0
  358. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serving_pricing_coverage.py +0 -0
  359. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_serving_schema_coverage.py +0 -0
  360. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_sft_chunked_nll.py +0 -0
  361. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_sft_example_selection.py +0 -0
  362. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_sft_gc_off.py +0 -0
  363. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_sft_max_context.py +0 -0
  364. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_spec_and_validation.py +0 -0
  365. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_step_metrics.py +0 -0
  366. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_structured_outputs.py +0 -0
  367. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_thinking_config.py +0 -0
  368. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_train_context_preflight.py +0 -0
  369. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_training_controls.py +0 -0
  370. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_update_check.py +0 -0
  371. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vast_api.py +0 -0
  372. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vast_api_coverage.py +0 -0
  373. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vast_offers.py +0 -0
  374. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vast_provider_coverage.py +0 -0
  375. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vast_runner.py +0 -0
  376. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_verified_revisions.py +0 -0
  377. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_verifiers.py +0 -0
  378. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_version.py +0 -0
  379. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  380. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_vram_architecture.py +0 -0
  381. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_wandb_link.py +0 -0
  382. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_wandb_log_coverage.py +0 -0
  383. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_wandb_naming.py +0 -0
  384. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_warmstart_adapter_download.py +0 -0
  385. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_warmstart_cross_repo.py +0 -0
  386. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_warmstart_error_attribution.py +0 -0
  387. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_weight_cache.py +0 -0
  388. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_dryrun.py +0 -0
  389. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_hardexit.py +0 -0
  390. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_image.py +0 -0
  391. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_init_heartbeat.py +0 -0
  392. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_stack.py +0 -0
  393. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.65}/tests/test_worker_thinking.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 1.0.63
3
+ Version: 1.0.65
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
@@ -105,6 +105,17 @@ _MULTI_TURN_SCORE_BATCH_SIZE = 64
105
105
  _MULTI_TURN_SCORE_FLUSH_WAIT_S = 0.1
106
106
  _MULTI_TURN_SCORE_SHUTDOWN_WAIT_S = 5.0
107
107
 
108
+ # the bridge's listen() backlog. verl opens one connection per episode and starts a whole step's
109
+ # episodes at once, so the accept queue sees the entire rollout batch -- prompts_per_step *
110
+ # group_size, 512 on the default 64x8 recipe -- connecting in a burst. socketserver's default of 5
111
+ # overflows there and the kernel RESETS the excess, which surfaces client-side as
112
+ # ConnectionResetError at getresponse() (the connect and send landed in the queue; the reset arrives
113
+ # when the queue is dropped). bridge_post deliberately does not retry, so that reset kills the run.
114
+ # the opd teacher bridge already carries this fix as _TEXT_TEACHER_REQUEST_BACKLOG; a fixed constant
115
+ # would only move the cliff, so size the queue from the burst the caller actually generates.
116
+ # linux caps the effective backlog at somaxconn (4096 here) and silently clamps beyond it.
117
+ _REWARD_BRIDGE_MIN_REQUEST_BACKLOG = 128
118
+
108
119
 
109
120
  # --------------------------------------------------------------------------------------------
110
121
  # pure helpers (no verl, no gpu, no network) -- unit tested directly.
@@ -1229,14 +1240,20 @@ def render_reentrant_checkpointing_shim(reentrant: bool, *, multimodal: bool = F
1229
1240
  engines whose config may legitimately have checkpointing off, and enabling it there would turn
1230
1241
  on a feature verl chose to leave off.
1231
1242
 
1232
- on a MULTIMODAL run the same hook also restores vision input gradients. reentrant recompute
1233
- drops the backward for a checkpointed block when none of that block's inputs require grad, and
1234
- the vision tower's patch embeddings are exactly that case: the pixels are frozen inputs, so
1235
- without a forward hook marking the patch-embed output as requiring grad the visual modules
1236
- silently receive nothing while the language side trains normally and the run reports success
1237
- (``tests/test_multimodal_input_grads.py``). the retired trl path installed the same hook via a
1238
- trainer callback; verl has no callback surface, so it rides this shim instead -- and only here,
1239
- because non-reentrant recompute does not have the behaviour that makes it necessary
1243
+ reentrant recompute drops the backward for a checkpointed block when none of that block's
1244
+ inputs require grad. that hits the LANGUAGE side on every lora run: lora freezes the
1245
+ embeddings, so the hidden states entering the first checkpointed decoder layer have
1246
+ ``requires_grad=False`` and the whole segment -- containing every lora parameter -- receives no
1247
+ gradient, while the run reports success and bills (GRAD-001). ``enable_input_require_grads()``
1248
+ is what prevents it, and it is unconditional here because every flash rl run is lora.
1249
+
1250
+ on a MULTIMODAL run the same hook additionally restores VISION input gradients. the vision
1251
+ tower's patch embeddings are the same failure in a place the language-side call does not
1252
+ reach: the pixels are frozen inputs, so without a forward hook marking the patch-embed output
1253
+ as requiring grad the visual modules silently receive nothing while the language side trains
1254
+ normally (``tests/test_multimodal_input_grads.py``). the retired trl path installed that hook
1255
+ via a trainer callback; verl has no callback surface, so it rides this shim instead -- and only
1256
+ here, because non-reentrant recompute does not have the behaviour that makes it necessary
1240
1257
  (codex[bot]).
1241
1258
  """
1242
1259
  if not reentrant:
@@ -1290,6 +1307,13 @@ def _flash_reentrant_build_module(self):
1290
1307
  # only when verl actually enabled checkpointing: calling gradient_checkpointing_enable on a
1291
1308
  # model verl deliberately left uncheckpointed would turn it ON and change the memory profile.
1292
1309
  if getattr(self.model_config, "enable_gradient_checkpointing", False):
1310
+ # the LANGUAGE-side counterpart of the vision hook below, and required for the same
1311
+ # reason (GRAD-001): lora freezes the embeddings, so nothing entering the first
1312
+ # checkpointed decoder layer requires grad and reentrant recompute drops the backward
1313
+ # for the whole segment -- where every lora parameter lives. the vision hook only ever
1314
+ # covered the patch embeddings on multimodal runs; text-only runs had no hook at all and
1315
+ # trained nothing while reporting success.
1316
+ module.enable_input_require_grads()
1293
1317
  module.gradient_checkpointing_enable(gradient_checkpointing_kwargs={{"use_reentrant": True}}){vision_hook}
1294
1318
  print("[rl-verl] reentrant gradient checkpointing is active", flush=True)
1295
1319
  return module
@@ -2071,12 +2095,15 @@ def multi_turn_child_env(inp: dict, *, reward_url: str, thinking: bool) -> dict[
2071
2095
  }
2072
2096
 
2073
2097
 
2074
- def start_reward_server(score_by_index, *, example_count: int, multi_turn_bridge=None):
2098
+ def start_reward_server(
2099
+ score_by_index, *, example_count: int, multi_turn_bridge=None, rollout_batch: int = 0
2100
+ ):
2075
2101
  """start a localhost http reward server. score_by_index(index, solution_str) -> float.
2076
2102
 
2077
2103
  returns (server, base_url). the server runs in a daemon thread; call server.shutdown() when
2078
2104
  done. single-turn scoring lives at ``<base_url>/score``; when ``multi_turn_bridge`` is given,
2079
- its four episode routes are served alongside it from the same thread pool.
2105
+ its four episode routes are served alongside it from the same thread pool. ``rollout_batch`` is
2106
+ how many episodes verl starts at once (prompts_per_step * group_size) and sizes the accept queue.
2080
2107
  """
2081
2108
  # serialize scoring so the flash env sees sequential calls: a flash env is a plain python object
2082
2109
  # with no concurrency contract, and the retired trl path only ever called it from one thread.
@@ -2123,7 +2150,10 @@ def start_reward_server(score_by_index, *, example_count: int, multi_turn_bridge
2123
2150
  self.end_headers()
2124
2151
  self.wfile.write(body)
2125
2152
 
2126
- server = ThreadingHTTPServer(("127.0.0.1", 0), _Handler)
2153
+ class _RewardBridgeHTTPServer(ThreadingHTTPServer):
2154
+ request_queue_size = max(_REWARD_BRIDGE_MIN_REQUEST_BACKLOG, int(rollout_batch))
2155
+
2156
+ server = _RewardBridgeHTTPServer(("127.0.0.1", 0), _Handler)
2127
2157
  threading.Thread(target=server.serve_forever, daemon=True).start()
2128
2158
  port = server.server_address[1]
2129
2159
  return server, f"http://127.0.0.1:{port}"
@@ -3159,6 +3189,7 @@ def run_rl_train():
3159
3189
  _score,
3160
3190
  example_count=len(rollout_examples),
3161
3191
  multi_turn_bridge=multi_turn_bridge,
3192
+ rollout_batch=int(inp["prompts_per_step"]) * int(inp["group_size"]),
3162
3193
  )
3163
3194
  # bound before the try so the finally can always ask whether it was started.
3164
3195
  resume_uploader: _VerlResumeUploader | None = None
@@ -57,6 +57,10 @@ from flash.engine.worker.sft import (
57
57
  _SFT_LORAPLUS_RATIO = 16.0
58
58
  _LORAPLUS_READY_MARKER = "FLASH_LORAPLUS_READY"
59
59
  _VERL_STEP_RE = re.compile(r"(?:^|\s)step:(\d+)(?:\s|$)")
60
+ # consecutive zero-grad-norm steps tolerated before the run is failed as untrainable (GRAD-001).
61
+ # 2 is enough to separate a one-off fully-masked batch from a severed backward graph, and keeps
62
+ # the wasted spend to a couple of steps rather than the whole run.
63
+ _MAX_ZERO_GRAD_STEPS = 2
60
64
 
61
65
  _REQUIRED_OVERRIDE_KEYS = (
62
66
  "train_files",
@@ -535,6 +539,15 @@ _flash_original_build_module = _FlashFSDPEngine._build_module
535
539
 
536
540
  def _flash_build_reentrant_module(self):
537
541
  module = _flash_original_build_module(self)
542
+ # REQUIRED before enabling reentrant checkpointing on a lora model, and the reason every
543
+ # verl sft run silently trained nothing (GRAD-001). lora freezes the embeddings, so the
544
+ # hidden states entering the first checkpointed decoder layer have requires_grad=False.
545
+ # reentrant checkpointing needs at least one input tensor requiring grad to attach the
546
+ # recomputed forward to the graph; with none it warns "None of the inputs have
547
+ # requires_grad=True. Gradients will be None" and returns no gradient for the whole
548
+ # segment -- which is where every lora parameter lives. result was grad_norm 0.0 on every
549
+ # step while the run reported done and billed. opd already does this; sft did not.
550
+ module.enable_input_require_grads()
538
551
  module.gradient_checkpointing_enable(gradient_checkpointing_kwargs={"use_reentrant": True})
539
552
  return module
540
553
 
@@ -1340,6 +1353,9 @@ def run_sft_train(spec=None) -> None:
1340
1353
  ]
1341
1354
 
1342
1355
  progress = {"step": resume_step, "loss": None, "grad_norm": None, "lr": None}
1356
+ # consecutive steps seen with grad_norm == 0.0 at a nonzero lr. one step can legitimately be
1357
+ # zero (a fully-masked micro-batch), so require a short run of them before failing.
1358
+ zero_grad_steps: list[int] = []
1343
1359
  loss_curve: list[float] = []
1344
1360
  train_tokens = sft_completed_train_tokens(
1345
1361
  total_tokens_per_epoch,
@@ -1379,6 +1395,21 @@ def run_sft_train(spec=None) -> None:
1379
1395
  progress["loss"] = loss
1380
1396
  if grad_norm is not None:
1381
1397
  progress["grad_norm"] = grad_norm
1398
+ # a grad norm of exactly 0.0 while the lr is still nonzero means the backward pass
1399
+ # produced nothing for every trainable parameter. that is never legitimate: it is a
1400
+ # broken graph, not a small update. GRAD-001 shipped four runs that reported done and
1401
+ # billed while training nothing, because this number was recorded and never read.
1402
+ # fail the run instead of paying for a zero adapter that then deploys and serves.
1403
+ if grad_norm == 0.0 and (learning_rate_value is None or learning_rate_value > 0.0):
1404
+ zero_grad_steps.append(int(progress["step"] or 0))
1405
+ if len(zero_grad_steps) >= _MAX_ZERO_GRAD_STEPS:
1406
+ raise RuntimeError(
1407
+ "verl reported train/grad_norm=0.0 with a nonzero learning rate on "
1408
+ f"{len(zero_grad_steps)} consecutive steps: no gradient is reaching the "
1409
+ "lora parameters, so this run would train nothing. see GRAD-001"
1410
+ )
1411
+ else:
1412
+ zero_grad_steps.clear()
1382
1413
  if learning_rate_value is not None:
1383
1414
  progress["lr"] = learning_rate_value
1384
1415
 
@@ -1402,6 +1433,14 @@ def run_sft_train(spec=None) -> None:
1402
1433
  return_code = 0
1403
1434
  if resume_step < update_horizon:
1404
1435
  watcher.start()
1436
+ # completeness is only a meaningful question when training ran to the end. an on_line
1437
+ # callback that raises (the zero-grad guard above, the lora+ guard) unwinds BEFORE
1438
+ # return_code is assigned, so deriving the flag from return_code alone would leave it at
1439
+ # its initial 0 and demand every required save from a run that stopped at step 2. the
1440
+ # watcher would then raise "required saves were not durably published" from the finally
1441
+ # and REPLACE the diagnosis with a downstream symptom. opd_train tracks the same flag for
1442
+ # the same reason.
1443
+ training_completed = False
1405
1444
  try:
1406
1445
  with liveness_heartbeat(
1407
1446
  "sft_step",
@@ -1415,8 +1454,9 @@ def run_sft_train(spec=None) -> None:
1415
1454
  on_line=on_line,
1416
1455
  heartbeat=child_heartbeat,
1417
1456
  )
1457
+ training_completed = return_code == 0
1418
1458
  finally:
1419
- watcher.stop(require_complete=return_code == 0)
1459
+ watcher.stop(require_complete=training_completed)
1420
1460
  train_wall = time.time() - train_started_at
1421
1461
  device_peak_gpu_gb = gpu_sampler.stop_gb()
1422
1462
  if return_code != 0:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "1.0.63"
3
+ version = "1.0.65"
4
4
  description = "Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI"
5
5
  readme = "README.md"
6
6
  # RunPod Flash supports Python 3.11-3.12 (not 3.13 yet).
@@ -123,7 +123,7 @@ artifacts = ["flash/py.typed"]
123
123
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
124
124
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
125
125
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
126
- version = "1.0.63"
126
+ version = "1.0.65"
127
127
 
128
128
  [tool.pytest.ini_options]
129
129
  pythonpath = ["."]
@@ -11,6 +11,7 @@ import math
11
11
  import os
12
12
  import re
13
13
  import shutil
14
+ import socket
14
15
  import sys
15
16
  import textwrap
16
17
  import threading
@@ -1202,6 +1203,52 @@ def test_concurrent_scorers_are_serialized_for_the_env():
1202
1203
  server.shutdown()
1203
1204
 
1204
1205
 
1206
+ def test_reward_server_accept_queue_holds_a_whole_rollout_batch(monkeypatch):
1207
+ # verl opens one connection per episode and starts a whole step at once, so the accept queue
1208
+ # sees prompts_per_step * group_size connects in a burst. socketserver's default backlog of 5
1209
+ # overflows there and the kernel RESETS the excess, which reaches the client as
1210
+ # ConnectionResetError at getresponse(); bridge_post does not retry, so that kills the run.
1211
+ rollout_batch = 512 # the default 64x8 recipe
1212
+
1213
+ # record the argument the server actually hands to listen(). asserting on request_queue_size
1214
+ # alone cannot tell a working fix from a no-op: server_activate() reads that attribute once,
1215
+ # so a value set after server_bind() would never reach the socket at all.
1216
+ backlogs = []
1217
+ real_listen = socket.socket.listen
1218
+
1219
+ def spy_listen(self, *args):
1220
+ backlogs.append(args[0] if args else None)
1221
+ return real_listen(self, *args)
1222
+
1223
+ monkeypatch.setattr(socket.socket, "listen", spy_listen)
1224
+
1225
+ server, _url = rl_train.start_reward_server(
1226
+ lambda idx, solution: 1.0, example_count=8, rollout_batch=rollout_batch
1227
+ )
1228
+ try:
1229
+ assert server.request_queue_size >= rollout_batch
1230
+ assert backlogs, "server never called listen()"
1231
+ assert backlogs[-1] is not None, "listen() was called with no backlog argument"
1232
+ assert backlogs[-1] >= rollout_batch, f"listen() backlog is {backlogs[-1]}"
1233
+ finally:
1234
+ server.shutdown()
1235
+ server.server_close()
1236
+
1237
+
1238
+ @pytest.mark.parametrize("rollout_batch", [0, 32, 64 * 8, 2048])
1239
+ def test_reward_bridge_backlog_never_falls_below_the_burst(rollout_batch):
1240
+ # a fixed constant would only move the cliff, so the queue is sized from the caller's burst.
1241
+ # an unspecified batch still keeps a floor well clear of socketserver's default of 5.
1242
+ server, _url = rl_train.start_reward_server(
1243
+ lambda idx, solution: 1.0, example_count=1, rollout_batch=rollout_batch
1244
+ )
1245
+ try:
1246
+ assert server.request_queue_size >= max(128, rollout_batch)
1247
+ finally:
1248
+ server.shutdown()
1249
+ server.server_close()
1250
+
1251
+
1205
1252
  # ------------------------------- reward parity -------------------------------
1206
1253
  class _BreakdownEnv:
1207
1254
  def scores_breakdown(self, graded, ex, state):
@@ -1699,6 +1746,48 @@ def test_the_reentrant_shim_installs_vision_input_grads_only_for_multimodal_runs
1699
1746
  assert rl_train.render_reentrant_checkpointing_shim(False, multimodal=True) == ""
1700
1747
 
1701
1748
 
1749
+ def test_the_reentrant_shim_enables_language_side_input_grads_before_checkpointing():
1750
+ """GRAD-001: every rl run is lora, so lora freezes the embeddings and nothing entering the
1751
+ first checkpointed decoder layer requires grad. reentrant recompute then drops the backward
1752
+ for the whole segment -- where every lora parameter lives -- and the run reports success while
1753
+ training nothing. the vision hook only ever covered the patch embeddings on multimodal runs;
1754
+ text-only runs had no hook at all."""
1755
+ calls = []
1756
+
1757
+ class FakeModule:
1758
+ def enable_input_require_grads(self):
1759
+ calls.append("require_grads")
1760
+
1761
+ def gradient_checkpointing_enable(self, **kwargs):
1762
+ calls.append(("gc_enable", kwargs))
1763
+
1764
+ class FakeEngine:
1765
+ def __init__(self, checkpointing):
1766
+ self.model_config = SimpleNamespace(enable_gradient_checkpointing=checkpointing)
1767
+
1768
+ def _build_module(self):
1769
+ return FakeModule()
1770
+
1771
+ source = rl_train.render_reentrant_checkpointing_shim(True)
1772
+ start = source.index("def _flash_reentrant_build_module")
1773
+ end = source.index("_FlashReentrantEngine._build_module = _flash_reentrant_build_module")
1774
+ namespace = {"_flash_reentrant_original_build_module": FakeEngine._build_module}
1775
+ exec(compile(source[start:end], "sitecustomize.py", "exec"), namespace)
1776
+ build = namespace["_flash_reentrant_build_module"]
1777
+
1778
+ build(FakeEngine(checkpointing=True))
1779
+ # order matters: enabling checkpointing first captures the graph before any input requires
1780
+ # grad, so asserting mere presence would pass on a broken shim.
1781
+ assert calls[0] == "require_grads"
1782
+ assert calls[1] == ("gc_enable", {"gradient_checkpointing_kwargs": {"use_reentrant": True}})
1783
+
1784
+ # the guard is load-bearing: when verl left checkpointing OFF, touching the module at all
1785
+ # would turn on a feature verl deliberately declined and change the memory profile.
1786
+ calls.clear()
1787
+ build(FakeEngine(checkpointing=False))
1788
+ assert calls == []
1789
+
1790
+
1702
1791
  @contextlib.contextmanager
1703
1792
  def _vision_hook_installer(torch_module):
1704
1793
  """yield the rendered vision helper with a stand-in torch visible to it.
@@ -1831,6 +1920,10 @@ def test_the_reentrant_shim_flips_the_flag_and_leaves_uncheckpointed_models_alon
1831
1920
  class _Module:
1832
1921
  def __init__(self):
1833
1922
  self.kwargs = None
1923
+ self.input_grads = False
1924
+
1925
+ def enable_input_require_grads(self):
1926
+ self.input_grads = True
1834
1927
 
1835
1928
  def gradient_checkpointing_enable(self, gradient_checkpointing_kwargs=None):
1836
1929
  self.kwargs = gradient_checkpointing_kwargs
@@ -1857,12 +1950,17 @@ def test_the_reentrant_shim_flips_the_flag_and_leaves_uncheckpointed_models_alon
1857
1950
  sys.modules.setdefault(name, types.ModuleType(name))
1858
1951
  sys.modules[module_stub.__name__] = module_stub
1859
1952
  exec(rl_train.render_reentrant_checkpointing_shim(True), {})
1860
- # checkpointing on -> the flag is put back to reentrant
1953
+ # checkpointing on -> the flag is put back to reentrant, and the lora-frozen embeddings
1954
+ # get input grads so the checkpointed segment actually produces a backward (GRAD-001)
1861
1955
  engine = _Engine(True)
1862
- assert engine._build_module().kwargs == {"use_reentrant": True}
1956
+ built = engine._build_module()
1957
+ assert built.kwargs == {"use_reentrant": True}
1958
+ assert built.input_grads is True
1863
1959
  # checkpointing off -> untouched, so the shim cannot silently raise the memory profile
1864
1960
  off = _Engine(False)
1865
- assert off._build_module().kwargs is None
1961
+ off_built = off._build_module()
1962
+ assert off_built.kwargs is None
1963
+ assert off_built.input_grads is False
1866
1964
  finally:
1867
1965
  for name, value in saved.items():
1868
1966
  if value is None:
@@ -5,15 +5,18 @@ from __future__ import annotations
5
5
  import contextlib
6
6
  import importlib.util
7
7
  import os
8
+ import re
8
9
  import sys
9
10
  import types
10
11
  from types import SimpleNamespace
11
12
 
12
13
  import pytest
13
14
 
15
+ from flash.engine.worker.backend_common import parse_verl_metric
14
16
  from flash.engine.worker.sft import _pretokenize_completion_only
15
17
  from flash.engine.worker.sft_train import (
16
18
  _LORAPLUS_READY_MARKER,
19
+ _MAX_ZERO_GRAD_STEPS,
17
20
  _VERL_OPTIMIZER_IMPL,
18
21
  _VERL_OPTIMIZER_NAME,
19
22
  _build_verl_child_env,
@@ -478,6 +481,115 @@ def test_generated_sitecustomize_installs_linear_scheduler_and_required_loraplus
478
481
  assert scheduler_calls == [("optimizer", {"num_warmup_steps": 2, "num_training_steps": 20})]
479
482
 
480
483
 
484
+ def test_reentrant_checkpointing_enables_input_grads_before_enabling_checkpointing():
485
+ """GRAD-001: lora freezes the embeddings, so nothing entering the first checkpointed layer
486
+ requires grad and reentrant checkpointing returns no gradient at all. the shim must call
487
+ enable_input_require_grads() BEFORE gradient_checkpointing_enable(), or every sft run
488
+ trains nothing while reporting done and billing."""
489
+ calls = []
490
+
491
+ class FakeModule:
492
+ def enable_input_require_grads(self):
493
+ calls.append("require_grads")
494
+
495
+ def gradient_checkpointing_enable(self, **kwargs):
496
+ calls.append(("gc_enable", kwargs))
497
+
498
+ class FakeEngine:
499
+ def _build_module(self):
500
+ return FakeModule()
501
+
502
+ source = _render_sft_sitecustomize(
503
+ seed=1,
504
+ loraplus_ratio=16,
505
+ save_at_steps=(),
506
+ total_steps=4,
507
+ reentrant_gradient_checkpointing=True,
508
+ )
509
+ # execute only the reentrant block: the surrounding shim imports verl/torch at module scope.
510
+ block = source[source.index("def _flash_build_reentrant_module") :]
511
+ block = block[: block.index("_FlashFSDPEngine._build_module = _flash_build_reentrant_module")]
512
+ namespace = {"_flash_original_build_module": FakeEngine._build_module}
513
+ exec(compile(block, "shim.py", "exec"), namespace)
514
+
515
+ namespace["_flash_build_reentrant_module"](FakeEngine())
516
+
517
+ # order matters: enabling checkpointing first would capture the graph before any input
518
+ # requires grad, so asserting mere presence would pass on a broken shim.
519
+ assert calls[0] == "require_grads"
520
+ assert calls[1] == ("gc_enable", {"gradient_checkpointing_kwargs": {"use_reentrant": True}})
521
+
522
+ # the non-reentrant path never patches _build_module at all, so it must not appear.
523
+ non_reentrant = _render_sft_sitecustomize(
524
+ seed=1,
525
+ loraplus_ratio=16,
526
+ save_at_steps=(),
527
+ total_steps=4,
528
+ reentrant_gradient_checkpointing=False,
529
+ )
530
+ assert "enable_input_require_grads" not in non_reentrant
531
+
532
+
533
+ def test_zero_grad_norm_at_nonzero_lr_fails_the_run():
534
+ """GRAD-001: four runs reported done and charged while grad_norm was 0.0 on every step.
535
+ the number was parsed and recorded, never read. replay the real g4 log lines."""
536
+
537
+ def replay(lines):
538
+ zero_grad_steps: list[int] = []
539
+ for step, line in enumerate(lines):
540
+ grad_norm = parse_verl_metric(line, "train/grad_norm")
541
+ learning_rate_value = parse_verl_metric(line, "train/lr")
542
+ if grad_norm is None:
543
+ continue
544
+ if grad_norm == 0.0 and (learning_rate_value is None or learning_rate_value > 0.0):
545
+ zero_grad_steps.append(step)
546
+ if len(zero_grad_steps) >= _MAX_ZERO_GRAD_STEPS:
547
+ raise RuntimeError(
548
+ "verl reported train/grad_norm=0.0 with a nonzero learning rate on "
549
+ f"{len(zero_grad_steps)} consecutive steps: no gradient is reaching "
550
+ "the lora parameters, so this run would train nothing. see GRAD-001"
551
+ )
552
+ else:
553
+ zero_grad_steps.clear()
554
+
555
+ # verbatim shape of the g4 gsm8k lines (flash-1785592071-e56cf3c6): loss barely moves on a
556
+ # replayed identical batch because nothing is learning.
557
+ broken = [
558
+ "step:1 - train/loss:0.5470 - train/grad_norm:0.0 - train/lr:0.0001",
559
+ "step:2 - train/loss:0.5437 - train/grad_norm:0.0 - train/lr:0.0001",
560
+ "step:3 - train/loss:0.5474 - train/grad_norm:0.0 - train/lr:0.0001",
561
+ "step:4 - train/loss:0.5444 - train/grad_norm:0.0 - train/lr:0.0001",
562
+ ]
563
+ with pytest.raises(RuntimeError, match=re.escape("grad_norm=0.0")):
564
+ replay(broken)
565
+
566
+ # a healthy run must not trip the guard.
567
+ replay(
568
+ [
569
+ "step:1 - train/loss:0.9 - train/grad_norm:1.4 - train/lr:0.0001",
570
+ "step:2 - train/loss:0.7 - train/grad_norm:0.9 - train/lr:0.0001",
571
+ ]
572
+ )
573
+
574
+ # an isolated zero is a legitimately fully-masked micro-batch, not a severed graph.
575
+ replay(
576
+ [
577
+ "step:1 - train/loss:0.9 - train/grad_norm:0.0 - train/lr:0.0001",
578
+ "step:2 - train/loss:0.7 - train/grad_norm:1.1 - train/lr:0.0001",
579
+ "step:3 - train/loss:0.6 - train/grad_norm:0.0 - train/lr:0.0001",
580
+ ]
581
+ )
582
+
583
+ # a decayed schedule reaching lr 0.0 produces a legitimate zero grad norm.
584
+ replay(
585
+ [
586
+ "step:1 - train/loss:0.5 - train/grad_norm:0.0 - train/lr:0.0",
587
+ "step:2 - train/loss:0.5 - train/grad_norm:0.0 - train/lr:0.0",
588
+ "step:3 - train/loss:0.5 - train/grad_norm:0.0 - train/lr:0.0",
589
+ ]
590
+ )
591
+
592
+
481
593
  def test_loraplus_shim_has_no_plain_lora_fallback():
482
594
  source = render_loraplus_shim(16)
483
595
  assert _LORAPLUS_READY_MARKER in source
@@ -615,7 +727,11 @@ def test_resume_credits_only_required_saves_that_are_durable(monkeypatch):
615
727
  assert sft_train._durable_required_save_steps((3, 5, 9), 5) == {3}
616
728
 
617
729
 
618
- def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypatch):
730
+ def _stub_sft_run(monkeypatch, *, save_at_steps=(), watcher_cls=None):
731
+ """monkeypatch every out-of-process dependency of run_sft_train and return (spec, captured).
732
+
733
+ the caller supplies its own ``run_verl_training`` fake, which is the only remaining seam.
734
+ """
619
735
  import flash.engine.worker as worker
620
736
  from flash.engine.worker import sft_train
621
737
 
@@ -630,7 +746,7 @@ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypa
630
746
  max_context_tokens=1024,
631
747
  max_examples=2,
632
748
  max_steps=2,
633
- save_at_steps=(),
749
+ save_at_steps=save_at_steps,
634
750
  save_every=50,
635
751
  init_from_adapter="",
636
752
  ),
@@ -683,7 +799,7 @@ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypa
683
799
  def stop_gb(self):
684
800
  return 12.5
685
801
 
686
- class Watcher:
802
+ class _DefaultWatcher:
687
803
  def __init__(self, **kwargs):
688
804
  self.processed_steps = set()
689
805
 
@@ -696,7 +812,25 @@ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypa
696
812
  def raise_if_failed(self):
697
813
  return None
698
814
 
815
+ Watcher = watcher_cls or _DefaultWatcher
816
+
699
817
  captured = {"heartbeats": [], "published": [], "uploads": []}
818
+ # run_sft_train imports AutoProcessor at data-loading time and transformers is not installed in
819
+ # the cpu test env. this used to pass only because some EARLIER test module left a transformers
820
+ # stub in sys.modules, so running this file alone failed -- stub it here so the test stands on
821
+ # its own. monkeypatch.setitem restores whatever was there (real module or nothing) afterwards.
822
+ monkeypatch.setitem(
823
+ sys.modules,
824
+ "transformers",
825
+ sys.modules.get("transformers")
826
+ or _module(
827
+ "transformers",
828
+ AutoProcessor=SimpleNamespace(from_pretrained=lambda *a, **k: None),
829
+ # datasets' dill serializer issubclass()-checks against this while writing the
830
+ # parquet, so it has to be a real class rather than a namespace attribute.
831
+ PreTrainedTokenizerBase=type("PreTrainedTokenizerBase", (), {}),
832
+ ),
833
+ )
700
834
  monkeypatch.delenv("WANDB_API_KEY", raising=False)
701
835
  monkeypatch.setattr(worker, "SEED", 7)
702
836
  monkeypatch.setattr(worker, "RUN_ID", "test-sft-verl-orchestration")
@@ -765,6 +899,14 @@ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypa
765
899
 
766
900
  monkeypatch.setattr(sft_train, "_export_checkpoint_adapter", fake_export)
767
901
 
902
+ return spec, captured
903
+
904
+
905
+ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypatch):
906
+ from flash.engine.worker import sft_train
907
+
908
+ spec, captured = _stub_sft_run(monkeypatch)
909
+
768
910
  def fake_training(command, *, env, on_step, on_line, heartbeat):
769
911
  captured["command"] = command
770
912
  captured["child_env"] = env
@@ -798,6 +940,61 @@ def test_run_sft_train_orchestrates_exact_dataset_and_resume_accounting(monkeypa
798
940
  assert captured["meta"]["notes"]["realized_max_length"] > 0
799
941
 
800
942
 
943
+ def test_a_guard_failure_is_not_replaced_by_the_watcher_completeness_error(monkeypatch):
944
+ """the zero-grad diagnosis must survive the finally block, not be overwritten by it.
945
+
946
+ an on_line guard raises from INSIDE run_verl_training, so return_code is never assigned and
947
+ keeps its initial 0. deriving require_complete from it would then demand every save_at_steps
948
+ entry from a run that died at step 2, and the watcher's "required saves were not durably
949
+ published" would unwind out of the finally in place of the real cause -- turning the one
950
+ error GRAD-001 exists to surface into a checkpointing red herring.
951
+ """
952
+ from flash.engine.worker import sft_train
953
+
954
+ stopped: list[bool] = []
955
+
956
+ class Watcher:
957
+ def __init__(self, **kwargs):
958
+ self.processed_steps = set()
959
+ self.required_steps = frozenset(kwargs.get("required_steps", ()))
960
+
961
+ def start(self):
962
+ return None
963
+
964
+ def raise_if_failed(self):
965
+ return None
966
+
967
+ def stop(self, *, require_complete):
968
+ stopped.append(require_complete)
969
+ if require_complete:
970
+ missing = sorted(self.required_steps - self.processed_steps)
971
+ if missing:
972
+ raise RuntimeError(f"required saves were not durably published: {missing}")
973
+
974
+ # a required save the run never durably publishes: the guard raises on the same step, before
975
+ # the watcher has processed it. (the step is inside the 2-update horizon because
976
+ # validate_save_steps rejects anything beyond it at config time.)
977
+ spec, _ = _stub_sft_run(monkeypatch, save_at_steps=(2,), watcher_cls=Watcher)
978
+
979
+ def fake_training(command, *, env, on_step, on_line, heartbeat):
980
+ on_line(f"{_LORAPLUS_READY_MARKER} ratio=16 optimizer=AdamW\n")
981
+ for step in range(1, _MAX_ZERO_GRAD_STEPS + 1):
982
+ on_line(
983
+ f"step:{step} - train/loss:1.0 - train/grad_norm:0.0 - train/lr:5e-05 "
984
+ "- train/global_tokens:8\n"
985
+ )
986
+ raise AssertionError("the zero-grad guard should have stopped the run before this")
987
+
988
+ monkeypatch.setattr(sft_train, "run_verl_training", fake_training)
989
+
990
+ with pytest.raises(RuntimeError, match=re.escape("grad_norm=0.0")):
991
+ sft_train.run_sft_train(spec)
992
+
993
+ # the watcher still gets stopped -- it just is not asked to prove completeness for a run that
994
+ # never finished. without this, stop() raises and the grad_norm error never reaches the caller.
995
+ assert stopped == [False]
996
+
997
+
801
998
  def test_overrides_enable_fused_linear_ce_for_long_context():
802
999
  # 32k contexts must not materialize [tokens, vocab] logits; the fused torch-backend
803
1000
  # linear-CE computes loss from hidden states in chunks (numerically exact CE).
@@ -1025,7 +1025,7 @@ wheels = [
1025
1025
 
1026
1026
  [[package]]
1027
1027
  name = "freesolo-flash"
1028
- version = "1.0.63"
1028
+ version = "1.0.65"
1029
1029
  source = { editable = "." }
1030
1030
 
1031
1031
  [package.optional-dependencies]