freesolo-flash-dev 1.0.100__tar.gz → 1.1.2__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 (417) hide show
  1. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft_train.py +51 -0
  3. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/__init__.py +7 -1
  4. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/deploy.py +3 -0
  5. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/lifecycle.py +19 -0
  6. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/pyproject.toml +3 -3
  7. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_resume_safety.py +4 -0
  8. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_routing.py +51 -0
  9. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_train.py +73 -3
  10. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/uv.lock +1 -1
  11. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.dockerignore +0 -0
  12. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.env.example +0 -0
  13. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  14. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  15. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  16. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  17. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/dependabot.yml +0 -0
  18. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/auto-rebake.yml +0 -0
  19. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/bake-kernel-cache.yml +0 -0
  20. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/ci.yml +0 -0
  21. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/main-source-guard.yml +0 -0
  22. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/notify-tests-repo.yml +0 -0
  23. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish-dev.yml +0 -0
  24. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish-image.yml +0 -0
  25. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish.yml +0 -0
  26. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/version-parity.yml +0 -0
  27. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/worker-image.yml +0 -0
  28. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.gitignore +0 -0
  29. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/CODE_OF_CONDUCT.md +0 -0
  30. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/CONTRIBUTING.md +0 -0
  31. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/Dockerfile +0 -0
  32. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/Dockerfile.worker +0 -0
  33. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/LICENSE +0 -0
  34. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/NOTICE +0 -0
  35. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/README.md +0 -0
  36. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/SECURITY.md +0 -0
  37. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/SELF_HOSTING.md +0 -0
  38. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/build/kernel_cache/.gitignore +0 -0
  39. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/build/kernel_cache/.keep +0 -0
  40. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/Dockerfile +0 -0
  41. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/README.md +0 -0
  42. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/entrypoint.sh +0 -0
  43. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/Dockerfile.kernelcache +0 -0
  44. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/Dockerfile.kernelcache.relayer +0 -0
  45. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/bake_kernel_cache.py +0 -0
  46. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/bake_pod_entry.py +0 -0
  47. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/kernel_fingerprint.py +0 -0
  48. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/make_rp_handler.py +0 -0
  49. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/__init__.py +0 -0
  50. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_channel.py +0 -0
  51. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_fileio.py +0 -0
  52. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_logging.py +0 -0
  53. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_paths.py +0 -0
  54. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/adapter_artifacts.py +0 -0
  55. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/catalog.py +0 -0
  56. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/TRAINING.md +0 -0
  57. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/__init__.py +0 -0
  58. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/__main__.py +0 -0
  59. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/_tty.py +0 -0
  60. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/commands.py +0 -0
  61. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_eval.py +0 -0
  62. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_setup.py +0 -0
  63. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_test.py +0 -0
  64. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/envpush.py +0 -0
  65. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/render.py +0 -0
  66. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/traces.py +0 -0
  67. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/training_doc.py +0 -0
  68. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/__init__.py +0 -0
  69. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/config.py +0 -0
  70. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/http.py +0 -0
  71. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/runtime_secrets.py +0 -0
  72. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/specs.py +0 -0
  73. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/__init__.py +0 -0
  74. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/analytical.py +0 -0
  75. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/facts.py +0 -0
  76. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/spec.py +0 -0
  77. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/types.py +0 -0
  78. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/diagnostics.py +0 -0
  79. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/__init__.py +0 -0
  80. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/accounting.py +0 -0
  81. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/multiturn_reward_scoring.py +0 -0
  82. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/recipe.py +0 -0
  83. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/reward_profile.py +0 -0
  84. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/rollout_profile.py +0 -0
  85. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/sft_workload.py +0 -0
  86. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/steps.py +0 -0
  87. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/structured_outputs.py +0 -0
  88. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/vram.py +0 -0
  89. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/__init__.py +0 -0
  90. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/__main__.py +0 -0
  91. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/_pkg.py +0 -0
  92. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/adapter.py +0 -0
  93. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/backend_common.py +0 -0
  94. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/decoding.py +0 -0
  95. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/finalize.py +0 -0
  96. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo.py +0 -0
  97. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo_multiturn.py +0 -0
  98. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo_plugin.py +0 -0
  99. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/heartbeat.py +0 -0
  100. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/hf.py +0 -0
  101. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/kernel_warmup.py +0 -0
  102. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/lora.py +0 -0
  103. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/multiturn_glue.py +0 -0
  104. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd.py +0 -0
  105. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_gkd.py +0 -0
  106. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_multiturn.py +0 -0
  107. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_plugin.py +0 -0
  108. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_structured.py +0 -0
  109. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_train.py +0 -0
  110. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/packing.py +0 -0
  111. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/__init__.py +0 -0
  112. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/attn.py +0 -0
  113. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/diagnostics.py +0 -0
  114. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/lifecycle.py +0 -0
  115. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/liger.py +0 -0
  116. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/loraplus.py +0 -0
  117. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/memory.py +0 -0
  118. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rl.py +0 -0
  119. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rl_train.py +0 -0
  120. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rng.py +0 -0
  121. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rollout_samples.py +0 -0
  122. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft.py +0 -0
  123. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft_profile.py +0 -0
  124. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/teacher.py +0 -0
  125. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/teacher_encoding.py +0 -0
  126. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/tokenizer_align.py +0 -0
  127. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/wandb_log.py +0 -0
  128. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker_entrypoint.py +0 -0
  129. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/__init__.py +0 -0
  130. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/adapter.py +0 -0
  131. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/archive.py +0 -0
  132. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/archive_policy.py +0 -0
  133. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/base.py +0 -0
  134. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/evaluations.py +0 -0
  135. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/loader.py +0 -0
  136. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/pull.py +0 -0
  137. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/registry.py +0 -0
  138. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/lora_rank.py +0 -0
  139. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/multimodal.py +0 -0
  140. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_limits.py +0 -0
  141. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_retry_contract.py +0 -0
  142. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_validation.py +0 -0
  143. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/__init__.py +0 -0
  144. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_auth.py +0 -0
  145. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_deadline.py +0 -0
  146. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_hf_artifacts.py +0 -0
  147. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_hf_retry.py +0 -0
  148. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_http.py +0 -0
  149. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance.py +0 -0
  150. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_bootstrap.py +0 -0
  151. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_poll.py +0 -0
  152. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_provider.py +0 -0
  153. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_poll.py +0 -0
  154. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_worker.py +0 -0
  155. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/allocator.py +0 -0
  156. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/base.py +0 -0
  157. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/__init__.py +0 -0
  158. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/api.py +0 -0
  159. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/auth.py +0 -0
  160. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/gpus.py +0 -0
  161. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  162. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  163. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/preflight.py +0 -0
  164. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/pricing.py +0 -0
  165. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/preflight.py +0 -0
  166. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/realized.py +0 -0
  167. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/__init__.py +0 -0
  168. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/api.py +0 -0
  169. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/auth.py +0 -0
  170. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/cost.py +0 -0
  171. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/gpus.py +0 -0
  172. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/jobs.py +0 -0
  173. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/keys.py +0 -0
  174. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/preflight.py +0 -0
  175. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/pricing.py +0 -0
  176. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/slots.py +0 -0
  177. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/train/__init__.py +0 -0
  178. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/train/endpoints.py +0 -0
  179. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/__init__.py +0 -0
  180. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/api.py +0 -0
  181. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/auth.py +0 -0
  182. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/jobs/__init__.py +0 -0
  183. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/jobs/builders.py +0 -0
  184. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/preflight.py +0 -0
  185. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/pricing.py +0 -0
  186. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/weight_cache_preload.py +0 -0
  187. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/py.typed +0 -0
  188. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/checkpoints.py +0 -0
  189. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/verified_revisions.py +0 -0
  190. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/schema/__init__.py +0 -0
  191. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/schema/fields.py +0 -0
  192. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/__init__.py +0 -0
  193. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/deploy.py +0 -0
  194. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/export.py +0 -0
  195. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/preflight.py +0 -0
  196. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/pricing.py +0 -0
  197. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/urls.py +0 -0
  198. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/__init__.py +0 -0
  199. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/__main__.py +0 -0
  200. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_deps.py +0 -0
  201. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_internal_client.py +0 -0
  202. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_locks.py +0 -0
  203. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_runtime.py +0 -0
  204. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/app.py +0 -0
  205. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/auth.py +0 -0
  206. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/billing.py +0 -0
  207. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/billing_retry.py +0 -0
  208. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/checkpoints.py +0 -0
  209. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/db.py +0 -0
  210. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/environment_registry.py +0 -0
  211. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/envs.py +0 -0
  212. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/projects.py +0 -0
  213. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/reconcile.py +0 -0
  214. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/repo_cleanup.py +0 -0
  215. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/__init__.py +0 -0
  216. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/envs.py +0 -0
  217. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/meta.py +0 -0
  218. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/runs.py +0 -0
  219. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/serving.py +0 -0
  220. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/teacher.py +0 -0
  221. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/run_registry.py +0 -0
  222. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/teacher_broker.py +0 -0
  223. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/spec.py +0 -0
  224. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/thinking.py +0 -0
  225. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/workload_profile.py +0 -0
  226. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/scripts/build_dev_dist.py +0 -0
  227. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/scripts/scrub_history.sh +0 -0
  228. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/__init__.py +0 -0
  229. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/__init__.py +0 -0
  230. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/profile.py +0 -0
  231. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/runner.py +0 -0
  232. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/specs.py +0 -0
  233. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/vast.py +0 -0
  234. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/conftest.py +0 -0
  235. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/fixtures/math_eval.jsonl +0 -0
  236. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/fixtures/math_train.jsonl +0 -0
  237. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/__init__.py +0 -0
  238. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/conftest.py +0 -0
  239. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_lambda_live.py +0 -0
  240. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_runpod_live.py +0 -0
  241. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_vast_live.py +0 -0
  242. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_accounting_coverage.py +0 -0
  243. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_agent_flash_cli_contract.py +0 -0
  244. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_algorithms.py +0 -0
  245. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_allocator.py +0 -0
  246. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_async_uploads.py +0 -0
  247. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_backend_common.py +0 -0
  248. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_backend_jobspec_contract.py +0 -0
  249. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_base_model_provenance.py +0 -0
  250. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_billing_retry.py +0 -0
  251. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cancel_remote.py +0 -0
  252. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_catalog_consistency.py +0 -0
  253. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_charge_pricing.py +0 -0
  254. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_checkpoints.py +0 -0
  255. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_commands.py +0 -0
  256. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_commands_coverage.py +0 -0
  257. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_credential_shadowing.py +0 -0
  258. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_errors.py +0 -0
  259. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_estimate.py +0 -0
  260. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_gpus_flag.py +0 -0
  261. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_help.py +0 -0
  262. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_main_coverage.py +0 -0
  263. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_managed.py +0 -0
  264. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_render_coverage.py +0 -0
  265. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_render_theme.py +0 -0
  266. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_traces.py +0 -0
  267. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_wandb_warning.py +0 -0
  268. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client.py +0 -0
  269. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client_server_integration.py +0 -0
  270. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client_stream_reads.py +0 -0
  271. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_config_env_vars.py +0 -0
  272. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_config_overrides.py +0 -0
  273. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_controlled_experiment_repairs.py +0 -0
  274. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_analytical.py +0 -0
  275. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_equation.py +0 -0
  276. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_estimate.py +0 -0
  277. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_hardware.py +0 -0
  278. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_models.py +0 -0
  279. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_rewards.py +0 -0
  280. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_deploy_latency.py +0 -0
  281. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_dev_channel.py +0 -0
  282. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_disk_gb.py +0 -0
  283. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_endpoint_name.py +0 -0
  284. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_cache_evict.py +0 -0
  285. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_delete.py +0 -0
  286. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_download.py +0 -0
  287. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_download_api.py +0 -0
  288. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_evaluations.py +0 -0
  289. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_no_install.py +0 -0
  290. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_publish.py +0 -0
  291. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull.py +0 -0
  292. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_coverage.py +0 -0
  293. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_loader_coverage.py +0 -0
  294. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_managed_control_plane.py +0 -0
  295. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_push.py +0 -0
  296. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_rate_limit_resolve.py +0 -0
  297. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_ref_pinning.py +0 -0
  298. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_test.py +0 -0
  299. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_envs_coverage.py +0 -0
  300. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_export.py +0 -0
  301. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flash_mvp.py +0 -0
  302. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flash_worker.py +0 -0
  303. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flashinfer_cache_dirs.py +0 -0
  304. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_github_urlopen_retry.py +0 -0
  305. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_gpu_type_single_field.py +0 -0
  306. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_gpus.py +0 -0
  307. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_params.py +0 -0
  308. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_resident_sizing.py +0 -0
  309. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_reward_batching.py +0 -0
  310. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_health_liveness.py +0 -0
  311. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_hf_retry.py +0 -0
  312. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_idle_endpoint_reaper.py +0 -0
  313. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_infisical_overlay.py +0 -0
  314. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_instance_bootstrap_coverage.py +0 -0
  315. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_internal_client.py +0 -0
  316. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_jobs.py +0 -0
  317. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kernel_cache.py +0 -0
  318. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kernel_fingerprint.py +0 -0
  319. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kv_util.py +0 -0
  320. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lambda_api_coverage.py +0 -0
  321. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lambda_runner.py +0 -0
  322. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_logging.py +0 -0
  323. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_login_perms.py +0 -0
  324. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lora_rank_coverage.py +0 -0
  325. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lora_rank_preflight.py +0 -0
  326. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_managed_config_fields.py +0 -0
  327. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_managed_hf_repo.py +0 -0
  328. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_metrics_schema_agent_contract.py +0 -0
  329. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_mig_guard.py +0 -0
  330. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multi_gpu_gate.py +0 -0
  331. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multimodal_input_grads.py +0 -0
  332. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multimodal_training.py +0 -0
  333. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_named_reward_metrics.py +0 -0
  334. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_oom_escalate_gpu.py +0 -0
  335. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd.py +0 -0
  336. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_limits.py +0 -0
  337. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_train.py +0 -0
  338. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_open_model_policy.py +0 -0
  339. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_orchestrator_flash.py +0 -0
  340. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_packing.py +0 -0
  341. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_packing_coverage.py +0 -0
  342. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_parasail_teacher.py +0 -0
  343. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_poll_helpers.py +0 -0
  344. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_preflight.py +0 -0
  345. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_projects.py +0 -0
  346. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_preflight_coverage.py +0 -0
  347. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_teardown_robustness.py +0 -0
  348. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_providers_symmetry.py +0 -0
  349. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_realized_cost.py +0 -0
  350. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_repo_cleanup.py +0 -0
  351. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_resolve_params_b.py +0 -0
  352. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_resume_on_retry.py +0 -0
  353. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_reward_profile.py +0 -0
  354. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rl_train.py +0 -0
  355. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_profile_quote.py +0 -0
  356. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_profile_sampling.py +0 -0
  357. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_samples.py +0 -0
  358. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_workload_profile.py +0 -0
  359. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runmgmt.py +0 -0
  360. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_api_delete.py +0 -0
  361. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_key_fingerprint.py +0 -0
  362. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_key_waterfall.py +0 -0
  363. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_slots.py +0 -0
  364. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serve.py +0 -0
  365. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serve_reasoning_content.py +0 -0
  366. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_api.py +0 -0
  367. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_app_coverage.py +0 -0
  368. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_auth_singleflight.py +0 -0
  369. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_billing.py +0 -0
  370. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_checkpoints_coverage.py +0 -0
  371. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_db.py +0 -0
  372. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_envs_coverage.py +0 -0
  373. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_locks_coverage.py +0 -0
  374. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_standalone.py +0 -0
  375. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_context_preflight.py +0 -0
  376. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_contract.py +0 -0
  377. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_pricing_coverage.py +0 -0
  378. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_schema_coverage.py +0 -0
  379. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_chunked_nll.py +0 -0
  380. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_example_selection.py +0 -0
  381. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_gc_off.py +0 -0
  382. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_max_context.py +0 -0
  383. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_profile_worker.py +0 -0
  384. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_workload.py +0 -0
  385. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_spec_and_validation.py +0 -0
  386. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_step_floor.py +0 -0
  387. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_step_metrics.py +0 -0
  388. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_structured_outputs.py +0 -0
  389. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_teacher_broker.py +0 -0
  390. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_thinking_config.py +0 -0
  391. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_train_context_preflight.py +0 -0
  392. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_training_controls.py +0 -0
  393. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_api.py +0 -0
  394. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_api_coverage.py +0 -0
  395. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_offers.py +0 -0
  396. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_provider_coverage.py +0 -0
  397. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_runner.py +0 -0
  398. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_verified_revisions.py +0 -0
  399. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_verifiers.py +0 -0
  400. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_version.py +0 -0
  401. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  402. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vram_architecture.py +0 -0
  403. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_link.py +0 -0
  404. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_log_coverage.py +0 -0
  405. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_naming.py +0 -0
  406. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_adapter_download.py +0 -0
  407. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_cross_repo.py +0 -0
  408. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_error_attribution.py +0 -0
  409. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_weight_cache.py +0 -0
  410. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_dryrun.py +0 -0
  411. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_hardexit.py +0 -0
  412. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_image.py +0 -0
  413. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_init_heartbeat.py +0 -0
  414. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_stack.py +0 -0
  415. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_thinking.py +0 -0
  416. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_workload_profile.py +0 -0
  417. {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_workload_profile_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 1.0.100
3
+ Version: 1.1.2
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
@@ -549,6 +549,9 @@ class _VerlCheckpointWatcher:
549
549
  ) -> None:
550
550
  self.local_dir = local_dir
551
551
  self.export_root = export_root
552
+ # sibling of export_root so it shares its filesystem: _staged_source hardlinks into it, and
553
+ # os.link cannot cross a mount. holds one step at a time and is cleaned up after each publish.
554
+ self.staging_root = os.path.join(export_root, "_staging")
552
555
  self.python_bin = python_bin
553
556
  self.model_id = model_id
554
557
  self.model_revision = model_revision
@@ -605,10 +608,58 @@ class _VerlCheckpointWatcher:
605
608
  def _should_publish(self, step: int) -> bool:
606
609
  return not self.required_steps or step in self.required_steps
607
610
 
611
+ def _staged_source(self, step: int, checkpoint_dir: str) -> str:
612
+ """Pin a completed checkpoint against verl's retention before the slow work reads it.
613
+
614
+ This watcher publishes on its own thread, so the merge and the multi-GB upload below both
615
+ overlap training. verl owns `local_dir` and prunes it from the CHILD process: under
616
+ `trainer.max_ckpt_to_keep=1`, `register_checkpoint` calls `shutil.rmtree` on global_step_N as
617
+ soon as N+1 finishes saving. Nothing the parent sets can pin a directory across that, so a
618
+ required save still exporting or uploading when the next save lands reads a tree being
619
+ deleted underneath it, and an otherwise successful paid run fails its required save. Close
620
+ `save_at_steps` (say `[2, 3]`) make that likely rather than theoretical.
621
+
622
+ HARDLINKS, not a copy and not a rename:
623
+ - a copy of tens of GB is itself slow enough to lose the same race it is meant to close.
624
+ - a rename would win the race but break in-pod resume: verl resolves
625
+ `latest_checkpointed_iteration.txt` to `global_step_N` (`find_latest_ckpt_path`), so moving
626
+ the directory leaves the tracker pointing at nothing if the pod is preempted afterwards.
627
+
628
+ Linking is O(1) per file and leaves the original in place, so verl's retention and resume see
629
+ exactly what they saw before while the link keeps the DATA alive for as long as this watcher
630
+ needs it. A pruned original just drops its own reference; the inode survives.
631
+
632
+ Best-effort by design: any failure returns the original path, which is the previous
633
+ (race-exposed) behaviour. Degrading beats failing a run that would otherwise complete.
634
+ """
635
+ staged = os.path.join(self.staging_root, f"global_step_{step}")
636
+ try:
637
+ shutil.rmtree(staged, ignore_errors=True)
638
+ shutil.copytree(checkpoint_dir, staged, copy_function=os.link)
639
+ except OSError as error:
640
+ print(
641
+ f"[ckpt] step {step} could not be staged out of verl's retention tree "
642
+ f"({error}); publishing from verl's copy",
643
+ flush=True,
644
+ )
645
+ shutil.rmtree(staged, ignore_errors=True)
646
+ return checkpoint_dir
647
+ return staged
648
+
608
649
  def _publish(self, step: int, checkpoint_dir: str) -> None:
609
650
  if not self._should_publish(step):
610
651
  self.processed_steps.add(step)
611
652
  return
653
+ staged_dir = self._staged_source(step, checkpoint_dir)
654
+ try:
655
+ self._publish_from(step, staged_dir)
656
+ finally:
657
+ if staged_dir != checkpoint_dir:
658
+ # the links have served their purpose once the upload returns; the originals and the
659
+ # exported adapter are untouched by this.
660
+ shutil.rmtree(staged_dir, ignore_errors=True)
661
+
662
+ def _publish_from(self, step: int, checkpoint_dir: str) -> None:
612
663
  actor_dir = resolve_checkpoint_actor_dir(checkpoint_dir)
613
664
  adapter_dir = os.path.join(self.export_root, f"step-{step}")
614
665
  _export_checkpoint_adapter(
@@ -2080,18 +2080,24 @@ def _persist_metrics(spec: JobSpec, metrics: dict) -> float:
2080
2080
  # case _gpu_rate prices off whichever configured provider offers the class.
2081
2081
  provider = str(metrics.get("allocated_provider") or "")
2082
2082
  rate = _gpu_rate(gpu_type, provider)
2083
+ # `hourly_rate` is per CARD, so a sharded run costs the wall times the rate times the number of
2084
+ # cards it actually occupied. `allocated_gpu_count` is worker/lifecycle-stamped for the same
2085
+ # reason `allocated_gpu` is: the spec's gpu.count is only a ceiling and allocation may pick
2086
+ # fewer. Absent on records predating the stamp, where one card is the correct reading.
2087
+ gpu_count = max(1, int(metrics.get("allocated_gpu_count") or 1))
2083
2088
  cost = metrics.get("cost_usd")
2084
2089
  if cost:
2085
2090
  cost = float(cost or 0.0)
2086
2091
  else:
2087
2092
  wall = float(metrics.get("wall_seconds") or 0.0)
2088
- cost = wall / 3600.0 * rate
2093
+ cost = wall / 3600.0 * rate * gpu_count
2089
2094
  metrics = {**metrics, "cost_usd": cost}
2090
2095
  metrics.setdefault("notes", {})
2091
2096
  if isinstance(metrics["notes"], dict):
2092
2097
  metrics["notes"]["provider"] = provider or "unknown"
2093
2098
  metrics["notes"]["gpu_rate_usd_hr"] = rate
2094
2099
  metrics["notes"]["gpu"] = gpu_type
2100
+ metrics["notes"]["gpu_count"] = gpu_count
2095
2101
  with open(os.path.join(dest, "metrics.json"), "w") as f:
2096
2102
  json.dump(metrics, f, indent=2)
2097
2103
  with contextlib.suppress(Exception):
@@ -1110,6 +1110,7 @@ def attach_run(run_id: str, log_stream=None) -> RunStatus:
1110
1110
  raise ValueError("persisted attempt identity is missing or invalid")
1111
1111
  next_attempt = recovered_attempt + 1
1112
1112
  allocated_gpu = remote.pop("allocated_gpu", None)
1113
+ allocated_gpu_count = remote.pop("allocated_gpu_count", None)
1113
1114
  handle = JobHandle.from_dict(remote)
1114
1115
  try:
1115
1116
  poll_spec = _spec_with_remaining_wall(worker_spec, require_provider_minimum=False)
@@ -1256,6 +1257,8 @@ def attach_run(run_id: str, log_stream=None) -> RunStatus:
1256
1257
  return status_for_return()
1257
1258
  if allocated_gpu and isinstance(res.metrics, dict):
1258
1259
  res.metrics.setdefault("allocated_gpu", allocated_gpu)
1260
+ if allocated_gpu_count and isinstance(res.metrics, dict):
1261
+ res.metrics.setdefault("allocated_gpu_count", int(allocated_gpu_count))
1259
1262
  if not _adopt_completed_attempt(
1260
1263
  run_id,
1261
1264
  worker_spec,
@@ -316,6 +316,9 @@ def _runpod_completed_metrics(handle, *, deadline_at: float | None = None) -> di
316
316
  allocated_gpu = original.get("allocated_gpu")
317
317
  if allocated_gpu:
318
318
  metrics.setdefault("allocated_gpu", allocated_gpu)
319
+ allocated_count = original.get("allocated_gpu_count")
320
+ if allocated_count:
321
+ metrics.setdefault("allocated_gpu_count", int(allocated_count))
319
322
  return metrics
320
323
  except _CompletedAttemptPending:
321
324
  raise
@@ -684,6 +687,11 @@ def _submit_seed_supervised(
684
687
  **canonical_handle,
685
688
  "seed": int(seed),
686
689
  "allocated_gpu": current_gpu.get("name"),
690
+ # carried beside the gpu name for the same reason and by the same route: the
691
+ # canonical provider handle drops unknown keys, so a recovering process can only
692
+ # learn the shape from what was persisted here. without the count a run adopted
693
+ # after a control-plane restart prices its wall as a single card.
694
+ "allocated_gpu_count": current_gpu.get("count"),
687
695
  "on_last_gpu": bool(current_on_last_gpu["value"]),
688
696
  "code_prefix": code_prefix,
689
697
  }
@@ -742,6 +750,11 @@ def _submit_seed_supervised(
742
750
  metrics.setdefault("allocated_gpu", current_gpu["name"])
743
751
  if current_gpu.get("provider"):
744
752
  metrics.setdefault("allocated_provider", current_gpu["provider"])
753
+ # the runpod serverless route returns here rather than through the `res.ok` stamp below, so
754
+ # the card count has to be recorded on both or a sharded serverless run is still priced as
755
+ # one card. same source either way: the candidate allocation actually chose.
756
+ if current_gpu.get("count"):
757
+ metrics.setdefault("allocated_gpu_count", int(current_gpu["count"]))
745
758
  return metrics
746
759
 
747
760
  max_retries = int(spec.gpu.max_retries)
@@ -984,6 +997,7 @@ def _submit_seed_supervised(
984
997
  raise
985
998
  current_gpu["name"] = chosen.gpu
986
999
  current_gpu["provider"] = chosen.provider
1000
+ current_gpu["count"] = int(getattr(chosen, "gpu_count", 1) or 1)
987
1001
  current_attempt["value"] = attempt
988
1002
  retry_delay = 0
989
1003
  submission_lock = _deploy_lock(spec.run_id)
@@ -1113,6 +1127,11 @@ def _submit_seed_supervised(
1113
1127
  # the provider that actually billed this run, so cost attribution prices the
1114
1128
  # class on ITS substrate rather than assuming RunPod's table.
1115
1129
  res.metrics.setdefault("allocated_provider", chosen.provider)
1130
+ # and how many cards of it. `hourly_rate(gpu_type)` is per CARD, so a fallback that
1131
+ # prices the wall once records a 2x/4x run at half or a quarter of its real spend.
1132
+ # the spec's own gpu.count cannot stand in: it is a ceiling, and allocation
1133
+ # routinely picks fewer (see TRAINING.md, "a ceiling, not an exact count").
1134
+ res.metrics.setdefault("allocated_gpu_count", int(getattr(chosen, "gpu_count", 1)))
1116
1135
  return res.metrics
1117
1136
  # cancel wins over any retry-shaped failure.
1118
1137
  try:
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "1.0.100"
4
- description = "Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI"
3
+ version = "1.1.2"
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).
7
7
  requires-python = ">=3.11,<3.13"
@@ -121,7 +121,7 @@ artifacts = ["flash/py.typed"]
121
121
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
122
122
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
123
123
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
124
- version = "1.0.100"
124
+ version = "1.1.2"
125
125
 
126
126
  [tool.pytest.ini_options]
127
127
  pythonpath = ["."]
@@ -656,6 +656,8 @@ def test_opd_automatic_retry_after_teardown_requires_all_markers_absent(monkeypa
656
656
  # stamped alongside the gpu so cost attribution prices the class on the substrate
657
657
  # that actually billed it.
658
658
  "allocated_provider": "runpod",
659
+ # and the card count, since the hourly rate is per card.
660
+ "allocated_gpu_count": 1,
659
661
  }
660
662
  assert provider.attempts == [0, 1]
661
663
  retry_submit = provider.events.index(("submit", 1))
@@ -744,6 +746,8 @@ def test_opd_retry_passes_gate_revision_and_overwrites_spoofed_value(monkeypatch
744
746
  # stamped alongside the gpu so cost attribution prices the class on the substrate
745
747
  # that actually billed it.
746
748
  "allocated_provider": "runpod",
749
+ # and the card count, since the hourly rate is per card.
750
+ "allocated_gpu_count": 1,
747
751
  }
748
752
  broker_transport = {
749
753
  "FLASH_CONTROL_PANEL_URL": "https://broker.example",
@@ -796,6 +796,57 @@ def test_runpod_cost_projection_flows_into_run_status(orch, monkeypatch):
796
796
  assert cost == pytest.approx(0.345) # 0.5 hr x $0.69/hr (RTX 4090)
797
797
 
798
798
 
799
+ def test_a_multi_card_run_is_costed_for_every_card_it_occupied(orch):
800
+ """`hourly_rate` is per CARD, so the measured cost must multiply by the allocated count.
801
+
802
+ without it a 4-card run records a quarter of its real spend, and the cost analytics that compare
803
+ estimates against actuals are corrupted by exactly that factor. the count has to come from the
804
+ metrics stamp rather than `spec.gpu.count`, which is only a ceiling -- allocation routinely picks
805
+ fewer cards than authored.
806
+ """
807
+ spec = _spec()
808
+ _seed_status(orch, spec)
809
+ base = {"train_tokens": 4096, "wall_seconds": 1800, "allocated_gpu": "RTX 4090"}
810
+
811
+ four_card = orch._persist_metrics(spec, {**base, "allocated_gpu_count": 4})
812
+
813
+ assert four_card == pytest.approx(0.345 * 4), "a 4-card run was priced as one card"
814
+ # a record predating the stamp still reads as one card rather than zero or a crash
815
+ assert orch._persist_metrics(spec, dict(base)) == pytest.approx(0.345)
816
+
817
+
818
+ def test_the_allocated_card_count_reaches_the_metrics_the_cost_is_read_from(orch, monkeypatch):
819
+ """The multiply above is inert unless allocation actually stamps the count it chose.
820
+
821
+ this drives the real submit path rather than handing `_persist_metrics` a literal, because the
822
+ two halves fail independently: pricing can multiply correctly by a count that is never recorded.
823
+ """
824
+ from flash.providers import allocator
825
+ from flash.providers.base import Candidate, PollResult
826
+ from flash.providers.runpod import jobs as rp_jobs
827
+
828
+ monkeypatch.setattr(
829
+ allocator,
830
+ "allocate",
831
+ lambda *a, **k: _alloc(candidates=(Candidate("runpod", "RTX 4090", 0.69, 24, 4),)),
832
+ )
833
+
834
+ def fake_runpod_submit(run_spec, seed, log=None, on_handle=None, attempt=0, **_):
835
+ if on_handle:
836
+ on_handle(_runpod_handle(attempt=attempt))
837
+ return PollResult(True, metrics={"train_tokens": 4096, "wall_seconds": 1800})
838
+
839
+ monkeypatch.setattr(rp_jobs, "submit_run", fake_runpod_submit)
840
+ spec = _spec(provider="runpod", type="RTX 4090", count=4)
841
+ _seed_status(orch, spec)
842
+
843
+ metrics = orch._submit_seed_supervised(spec, spec.seed, io.StringIO())
844
+
845
+ assert metrics["allocated_gpu_count"] == 4
846
+ # and the stamp is what pricing then reads, so the two halves compose
847
+ assert orch._persist_metrics(spec, metrics) == pytest.approx(0.345 * 4)
848
+
849
+
799
850
  def test_infra_retry_walks_to_next_runpod_class_and_deletes_endpoint(orch, monkeypatch):
800
851
  from flash.providers import allocator
801
852
  from flash.providers.base import Candidate, PollResult
@@ -5,7 +5,9 @@ from __future__ import annotations
5
5
  import contextlib
6
6
  import importlib.util
7
7
  import os
8
+ import pathlib
8
9
  import re
10
+ import shutil
9
11
  import sys
10
12
  import types
11
13
  from dataclasses import replace
@@ -959,10 +961,13 @@ def test_checkpoint_watcher_exports_and_uploads_required_step(monkeypatch, tmp_p
959
961
 
960
962
  watcher._publish(5, str(checkpoint_dir))
961
963
 
962
- assert exported[0][0] == str(actor_dir)
964
+ # both read the STAGED copy rather than verl's directory (see _staged_source); what matters is
965
+ # that they agree on one source and that it carries the `actor/` level this layout puts there.
966
+ assert os.path.basename(exported[0][0]) == "actor"
967
+ assert exported[0][0] == os.path.join(uploaded[0][1], "actor")
963
968
  assert published[0][1] == 5
964
969
  assert published[0][2]["required"] is True
965
- assert uploaded == [(5, str(checkpoint_dir))]
970
+ assert [step for step, _ in uploaded] == [5]
966
971
  assert watcher.processed_steps == {5}
967
972
 
968
973
 
@@ -1002,7 +1007,72 @@ def test_checkpoint_watcher_exports_the_sft_layout(monkeypatch, tmp_path):
1002
1007
 
1003
1008
  watcher._publish(5, str(checkpoint_dir))
1004
1009
 
1005
- assert exported == [str(checkpoint_dir)]
1010
+ # the export reads a STAGED copy, not verl's own directory (see _staged_source), so assert the
1011
+ # layout this test is about -- global_step_N itself, no `actor/` level -- rather than the path.
1012
+ assert [os.path.basename(path) for path in exported] == ["global_step_5"]
1013
+
1014
+
1015
+ def test_a_required_save_survives_verl_pruning_it_mid_publish(monkeypatch, tmp_path):
1016
+ """verl deleting global_step_N while it is being published must not fail the run.
1017
+
1018
+ the watcher publishes on its own thread, so the merge and the multi-GB upload overlap training.
1019
+ under `trainer.max_ckpt_to_keep=1` verl's `register_checkpoint` rmtree's global_step_N from the
1020
+ CHILD process the moment N+1 finishes saving, and the parent has no way to pin it. with close
1021
+ `save_at_steps` (here [2, 3]) that lands squarely inside the publish window.
1022
+
1023
+ this reproduces the interleave rather than asserting on the staging mechanism: the fake upload
1024
+ deletes verl's directory the way verl would, then reads the source it was handed. before staging,
1025
+ that read fails and takes the whole paid run down with a required-save error.
1026
+ """
1027
+ import flash.engine.worker as worker
1028
+ from flash.engine.worker import sft_train
1029
+
1030
+ local_dir = tmp_path / "checkpoints"
1031
+ checkpoint_dir = local_dir / "global_step_2"
1032
+ (checkpoint_dir / "actor" / "huggingface").mkdir(parents=True)
1033
+ (checkpoint_dir / "actor" / "model.safetensors").write_bytes(b"weights")
1034
+
1035
+ monkeypatch.setattr(
1036
+ sft_train,
1037
+ "_export_checkpoint_adapter",
1038
+ lambda actor, adapter, **kwargs: os.makedirs(adapter, exist_ok=True),
1039
+ )
1040
+ monkeypatch.setattr(
1041
+ worker, "publish_deployable_checkpoint", lambda adapter, step, **kwargs: None
1042
+ )
1043
+ read_back = {}
1044
+
1045
+ def fake_upload(step, checkpoint, **kwargs):
1046
+ kwargs["before_upload"]()
1047
+ # verl prunes global_step_2 because global_step_3 just landed. this is the whole race.
1048
+ shutil.rmtree(checkpoint_dir, ignore_errors=True)
1049
+ # the upload streams the folder AFTER that, so it must still have something to read.
1050
+ read_back["payload"] = (
1051
+ pathlib.Path(checkpoint, "actor", "model.safetensors").read_bytes()
1052
+ if os.path.isdir(checkpoint)
1053
+ else None
1054
+ )
1055
+ return True
1056
+
1057
+ monkeypatch.setattr(worker, "upload_resume_checkpoint", fake_upload)
1058
+ watcher = sft_train._VerlCheckpointWatcher(
1059
+ local_dir=str(local_dir),
1060
+ export_root=str(tmp_path / "exports"),
1061
+ python_bin="/verl/python",
1062
+ model_id="org/model",
1063
+ model_revision="commit",
1064
+ required_steps=(2, 3),
1065
+ )
1066
+
1067
+ watcher._publish(2, str(checkpoint_dir))
1068
+
1069
+ assert read_back["payload"] == b"weights", (
1070
+ "the upload lost its source when verl pruned the checkpoint, so a required save would fail "
1071
+ "an otherwise successful paid run"
1072
+ )
1073
+ assert watcher.processed_steps == {2}
1074
+ # the staging links are transient: they must not outlive the publish and accumulate on the pod.
1075
+ assert not os.path.exists(os.path.join(str(tmp_path / "exports"), "_staging", "global_step_2"))
1006
1076
 
1007
1077
 
1008
1078
  def test_resume_credits_only_required_saves_that_are_durable(monkeypatch):
@@ -1025,7 +1025,7 @@ wheels = [
1025
1025
 
1026
1026
  [[package]]
1027
1027
  name = "freesolo-flash"
1028
- version = "1.0.100"
1028
+ version = "1.1.2"
1029
1029
  source = { editable = "." }
1030
1030
 
1031
1031
  [package.optional-dependencies]