freesolo-flash-dev 1.0.63__tar.gz → 1.0.64__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.64}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/rl_train.py +21 -8
  3. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/sft_train.py +41 -1
  4. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/pyproject.toml +2 -2
  5. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_rl_train.py +54 -3
  6. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_sft_train.py +200 -3
  7. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/uv.lock +1 -1
  8. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.dockerignore +0 -0
  9. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.env.example +0 -0
  10. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  13. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  14. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/dependabot.yml +0 -0
  15. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/auto-rebake.yml +0 -0
  16. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/bake-kernel-cache.yml +0 -0
  17. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/ci.yml +0 -0
  18. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/main-source-guard.yml +0 -0
  19. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/notify-tests-repo.yml +0 -0
  20. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/publish-dev.yml +0 -0
  21. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/publish-image.yml +0 -0
  22. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/publish.yml +0 -0
  23. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/version-parity.yml +0 -0
  24. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.github/workflows/worker-image.yml +0 -0
  25. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/.gitignore +0 -0
  26. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/CODE_OF_CONDUCT.md +0 -0
  27. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/CONTRIBUTING.md +0 -0
  28. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/Dockerfile +0 -0
  29. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/Dockerfile.worker +0 -0
  30. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/LICENSE +0 -0
  31. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/NOTICE +0 -0
  32. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/README.md +0 -0
  33. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/SECURITY.md +0 -0
  34. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/build/kernel_cache/.gitignore +0 -0
  35. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/build/kernel_cache/.keep +0 -0
  36. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/Dockerfile.kernelcache +0 -0
  37. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/Dockerfile.kernelcache.relayer +0 -0
  38. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/bake_kernel_cache.py +0 -0
  39. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/bake_pod_entry.py +0 -0
  40. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/kernel_fingerprint.py +0 -0
  41. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/docker/make_rp_handler.py +0 -0
  42. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/__init__.py +0 -0
  43. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/_channel.py +0 -0
  44. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/_fileio.py +0 -0
  45. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/_logging.py +0 -0
  46. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/_update_check.py +0 -0
  47. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/adapter_artifacts.py +0 -0
  48. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/catalog.py +0 -0
  49. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/TRAINING.md +0 -0
  50. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/__init__.py +0 -0
  51. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/__main__.py +0 -0
  52. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/_tty.py +0 -0
  53. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/commands.py +0 -0
  54. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/env_eval.py +0 -0
  55. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/env_setup.py +0 -0
  56. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/env_test.py +0 -0
  57. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/envpush.py +0 -0
  58. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/render.py +0 -0
  59. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/traces.py +0 -0
  60. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cli/training_doc.py +0 -0
  61. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/client/__init__.py +0 -0
  62. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/client/config.py +0 -0
  63. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/client/http.py +0 -0
  64. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/client/runtime_secrets.py +0 -0
  65. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/client/specs.py +0 -0
  66. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/__init__.py +0 -0
  67. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/analytical.py +0 -0
  68. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/colocation.py +0 -0
  69. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/facts.py +0 -0
  70. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/spec.py +0 -0
  71. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/cost/types.py +0 -0
  72. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/diagnostics.py +0 -0
  73. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/__init__.py +0 -0
  74. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/accounting.py +0 -0
  75. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/multiturn_reward_scoring.py +0 -0
  76. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/recipe.py +0 -0
  77. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/reward_profile.py +0 -0
  78. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/steps.py +0 -0
  79. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/structured_outputs.py +0 -0
  80. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/vram.py +0 -0
  81. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/__init__.py +0 -0
  82. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/__main__.py +0 -0
  83. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/_pkg.py +0 -0
  84. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/adapter.py +0 -0
  85. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/backend_common.py +0 -0
  86. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/decoding.py +0 -0
  87. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/finalize.py +0 -0
  88. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/grpo.py +0 -0
  89. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/grpo_multiturn.py +0 -0
  90. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/grpo_plugin.py +0 -0
  91. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/heartbeat.py +0 -0
  92. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/hf.py +0 -0
  93. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/kernel_warmup.py +0 -0
  94. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/lora.py +0 -0
  95. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/multiturn_glue.py +0 -0
  96. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd.py +0 -0
  97. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd_gkd.py +0 -0
  98. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd_multiturn.py +0 -0
  99. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd_plugin.py +0 -0
  100. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd_structured.py +0 -0
  101. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/opd_train.py +0 -0
  102. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/packing.py +0 -0
  103. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/__init__.py +0 -0
  104. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/attn.py +0 -0
  105. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/diagnostics.py +0 -0
  106. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/lifecycle.py +0 -0
  107. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/liger.py +0 -0
  108. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/loraplus.py +0 -0
  109. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/perf/memory.py +0 -0
  110. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/rl.py +0 -0
  111. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/rng.py +0 -0
  112. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/rollout_samples.py +0 -0
  113. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/sft.py +0 -0
  114. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/teacher.py +0 -0
  115. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/tokenizer_align.py +0 -0
  116. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker/wandb_log.py +0 -0
  117. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/engine/worker_entrypoint.py +0 -0
  118. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/__init__.py +0 -0
  119. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/adapter.py +0 -0
  120. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/archive.py +0 -0
  121. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/archive_policy.py +0 -0
  122. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/base.py +0 -0
  123. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/evaluations.py +0 -0
  124. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/loader.py +0 -0
  125. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/pull.py +0 -0
  126. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/envs/registry.py +0 -0
  127. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/lora_rank.py +0 -0
  128. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/multimodal.py +0 -0
  129. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/opd_retry_contract.py +0 -0
  130. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/opd_validation.py +0 -0
  131. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/__init__.py +0 -0
  132. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_auth.py +0 -0
  133. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_deadline.py +0 -0
  134. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_hf_artifacts.py +0 -0
  135. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_hf_retry.py +0 -0
  136. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_http.py +0 -0
  137. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_instance.py +0 -0
  138. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_instance_bootstrap.py +0 -0
  139. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_instance_poll.py +0 -0
  140. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_instance_provider.py +0 -0
  141. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_poll.py +0 -0
  142. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/_worker.py +0 -0
  143. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/allocator.py +0 -0
  144. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/base.py +0 -0
  145. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/__init__.py +0 -0
  146. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/api.py +0 -0
  147. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/auth.py +0 -0
  148. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/gpus.py +0 -0
  149. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  150. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  151. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/preflight.py +0 -0
  152. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/lambdalabs/pricing.py +0 -0
  153. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/preflight.py +0 -0
  154. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/realized.py +0 -0
  155. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/__init__.py +0 -0
  156. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/api.py +0 -0
  157. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/auth.py +0 -0
  158. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/cost.py +0 -0
  159. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/gpus.py +0 -0
  160. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/jobs.py +0 -0
  161. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/keys.py +0 -0
  162. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/preflight.py +0 -0
  163. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/pricing.py +0 -0
  164. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/slots.py +0 -0
  165. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/train/__init__.py +0 -0
  166. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/runpod/train/endpoints.py +0 -0
  167. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/__init__.py +0 -0
  168. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/api.py +0 -0
  169. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/auth.py +0 -0
  170. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/jobs/__init__.py +0 -0
  171. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/jobs/builders.py +0 -0
  172. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/preflight.py +0 -0
  173. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/vast/pricing.py +0 -0
  174. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/providers/weight_cache_preload.py +0 -0
  175. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/py.typed +0 -0
  176. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/runner/__init__.py +0 -0
  177. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/runner/checkpoints.py +0 -0
  178. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/runner/deploy.py +0 -0
  179. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/runner/lifecycle.py +0 -0
  180. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/runner/verified_revisions.py +0 -0
  181. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/schema/__init__.py +0 -0
  182. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/schema/fields.py +0 -0
  183. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/__init__.py +0 -0
  184. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/deploy.py +0 -0
  185. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/export.py +0 -0
  186. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/preflight.py +0 -0
  187. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/pricing.py +0 -0
  188. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/serve/urls.py +0 -0
  189. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/__init__.py +0 -0
  190. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/__main__.py +0 -0
  191. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/_deps.py +0 -0
  192. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/_internal_client.py +0 -0
  193. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/_locks.py +0 -0
  194. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/_runtime.py +0 -0
  195. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/app.py +0 -0
  196. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/auth.py +0 -0
  197. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/billing.py +0 -0
  198. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/billing_retry.py +0 -0
  199. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/checkpoints.py +0 -0
  200. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/db.py +0 -0
  201. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/environment_registry.py +0 -0
  202. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/envs.py +0 -0
  203. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/projects.py +0 -0
  204. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/reconcile.py +0 -0
  205. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/repo_cleanup.py +0 -0
  206. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/routes/__init__.py +0 -0
  207. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/routes/envs.py +0 -0
  208. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/routes/meta.py +0 -0
  209. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/routes/runs.py +0 -0
  210. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/routes/serving.py +0 -0
  211. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/server/run_registry.py +0 -0
  212. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/spec.py +0 -0
  213. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/flash/thinking.py +0 -0
  214. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/infisical-entrypoint.sh +0 -0
  215. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/scripts/build_dev_dist.py +0 -0
  216. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/scripts/scrub_history.sh +0 -0
  217. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/__init__.py +0 -0
  218. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/_helpers/__init__.py +0 -0
  219. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/_helpers/runner.py +0 -0
  220. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/_helpers/specs.py +0 -0
  221. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/_helpers/vast.py +0 -0
  222. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/conftest.py +0 -0
  223. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/fixtures/math_eval.jsonl +0 -0
  224. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/fixtures/math_train.jsonl +0 -0
  225. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/live/__init__.py +0 -0
  226. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/live/conftest.py +0 -0
  227. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/live/test_lambda_live.py +0 -0
  228. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/live/test_runpod_live.py +0 -0
  229. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/live/test_vast_live.py +0 -0
  230. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_accounting_coverage.py +0 -0
  231. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_agent_flash_cli_contract.py +0 -0
  232. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_algorithms.py +0 -0
  233. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_allocator.py +0 -0
  234. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_async_uploads.py +0 -0
  235. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_backend_common.py +0 -0
  236. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_backend_jobspec_contract.py +0 -0
  237. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_base_model_provenance.py +0 -0
  238. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_billing_retry.py +0 -0
  239. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cancel_remote.py +0 -0
  240. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_catalog_consistency.py +0 -0
  241. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_charge_pricing.py +0 -0
  242. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_checkpoints.py +0 -0
  243. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_commands.py +0 -0
  244. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_commands_coverage.py +0 -0
  245. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_credential_shadowing.py +0 -0
  246. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_errors.py +0 -0
  247. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_estimate.py +0 -0
  248. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_help.py +0 -0
  249. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_main_coverage.py +0 -0
  250. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_managed.py +0 -0
  251. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_render_coverage.py +0 -0
  252. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_render_theme.py +0 -0
  253. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_traces.py +0 -0
  254. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cli_wandb_warning.py +0 -0
  255. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_client.py +0 -0
  256. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_client_server_integration.py +0 -0
  257. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_client_stream_reads.py +0 -0
  258. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_colocation.py +0 -0
  259. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_config_overrides.py +0 -0
  260. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_controlled_experiment_repairs.py +0 -0
  261. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_analytical.py +0 -0
  262. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_equation.py +0 -0
  263. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_estimate.py +0 -0
  264. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_hardware.py +0 -0
  265. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_models.py +0 -0
  266. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_cost_rewards.py +0 -0
  267. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_deploy_latency.py +0 -0
  268. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_dev_channel.py +0 -0
  269. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_disk_gb.py +0 -0
  270. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_endpoint_name.py +0 -0
  271. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_cache_evict.py +0 -0
  272. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_delete.py +0 -0
  273. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_download.py +0 -0
  274. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_download_api.py +0 -0
  275. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_evaluations.py +0 -0
  276. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_no_install.py +0 -0
  277. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_publish.py +0 -0
  278. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_pull.py +0 -0
  279. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_pull_coverage.py +0 -0
  280. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_pull_loader_coverage.py +0 -0
  281. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_pull_managed_control_plane.py +0 -0
  282. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_push.py +0 -0
  283. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_rate_limit_resolve.py +0 -0
  284. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_ref_pinning.py +0 -0
  285. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_env_test.py +0 -0
  286. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_envs_coverage.py +0 -0
  287. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_export.py +0 -0
  288. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_flash_mvp.py +0 -0
  289. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_flash_worker.py +0 -0
  290. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_flashinfer_cache_dirs.py +0 -0
  291. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_github_urlopen_retry.py +0 -0
  292. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_gpu_type_single_field.py +0 -0
  293. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_gpus.py +0 -0
  294. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_grpo_mask_aware.py +0 -0
  295. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_grpo_params.py +0 -0
  296. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_grpo_resident_sizing.py +0 -0
  297. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_health_liveness.py +0 -0
  298. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_hf_retry.py +0 -0
  299. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_idle_endpoint_reaper.py +0 -0
  300. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_instance_bootstrap_coverage.py +0 -0
  301. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_internal_client.py +0 -0
  302. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_jobs.py +0 -0
  303. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_kernel_cache.py +0 -0
  304. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_kernel_fingerprint.py +0 -0
  305. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_kv_util.py +0 -0
  306. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_lambda_api_coverage.py +0 -0
  307. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_lambda_runner.py +0 -0
  308. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_logging.py +0 -0
  309. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_login_perms.py +0 -0
  310. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_lora_rank_coverage.py +0 -0
  311. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_lora_rank_preflight.py +0 -0
  312. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_managed_config_fields.py +0 -0
  313. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_managed_hf_repo.py +0 -0
  314. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_metrics_schema_agent_contract.py +0 -0
  315. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_mig_guard.py +0 -0
  316. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_multi_gpu_gate.py +0 -0
  317. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_multimodal_input_grads.py +0 -0
  318. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_multimodal_training.py +0 -0
  319. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_named_reward_metrics.py +0 -0
  320. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_oom_escalate_gpu.py +0 -0
  321. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_opd.py +0 -0
  322. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_opd_resume_safety.py +0 -0
  323. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_opd_train.py +0 -0
  324. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_open_model_policy.py +0 -0
  325. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_orchestrator_flash.py +0 -0
  326. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_packing.py +0 -0
  327. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_packing_coverage.py +0 -0
  328. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_poll_helpers.py +0 -0
  329. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_preflight.py +0 -0
  330. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_projects.py +0 -0
  331. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_provider_preflight_coverage.py +0 -0
  332. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_provider_routing.py +0 -0
  333. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_provider_teardown_robustness.py +0 -0
  334. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_providers_symmetry.py +0 -0
  335. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_realized_cost.py +0 -0
  336. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_repo_cleanup.py +0 -0
  337. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_resolve_params_b.py +0 -0
  338. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_resume_on_retry.py +0 -0
  339. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_reward_profile.py +0 -0
  340. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_rollout_samples.py +0 -0
  341. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_runmgmt.py +0 -0
  342. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_runpod_api_delete.py +0 -0
  343. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_runpod_key_fingerprint.py +0 -0
  344. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_runpod_key_waterfall.py +0 -0
  345. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_runpod_slots.py +0 -0
  346. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serve.py +0 -0
  347. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serve_reasoning_content.py +0 -0
  348. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_api.py +0 -0
  349. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_app_coverage.py +0 -0
  350. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_auth_singleflight.py +0 -0
  351. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_billing.py +0 -0
  352. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_checkpoints_coverage.py +0 -0
  353. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_db.py +0 -0
  354. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_envs_coverage.py +0 -0
  355. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_server_locks_coverage.py +0 -0
  356. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serving_context_preflight.py +0 -0
  357. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serving_contract.py +0 -0
  358. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serving_pricing_coverage.py +0 -0
  359. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_serving_schema_coverage.py +0 -0
  360. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_sft_chunked_nll.py +0 -0
  361. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_sft_example_selection.py +0 -0
  362. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_sft_gc_off.py +0 -0
  363. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_sft_max_context.py +0 -0
  364. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_spec_and_validation.py +0 -0
  365. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_step_metrics.py +0 -0
  366. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_structured_outputs.py +0 -0
  367. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_thinking_config.py +0 -0
  368. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_train_context_preflight.py +0 -0
  369. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_training_controls.py +0 -0
  370. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_update_check.py +0 -0
  371. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vast_api.py +0 -0
  372. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vast_api_coverage.py +0 -0
  373. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vast_offers.py +0 -0
  374. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vast_provider_coverage.py +0 -0
  375. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vast_runner.py +0 -0
  376. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_verified_revisions.py +0 -0
  377. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_verifiers.py +0 -0
  378. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_version.py +0 -0
  379. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  380. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_vram_architecture.py +0 -0
  381. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_wandb_link.py +0 -0
  382. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_wandb_log_coverage.py +0 -0
  383. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_wandb_naming.py +0 -0
  384. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_warmstart_adapter_download.py +0 -0
  385. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_warmstart_cross_repo.py +0 -0
  386. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_warmstart_error_attribution.py +0 -0
  387. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_weight_cache.py +0 -0
  388. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_worker_dryrun.py +0 -0
  389. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_worker_hardexit.py +0 -0
  390. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_worker_image.py +0 -0
  391. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_worker_init_heartbeat.py +0 -0
  392. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/tests/test_worker_stack.py +0 -0
  393. {freesolo_flash_dev-1.0.63 → freesolo_flash_dev-1.0.64}/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.64
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
@@ -1229,14 +1229,20 @@ def render_reentrant_checkpointing_shim(reentrant: bool, *, multimodal: bool = F
1229
1229
  engines whose config may legitimately have checkpointing off, and enabling it there would turn
1230
1230
  on a feature verl chose to leave off.
1231
1231
 
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
1232
+ reentrant recompute drops the backward for a checkpointed block when none of that block's
1233
+ inputs require grad. that hits the LANGUAGE side on every lora run: lora freezes the
1234
+ embeddings, so the hidden states entering the first checkpointed decoder layer have
1235
+ ``requires_grad=False`` and the whole segment -- containing every lora parameter -- receives no
1236
+ gradient, while the run reports success and bills (GRAD-001). ``enable_input_require_grads()``
1237
+ is what prevents it, and it is unconditional here because every flash rl run is lora.
1238
+
1239
+ on a MULTIMODAL run the same hook additionally restores VISION input gradients. the vision
1240
+ tower's patch embeddings are the same failure in a place the language-side call does not
1241
+ reach: the pixels are frozen inputs, so without a forward hook marking the patch-embed output
1242
+ as requiring grad the visual modules silently receive nothing while the language side trains
1243
+ normally (``tests/test_multimodal_input_grads.py``). the retired trl path installed that hook
1244
+ via a trainer callback; verl has no callback surface, so it rides this shim instead -- and only
1245
+ here, because non-reentrant recompute does not have the behaviour that makes it necessary
1240
1246
  (codex[bot]).
1241
1247
  """
1242
1248
  if not reentrant:
@@ -1290,6 +1296,13 @@ def _flash_reentrant_build_module(self):
1290
1296
  # only when verl actually enabled checkpointing: calling gradient_checkpointing_enable on a
1291
1297
  # model verl deliberately left uncheckpointed would turn it ON and change the memory profile.
1292
1298
  if getattr(self.model_config, "enable_gradient_checkpointing", False):
1299
+ # the LANGUAGE-side counterpart of the vision hook below, and required for the same
1300
+ # reason (GRAD-001): lora freezes the embeddings, so nothing entering the first
1301
+ # checkpointed decoder layer requires grad and reentrant recompute drops the backward
1302
+ # for the whole segment -- where every lora parameter lives. the vision hook only ever
1303
+ # covered the patch embeddings on multimodal runs; text-only runs had no hook at all and
1304
+ # trained nothing while reporting success.
1305
+ module.enable_input_require_grads()
1293
1306
  module.gradient_checkpointing_enable(gradient_checkpointing_kwargs={{"use_reentrant": True}}){vision_hook}
1294
1307
  print("[rl-verl] reentrant gradient checkpointing is active", flush=True)
1295
1308
  return module
@@ -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.64"
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.64"
127
127
 
128
128
  [tool.pytest.ini_options]
129
129
  pythonpath = ["."]
@@ -1699,6 +1699,48 @@ def test_the_reentrant_shim_installs_vision_input_grads_only_for_multimodal_runs
1699
1699
  assert rl_train.render_reentrant_checkpointing_shim(False, multimodal=True) == ""
1700
1700
 
1701
1701
 
1702
+ def test_the_reentrant_shim_enables_language_side_input_grads_before_checkpointing():
1703
+ """GRAD-001: every rl run is lora, so lora freezes the embeddings and nothing entering the
1704
+ first checkpointed decoder layer requires grad. reentrant recompute then drops the backward
1705
+ for the whole segment -- where every lora parameter lives -- and the run reports success while
1706
+ training nothing. the vision hook only ever covered the patch embeddings on multimodal runs;
1707
+ text-only runs had no hook at all."""
1708
+ calls = []
1709
+
1710
+ class FakeModule:
1711
+ def enable_input_require_grads(self):
1712
+ calls.append("require_grads")
1713
+
1714
+ def gradient_checkpointing_enable(self, **kwargs):
1715
+ calls.append(("gc_enable", kwargs))
1716
+
1717
+ class FakeEngine:
1718
+ def __init__(self, checkpointing):
1719
+ self.model_config = SimpleNamespace(enable_gradient_checkpointing=checkpointing)
1720
+
1721
+ def _build_module(self):
1722
+ return FakeModule()
1723
+
1724
+ source = rl_train.render_reentrant_checkpointing_shim(True)
1725
+ start = source.index("def _flash_reentrant_build_module")
1726
+ end = source.index("_FlashReentrantEngine._build_module = _flash_reentrant_build_module")
1727
+ namespace = {"_flash_reentrant_original_build_module": FakeEngine._build_module}
1728
+ exec(compile(source[start:end], "sitecustomize.py", "exec"), namespace)
1729
+ build = namespace["_flash_reentrant_build_module"]
1730
+
1731
+ build(FakeEngine(checkpointing=True))
1732
+ # order matters: enabling checkpointing first captures the graph before any input requires
1733
+ # grad, so asserting mere presence would pass on a broken shim.
1734
+ assert calls[0] == "require_grads"
1735
+ assert calls[1] == ("gc_enable", {"gradient_checkpointing_kwargs": {"use_reentrant": True}})
1736
+
1737
+ # the guard is load-bearing: when verl left checkpointing OFF, touching the module at all
1738
+ # would turn on a feature verl deliberately declined and change the memory profile.
1739
+ calls.clear()
1740
+ build(FakeEngine(checkpointing=False))
1741
+ assert calls == []
1742
+
1743
+
1702
1744
  @contextlib.contextmanager
1703
1745
  def _vision_hook_installer(torch_module):
1704
1746
  """yield the rendered vision helper with a stand-in torch visible to it.
@@ -1831,6 +1873,10 @@ def test_the_reentrant_shim_flips_the_flag_and_leaves_uncheckpointed_models_alon
1831
1873
  class _Module:
1832
1874
  def __init__(self):
1833
1875
  self.kwargs = None
1876
+ self.input_grads = False
1877
+
1878
+ def enable_input_require_grads(self):
1879
+ self.input_grads = True
1834
1880
 
1835
1881
  def gradient_checkpointing_enable(self, gradient_checkpointing_kwargs=None):
1836
1882
  self.kwargs = gradient_checkpointing_kwargs
@@ -1857,12 +1903,17 @@ def test_the_reentrant_shim_flips_the_flag_and_leaves_uncheckpointed_models_alon
1857
1903
  sys.modules.setdefault(name, types.ModuleType(name))
1858
1904
  sys.modules[module_stub.__name__] = module_stub
1859
1905
  exec(rl_train.render_reentrant_checkpointing_shim(True), {})
1860
- # checkpointing on -> the flag is put back to reentrant
1906
+ # checkpointing on -> the flag is put back to reentrant, and the lora-frozen embeddings
1907
+ # get input grads so the checkpointed segment actually produces a backward (GRAD-001)
1861
1908
  engine = _Engine(True)
1862
- assert engine._build_module().kwargs == {"use_reentrant": True}
1909
+ built = engine._build_module()
1910
+ assert built.kwargs == {"use_reentrant": True}
1911
+ assert built.input_grads is True
1863
1912
  # checkpointing off -> untouched, so the shim cannot silently raise the memory profile
1864
1913
  off = _Engine(False)
1865
- assert off._build_module().kwargs is None
1914
+ off_built = off._build_module()
1915
+ assert off_built.kwargs is None
1916
+ assert off_built.input_grads is False
1866
1917
  finally:
1867
1918
  for name, value in saved.items():
1868
1919
  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.64"
1029
1029
  source = { editable = "." }
1030
1030
 
1031
1031
  [package.optional-dependencies]