freesolo-flash-dev 1.0.45__tar.gz → 1.0.47__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 (397) hide show
  1. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/heartbeat.py +2 -2
  3. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_verl.py +17 -6
  4. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/rl_verl.py +84 -16
  5. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/sft_verl.py +12 -0
  6. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/verl_common.py +173 -0
  7. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/pyproject.toml +2 -2
  8. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_rl_verl.py +144 -80
  9. freesolo_flash_dev-1.0.47/tests/test_verl_step_metrics.py +280 -0
  10. freesolo_flash_dev-1.0.47/tests/test_verl_wandb_link.py +215 -0
  11. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/uv.lock +1 -1
  12. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.dockerignore +0 -0
  13. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.env.example +0 -0
  14. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  15. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  16. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  17. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  18. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/dependabot.yml +0 -0
  19. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/auto-rebake.yml +0 -0
  20. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/bake-kernel-cache.yml +0 -0
  21. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/ci.yml +0 -0
  22. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/main-source-guard.yml +0 -0
  23. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/notify-tests-repo.yml +0 -0
  24. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/publish-dev.yml +0 -0
  25. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/publish-image.yml +0 -0
  26. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/publish.yml +0 -0
  27. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/version-parity.yml +0 -0
  28. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.github/workflows/worker-image.yml +0 -0
  29. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/.gitignore +0 -0
  30. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/CODE_OF_CONDUCT.md +0 -0
  31. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/CONTRIBUTING.md +0 -0
  32. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/Dockerfile +0 -0
  33. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/Dockerfile.worker +0 -0
  34. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/LICENSE +0 -0
  35. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/NOTICE +0 -0
  36. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/README.md +0 -0
  37. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/SECURITY.md +0 -0
  38. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/build/kernel_cache/.gitignore +0 -0
  39. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/build/kernel_cache/.keep +0 -0
  40. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/Dockerfile.kernelcache +0 -0
  41. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/Dockerfile.kernelcache.relayer +0 -0
  42. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/bake_kernel_cache.py +0 -0
  43. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/bake_pod_entry.py +0 -0
  44. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/kernel_fingerprint.py +0 -0
  45. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/docker/make_rp_handler.py +0 -0
  46. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/__init__.py +0 -0
  47. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/_channel.py +0 -0
  48. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/_fileio.py +0 -0
  49. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/_logging.py +0 -0
  50. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/_update_check.py +0 -0
  51. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/adapter_artifacts.py +0 -0
  52. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/catalog.py +0 -0
  53. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/__init__.py +0 -0
  54. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/__main__.py +0 -0
  55. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/_tty.py +0 -0
  56. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/commands.py +0 -0
  57. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/env_setup.py +0 -0
  58. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/env_test.py +0 -0
  59. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/envpush.py +0 -0
  60. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/render.py +0 -0
  61. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/traces.py +0 -0
  62. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cli/training_doc.py +0 -0
  63. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/client/__init__.py +0 -0
  64. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/client/config.py +0 -0
  65. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/client/http.py +0 -0
  66. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/client/runtime_secrets.py +0 -0
  67. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/client/specs.py +0 -0
  68. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cost/__init__.py +0 -0
  69. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cost/analytical.py +0 -0
  70. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cost/facts.py +0 -0
  71. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cost/spec.py +0 -0
  72. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/cost/types.py +0 -0
  73. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/diagnostics.py +0 -0
  74. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/__init__.py +0 -0
  75. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/accounting.py +0 -0
  76. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/chalk_kernels.py +0 -0
  77. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/multiturn_reward_scoring.py +0 -0
  78. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/multiturn_rollout.py +0 -0
  79. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/recipe.py +0 -0
  80. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/steps.py +0 -0
  81. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/structured_outputs.py +0 -0
  82. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/vram.py +0 -0
  83. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/__init__.py +0 -0
  84. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/__main__.py +0 -0
  85. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/_pkg.py +0 -0
  86. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/adapter.py +0 -0
  87. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/decoding.py +0 -0
  88. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/finalize.py +0 -0
  89. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/gpu_setup.py +0 -0
  90. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/grpo.py +0 -0
  91. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/grpo_multimodal.py +0 -0
  92. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
  93. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/hf.py +0 -0
  94. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/kernel_warmup.py +0 -0
  95. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/lora.py +0 -0
  96. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd.py +0 -0
  97. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_gkd.py +0 -0
  98. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_verl_multiturn.py +0 -0
  99. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_verl_plugin.py +0 -0
  100. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_verl_structured.py +0 -0
  101. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/opd_vllm.py +0 -0
  102. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/packing.py +0 -0
  103. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/__init__.py +0 -0
  104. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/attn.py +0 -0
  105. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/diagnostics.py +0 -0
  106. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/lifecycle.py +0 -0
  107. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/liger.py +0 -0
  108. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/loraplus.py +0 -0
  109. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/perf/memory.py +0 -0
  110. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/rl.py +0 -0
  111. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/rng.py +0 -0
  112. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/rollout_samples.py +0 -0
  113. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/sft.py +0 -0
  114. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/teacher.py +0 -0
  115. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/tokenizer_align.py +0 -0
  116. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker/wandb_log.py +0 -0
  117. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/engine/worker_entrypoint.py +0 -0
  118. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/__init__.py +0 -0
  119. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/adapter.py +0 -0
  120. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/archive.py +0 -0
  121. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/archive_policy.py +0 -0
  122. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/base.py +0 -0
  123. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/loader.py +0 -0
  124. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/pull.py +0 -0
  125. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/envs/registry.py +0 -0
  126. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/lora_rank.py +0 -0
  127. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/multimodal.py +0 -0
  128. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/opd_retry_contract.py +0 -0
  129. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/opd_verl_validation.py +0 -0
  130. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/__init__.py +0 -0
  131. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_auth.py +0 -0
  132. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_deadline.py +0 -0
  133. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_hf_artifacts.py +0 -0
  134. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_hf_retry.py +0 -0
  135. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_http.py +0 -0
  136. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_instance.py +0 -0
  137. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_instance_bootstrap.py +0 -0
  138. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_instance_poll.py +0 -0
  139. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_instance_provider.py +0 -0
  140. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_poll.py +0 -0
  141. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/_worker.py +0 -0
  142. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/allocator.py +0 -0
  143. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/base.py +0 -0
  144. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/__init__.py +0 -0
  145. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/api.py +0 -0
  146. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/auth.py +0 -0
  147. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/gpus.py +0 -0
  148. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  149. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  150. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/preflight.py +0 -0
  151. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/lambdalabs/pricing.py +0 -0
  152. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/preflight.py +0 -0
  153. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/realized.py +0 -0
  154. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/__init__.py +0 -0
  155. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/api.py +0 -0
  156. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/auth.py +0 -0
  157. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/cost.py +0 -0
  158. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/gpus.py +0 -0
  159. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/jobs.py +0 -0
  160. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/keys.py +0 -0
  161. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/preflight.py +0 -0
  162. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/pricing.py +0 -0
  163. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/slots.py +0 -0
  164. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/train/__init__.py +0 -0
  165. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/runpod/train/endpoints.py +0 -0
  166. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/__init__.py +0 -0
  167. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/api.py +0 -0
  168. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/auth.py +0 -0
  169. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/jobs/__init__.py +0 -0
  170. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/jobs/builders.py +0 -0
  171. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/preflight.py +0 -0
  172. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/vast/pricing.py +0 -0
  173. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/providers/weight_cache_preload.py +0 -0
  174. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/py.typed +0 -0
  175. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/runner/__init__.py +0 -0
  176. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/runner/checkpoints.py +0 -0
  177. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/runner/deploy.py +0 -0
  178. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/runner/lifecycle.py +0 -0
  179. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/runner/verified_revisions.py +0 -0
  180. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/schema/__init__.py +0 -0
  181. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/schema/fields.py +0 -0
  182. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/__init__.py +0 -0
  183. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/deploy.py +0 -0
  184. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/export.py +0 -0
  185. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/preflight.py +0 -0
  186. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/pricing.py +0 -0
  187. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/serve/urls.py +0 -0
  188. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/__init__.py +0 -0
  189. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/__main__.py +0 -0
  190. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/_deps.py +0 -0
  191. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/_internal_client.py +0 -0
  192. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/_locks.py +0 -0
  193. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/_runtime.py +0 -0
  194. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/app.py +0 -0
  195. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/auth.py +0 -0
  196. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/billing.py +0 -0
  197. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/billing_retry.py +0 -0
  198. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/checkpoints.py +0 -0
  199. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/db.py +0 -0
  200. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/environment_registry.py +0 -0
  201. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/envs.py +0 -0
  202. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/projects.py +0 -0
  203. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/reconcile.py +0 -0
  204. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/repo_cleanup.py +0 -0
  205. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/routes/__init__.py +0 -0
  206. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/routes/envs.py +0 -0
  207. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/routes/meta.py +0 -0
  208. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/routes/runs.py +0 -0
  209. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/routes/serving.py +0 -0
  210. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/server/run_registry.py +0 -0
  211. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/flash/spec.py +0 -0
  212. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/infisical-entrypoint.sh +0 -0
  213. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/scripts/build_dev_dist.py +0 -0
  214. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/scripts/scrub_history.sh +0 -0
  215. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/__init__.py +0 -0
  216. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/_helpers/__init__.py +0 -0
  217. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/_helpers/runner.py +0 -0
  218. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/_helpers/specs.py +0 -0
  219. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/_helpers/vast.py +0 -0
  220. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/conftest.py +0 -0
  221. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/fixtures/math_eval.jsonl +0 -0
  222. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/fixtures/math_train.jsonl +0 -0
  223. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/live/__init__.py +0 -0
  224. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/live/conftest.py +0 -0
  225. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/live/test_lambda_live.py +0 -0
  226. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/live/test_runpod_live.py +0 -0
  227. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/live/test_vast_live.py +0 -0
  228. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_accounting_coverage.py +0 -0
  229. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_agent_flash_cli_contract.py +0 -0
  230. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_algorithms.py +0 -0
  231. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_allocator.py +0 -0
  232. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_async_uploads.py +0 -0
  233. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_b200_rollout_opt.py +0 -0
  234. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_backend_jobspec_contract.py +0 -0
  235. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_base_model_provenance.py +0 -0
  236. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_billing_retry.py +0 -0
  237. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cancel_remote.py +0 -0
  238. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_catalog_consistency.py +0 -0
  239. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_chalk_kernels.py +0 -0
  240. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_charge_pricing.py +0 -0
  241. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_checkpoints.py +0 -0
  242. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_commands.py +0 -0
  243. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_commands_coverage.py +0 -0
  244. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_errors.py +0 -0
  245. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_estimate.py +0 -0
  246. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_help.py +0 -0
  247. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_main_coverage.py +0 -0
  248. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_managed.py +0 -0
  249. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_render_coverage.py +0 -0
  250. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_render_theme.py +0 -0
  251. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cli_traces.py +0 -0
  252. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_client.py +0 -0
  253. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_client_server_integration.py +0 -0
  254. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_client_stream_reads.py +0 -0
  255. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_config_overrides.py +0 -0
  256. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_controlled_experiment_repairs.py +0 -0
  257. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_analytical.py +0 -0
  258. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_equation.py +0 -0
  259. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_estimate.py +0 -0
  260. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_hardware.py +0 -0
  261. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_models.py +0 -0
  262. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_cost_rewards.py +0 -0
  263. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_deploy_latency.py +0 -0
  264. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_dev_channel.py +0 -0
  265. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_disk_gb.py +0 -0
  266. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_endpoint_name.py +0 -0
  267. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_cache_evict.py +0 -0
  268. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_delete.py +0 -0
  269. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_download.py +0 -0
  270. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_download_api.py +0 -0
  271. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_no_install.py +0 -0
  272. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_publish.py +0 -0
  273. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_pull.py +0 -0
  274. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_pull_coverage.py +0 -0
  275. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_pull_loader_coverage.py +0 -0
  276. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_pull_managed_control_plane.py +0 -0
  277. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_push.py +0 -0
  278. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_rate_limit_resolve.py +0 -0
  279. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_env_test.py +0 -0
  280. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_envs_coverage.py +0 -0
  281. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_export.py +0 -0
  282. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_flash_mvp.py +0 -0
  283. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_flash_worker.py +0 -0
  284. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_flashinfer_cache_dirs.py +0 -0
  285. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_github_urlopen_retry.py +0 -0
  286. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_gpu_type_single_field.py +0 -0
  287. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_gpus.py +0 -0
  288. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_grpo_cudagraphs.py +0 -0
  289. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_grpo_mask_aware.py +0 -0
  290. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_grpo_params.py +0 -0
  291. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_grpo_perturn.py +0 -0
  292. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_grpo_sleep_gate.py +0 -0
  293. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_health_liveness.py +0 -0
  294. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_hf_retry.py +0 -0
  295. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_idle_endpoint_reaper.py +0 -0
  296. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_instance_bootstrap_coverage.py +0 -0
  297. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_internal_client.py +0 -0
  298. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_jobs.py +0 -0
  299. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_kernel_cache.py +0 -0
  300. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_kernel_fingerprint.py +0 -0
  301. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_kv_util.py +0 -0
  302. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_lambda_api_coverage.py +0 -0
  303. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_lambda_runner.py +0 -0
  304. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_logging.py +0 -0
  305. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_login_perms.py +0 -0
  306. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_lora_rank_coverage.py +0 -0
  307. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_lora_rank_preflight.py +0 -0
  308. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_managed_config_fields.py +0 -0
  309. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_managed_hf_repo.py +0 -0
  310. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_metrics_schema_agent_contract.py +0 -0
  311. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_mig_guard.py +0 -0
  312. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multi_gpu_gate.py +0 -0
  313. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multimodal_input_grads.py +0 -0
  314. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multimodal_training.py +0 -0
  315. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_image_rollout.py +0 -0
  316. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_per_turn_reward.py +0 -0
  317. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_rollout.py +0 -0
  318. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_rollout_coverage.py +0 -0
  319. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_rollout_records.py +0 -0
  320. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_multiturn_rollout_request_policy.py +0 -0
  321. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_named_reward_metrics.py +0 -0
  322. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_oom_escalate_gpu.py +0 -0
  323. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_opd.py +0 -0
  324. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_opd_full_state_resume.py +0 -0
  325. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_opd_resume_safety.py +0 -0
  326. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_opd_verl.py +0 -0
  327. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_opd_vllm.py +0 -0
  328. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_open_model_policy.py +0 -0
  329. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_orchestrator_flash.py +0 -0
  330. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_packing.py +0 -0
  331. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_packing_coverage.py +0 -0
  332. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_poll_helpers.py +0 -0
  333. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_preflight.py +0 -0
  334. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_projects.py +0 -0
  335. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_provider_preflight_coverage.py +0 -0
  336. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_provider_routing.py +0 -0
  337. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_provider_teardown_robustness.py +0 -0
  338. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_providers_symmetry.py +0 -0
  339. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_realized_cost.py +0 -0
  340. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_repo_cleanup.py +0 -0
  341. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_resolve_params_b.py +0 -0
  342. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_resume_on_retry.py +0 -0
  343. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_rollout_samples.py +0 -0
  344. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_runmgmt.py +0 -0
  345. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_runpod_api_delete.py +0 -0
  346. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_runpod_key_fingerprint.py +0 -0
  347. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_runpod_key_waterfall.py +0 -0
  348. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_runpod_slots.py +0 -0
  349. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_serve.py +0 -0
  350. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_api.py +0 -0
  351. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_app_coverage.py +0 -0
  352. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_auth_singleflight.py +0 -0
  353. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_billing.py +0 -0
  354. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_checkpoints_coverage.py +0 -0
  355. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_db.py +0 -0
  356. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_envs_coverage.py +0 -0
  357. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_server_locks_coverage.py +0 -0
  358. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_serving_context_preflight.py +0 -0
  359. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_serving_contract.py +0 -0
  360. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_serving_pricing_coverage.py +0 -0
  361. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_serving_schema_coverage.py +0 -0
  362. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_activation_shaping.py +0 -0
  363. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_chunked_nll.py +0 -0
  364. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_example_selection.py +0 -0
  365. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_gc_off.py +0 -0
  366. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_max_context.py +0 -0
  367. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_tokenize_cache.py +0 -0
  368. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_sft_verl.py +0 -0
  369. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_single_turn_multimodal_grpo.py +0 -0
  370. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_spec_and_validation.py +0 -0
  371. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_structured_outputs.py +0 -0
  372. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_thinking_config.py +0 -0
  373. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_train_context_preflight.py +0 -0
  374. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_training_controls.py +0 -0
  375. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_update_check.py +0 -0
  376. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vast_api.py +0 -0
  377. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vast_api_coverage.py +0 -0
  378. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vast_offers.py +0 -0
  379. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vast_provider_coverage.py +0 -0
  380. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vast_runner.py +0 -0
  381. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_verified_revisions.py +0 -0
  382. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_verifiers.py +0 -0
  383. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_verl_common.py +0 -0
  384. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_version.py +0 -0
  385. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  386. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_vram_architecture.py +0 -0
  387. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_wandb_log_coverage.py +0 -0
  388. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_wandb_naming.py +0 -0
  389. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_warmstart_adapter_download.py +0 -0
  390. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_warmstart_cross_repo.py +0 -0
  391. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_weight_cache.py +0 -0
  392. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_worker_dryrun.py +0 -0
  393. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_worker_hardexit.py +0 -0
  394. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_worker_image.py +0 -0
  395. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_worker_init_heartbeat.py +0 -0
  396. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/tests/test_worker_stack.py +0 -0
  397. {freesolo_flash_dev-1.0.45 → freesolo_flash_dev-1.0.47}/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.45
3
+ Version: 1.0.47
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
@@ -84,7 +84,7 @@ _HB_CLAIM_SEQ = 0
84
84
  _STEP_GPU_DIAG_INTERVAL_S = 300.0
85
85
  _SFT_HEARTBEAT_INTERVAL_S = 60.0
86
86
  # retain at least one metric row per second across the 900s training heartbeat throttle window.
87
- _GRPO_METRIC_HISTORY_LIMIT = 1024
87
+ GRPO_METRIC_HISTORY_LIMIT = 1024
88
88
 
89
89
 
90
90
  def _dump_thread_stacks(reason: str) -> None:
@@ -425,7 +425,7 @@ def make_reward_heartbeat_callback(reward_metrics=None, samples=None):
425
425
  metrics["max_completion_tokens"] = int(max_completion_tokens)
426
426
  self.metrics_last = [item for item in self.metrics_last if item["step"] != step]
427
427
  self.metrics_last.append(metrics)
428
- self.metrics_last = self.metrics_last[-_GRPO_METRIC_HISTORY_LIMIT:]
428
+ self.metrics_last = self.metrics_last[-GRPO_METRIC_HISTORY_LIMIT:]
429
429
  LATEST_GRPO_METRICS_LAST[:] = self.metrics_last
430
430
  payload = {
431
431
  **metrics,
@@ -71,6 +71,8 @@ from flash.engine.worker.verl_common import (
71
71
  clamp_engine_len,
72
72
  latest_global_step_dir,
73
73
  model_max_position_embeddings,
74
+ parse_wandb_link,
75
+ render_wandb_link_shim,
74
76
  resolve_verl_python,
75
77
  run_verl_training,
76
78
  )
@@ -2344,13 +2346,14 @@ def run_opd_verl(spec=None) -> None:
2344
2346
  entry_path = os.path.join(shim_dir, "flash_opd_verl_entry.py")
2345
2347
  with open(entry_path, "w", encoding="utf-8") as file:
2346
2348
  file.write("import verl\nfrom flash_opd_verl_plugin import main\nmain()\n")
2349
+ opd_shim_source = _render_opd_sitecustomize(
2350
+ save_at_steps=knobs.save_at_steps,
2351
+ total_steps=update_horizon,
2352
+ )
2353
+ if "wandb" in loggers:
2354
+ opd_shim_source += render_wandb_link_shim()
2347
2355
  with open(os.path.join(shim_dir, "sitecustomize.py"), "w", encoding="utf-8") as file:
2348
- file.write(
2349
- _render_opd_sitecustomize(
2350
- save_at_steps=knobs.save_at_steps,
2351
- total_steps=update_horizon,
2352
- )
2353
- )
2356
+ file.write(opd_shim_source)
2354
2357
 
2355
2358
  resume_step, resume_state = _restore_verl_resume(
2356
2359
  local_dir,
@@ -2451,9 +2454,13 @@ def run_opd_verl(spec=None) -> None:
2451
2454
  )
2452
2455
  command = [python_bin, entry_path, *overrides]
2453
2456
  progress = {"step": resume_step, "loss": None}
2457
+ wandb_link: dict[str, str | None] = {}
2454
2458
 
2455
2459
  def on_line(line: str) -> None:
2456
2460
  watcher.raise_if_failed()
2461
+ link = parse_wandb_link(line)
2462
+ if link is not None:
2463
+ wandb_link.update(link)
2457
2464
  step_match = _VERL_STEP_RE.search(line)
2458
2465
  if step_match is None:
2459
2466
  return
@@ -2669,6 +2676,10 @@ def run_opd_verl(spec=None) -> None:
2669
2676
  "resumed": bool(resume_step),
2670
2677
  "wandb_project": project_name if "wandb" in loggers else None,
2671
2678
  "wandb_run_name": experiment_name if "wandb" in loggers else None,
2679
+ # the sdk's link_wandb reads notes["wandb_url"]; trl gets it from the parent's live
2680
+ # wandb.run, verl from the child marker (see verl_common.render_wandb_link_shim).
2681
+ "wandb_url": wandb_link.get("wandb_url"),
2682
+ "wandb_id": wandb_link.get("wandb_id"),
2672
2683
  },
2673
2684
  )
2674
2685
  finally:
@@ -41,7 +41,11 @@ from flash.engine.structured_outputs import (
41
41
  reasoning_parser_for,
42
42
  )
43
43
  from flash.engine.worker._pkg import W as _w
44
- from flash.engine.worker.heartbeat import liveness_heartbeat
44
+ from flash.engine.worker.heartbeat import (
45
+ GRPO_METRIC_HISTORY_LIMIT,
46
+ LATEST_GRPO_METRICS_LAST,
47
+ liveness_heartbeat,
48
+ )
45
49
  from flash.engine.worker.hf import _deployable_adapter_on_hf
46
50
  from flash.engine.worker.perf import gpu_diagnostics, setup_perf_backends, wait_for_gpu
47
51
  from flash.engine.worker.rng import backend_seed, seed_training_rngs
@@ -50,8 +54,13 @@ from flash.engine.worker.sft_verl import _hydra_val, _NvidiaSmiPeakSampler
50
54
  from flash.engine.worker.verl_common import (
51
55
  VERL_REQUIREMENT,
52
56
  agent_loop_workers,
57
+ append_step_metrics,
53
58
  clamp_engine_len,
54
59
  model_max_position_embeddings,
60
+ parse_verl_metric,
61
+ parse_verl_step_metrics,
62
+ parse_wandb_link,
63
+ render_wandb_link_shim,
55
64
  resolve_verl_python,
56
65
  verl_supports_rollout_field,
57
66
  )
@@ -370,6 +379,8 @@ def _build_verl_train_notes(
370
379
  fp8_kv: bool = False,
371
380
  wandb_project: str | None = None,
372
381
  wandb_run_name: str | None = None,
382
+ wandb_url: str | None = None,
383
+ wandb_id: str | None = None,
373
384
  ) -> dict:
374
385
  return {
375
386
  "backend": "verl",
@@ -419,6 +430,10 @@ def _build_verl_train_notes(
419
430
  "ppo_max_token_len_per_gpu": inp["engine_len"],
420
431
  "wandb_project": wandb_project,
421
432
  "wandb_run_name": wandb_run_name,
433
+ # the sdk's link_wandb reads notes["wandb_url"]; trl gets it from the parent's live
434
+ # wandb.run, verl from the child marker (see verl_common.render_wandb_link_shim).
435
+ "wandb_url": wandb_url,
436
+ "wandb_id": wandb_id,
422
437
  "grpo_recipe": {
423
438
  "kl_coef": inp["kl_coef"],
424
439
  "entropy_quantile": inp["entropy_quantile"],
@@ -1420,6 +1435,10 @@ def run_rl_verl():
1420
1435
  render_stop_sequences_shim(inp["stop_sequences"]),
1421
1436
  render_structured_outputs_shim(inp["structured_outputs"]),
1422
1437
  render_exact_save_steps_shim(inp["save_at_steps"], inp["steps"]),
1438
+ # gated on the key rather than the resolved logger list: that list needs python_bin,
1439
+ # which is resolved after this file is written. the shim is inert either way -- it only
1440
+ # fires when verl actually calls wandb.init, which requires wandb in the logger list.
1441
+ render_wandb_link_shim() if os.environ.get("WANDB_API_KEY") else "",
1423
1442
  )
1424
1443
  if part
1425
1444
  )
@@ -1433,6 +1452,8 @@ def run_rl_verl():
1433
1452
  # rolling buffer of recent (completion, score) so the training loop can dump one sample per step
1434
1453
  # to the flash log, matching the trl path's #607 per-step completion dump.
1435
1454
  recent_samples: list[tuple[str, float]] = []
1455
+ # filled from the child's marker line; stays empty when wandb is off (see render_wandb_link_shim).
1456
+ wandb_link: dict[str, str | None] = {}
1436
1457
  _samples_lock = threading.Lock()
1437
1458
 
1438
1459
  def _score(index: int, solution_str: str) -> float:
@@ -1544,14 +1565,21 @@ def run_rl_verl():
1544
1565
  item for item in (shim_dir, os.environ.get("PYTHONPATH", "")) if item
1545
1566
  )
1546
1567
  step_re = re.compile(r"step:\s*(\d+)")
1547
- reward_re = re.compile(r"critic/rewards/mean:([-\d.eE+]+)")
1548
- loss_re = re.compile(r"actor/pg_loss:([-\d.eE+]+)")
1549
1568
  reward_history: list[float] = []
1550
- resp_len_re = re.compile(r"response_length/mean:([\d.eE+-]+)")
1551
1569
  resp_len_history: list[float] = []
1552
1570
  loss_curve: list[float] = []
1553
1571
  last_dump_step = [-1]
1554
- with liveness_heartbeat("rl_step", progress=_progress, progress_step=True):
1572
+ # per-step backlog for `flash runs log -f`, rebuilt from verl's own step lines because its
1573
+ # trainer runs out of process and cannot host trl's callback. read by the liveness thread
1574
+ # below, so mutate it in place (append_step_metrics) rather than rebinding.
1575
+ metrics_last: list[dict] = []
1576
+ sent_first_metrics = False
1577
+ with liveness_heartbeat(
1578
+ "rl_step",
1579
+ progress=_progress,
1580
+ fields=lambda: {"metrics_last": list(metrics_last)},
1581
+ progress_step=True,
1582
+ ):
1555
1583
  proc = subprocess.Popen(
1556
1584
  [python_bin, "-m", "verl.trainer.main_ppo", *overrides],
1557
1585
  stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env_for_verl,
@@ -1560,6 +1588,9 @@ def run_rl_verl():
1560
1588
  try:
1561
1589
  for line in proc.stdout:
1562
1590
  print(f"[verl] {line}", end="", flush=True)
1591
+ link = parse_wandb_link(line)
1592
+ if link is not None:
1593
+ wandb_link.update(link)
1563
1594
  m = step_re.search(line)
1564
1595
  if m:
1565
1596
  step_box[0] = int(m.group(1))
@@ -1574,16 +1605,43 @@ def run_rl_verl():
1574
1605
  f"[rl-verl] step {step_box[0]} sample (reward={samp[1]:.3f}): {preview}",
1575
1606
  flush=True,
1576
1607
  )
1577
- # capture verl's per-step reward + policy loss for train_meta observability parity.
1578
- for pat, sink in (
1579
- (reward_re, reward_history),
1580
- (loss_re, loss_curve),
1581
- (resp_len_re, resp_len_history),
1582
- ):
1583
- hit = pat.search(line)
1584
- if hit:
1585
- with contextlib.suppress(ValueError):
1586
- sink.append(float(hit.group(1)))
1608
+ step_metrics = parse_verl_step_metrics(line)
1609
+ if step_metrics is not None:
1610
+ # a run constant rather than a verl metric, so it is stamped here the way
1611
+ # trl's callback reads it off args.max_completion_length.
1612
+ step_metrics["max_completion_tokens"] = inp["max_completion"]
1613
+ append_step_metrics(
1614
+ metrics_last, step_metrics, limit=GRPO_METRIC_HISTORY_LIMIT
1615
+ )
1616
+ # the worker's error path reads this global, so a run that dies mid-training
1617
+ # still reports the steps it did complete (worker/__init__.py:_err_metrics).
1618
+ LATEST_GRPO_METRICS_LAST[:] = metrics_last
1619
+ # the rl_train_start ping arms the 900s rl_step throttle, and the liveness
1620
+ # daemon never forces, so the first backlog would stay invisible for 15
1621
+ # minutes. force it through the way trl does (heartbeat.py
1622
+ # force_first_samples), and keep retrying until one commits: the daemon may
1623
+ # claim this step first, which is fine because its payload carries the same
1624
+ # backlog. only the FIRST is forced, so the hf commit cap stays protected.
1625
+ if not sent_first_metrics:
1626
+ sent_first_metrics = _w.heartbeat(
1627
+ "rl_step",
1628
+ force=True,
1629
+ step=step_metrics["step"],
1630
+ metrics_last=list(metrics_last),
1631
+ gpu=gpu_diagnostics(include_torch=False),
1632
+ )
1633
+ # per-step series for train_meta observability parity. these live on the same
1634
+ # line as everything else: verl's only console metric sink is LocalLogger,
1635
+ # which always prints "step:N - ..." (verl/utils/logger/aggregate_logger.py),
1636
+ # so a line without a step carries no metric to collect.
1637
+ for verl_key, sink in (
1638
+ ("critic/rewards/mean", reward_history),
1639
+ ("actor/pg_loss", loss_curve),
1640
+ ("response_length/mean", resp_len_history),
1641
+ ):
1642
+ value = parse_verl_metric(line, verl_key)
1643
+ if value is not None:
1644
+ sink.append(value)
1587
1645
  rc = proc.wait()
1588
1646
  except BaseException:
1589
1647
  # the stream loop died (upload error, cancel, oom in the parent): a still-running
@@ -1631,6 +1689,7 @@ def run_rl_verl():
1631
1689
  with liveness_heartbeat(
1632
1690
  "rl_finalizing",
1633
1691
  progress=lambda: steps_run,
1692
+ fields=lambda: {"metrics_last": list(metrics_last)},
1634
1693
  progress_step=True,
1635
1694
  keepalive=True,
1636
1695
  ):
@@ -1644,7 +1703,13 @@ def run_rl_verl():
1644
1703
  if final_save_due(steps_run, inp["save_at_steps"]):
1645
1704
  _w.publish_deployable_checkpoint(adapter_dir, steps_run)
1646
1705
 
1647
- _w.heartbeat("rl_trained", train_wall=train_wall, step=steps_run, gpu=gpu_diagnostics())
1706
+ _w.heartbeat(
1707
+ "rl_trained",
1708
+ train_wall=train_wall,
1709
+ step=steps_run,
1710
+ gpu=gpu_diagnostics(),
1711
+ metrics_last=list(metrics_last),
1712
+ )
1648
1713
  _w.write_train_meta(
1649
1714
  phase="rl",
1650
1715
  adapter_dir=adapter_dir,
@@ -1652,6 +1717,7 @@ def run_rl_verl():
1652
1717
  train_wall=train_wall,
1653
1718
  setup_seconds=setup_seconds,
1654
1719
  train_tokens=0,
1720
+ heartbeat_fields={"metrics_last": list(metrics_last)},
1655
1721
  generated_tokens=int(
1656
1722
  sum(resp_len_history) / max(1, len(resp_len_history))
1657
1723
  * steps_run * inp["prompts_per_step"] * inp["group_size"]
@@ -1670,5 +1736,7 @@ def run_rl_verl():
1670
1736
  fp8_kv=fp8_kv,
1671
1737
  wandb_project=project_name if "wandb" in loggers else None,
1672
1738
  wandb_run_name=experiment_name if "wandb" in loggers else None,
1739
+ wandb_url=wandb_link.get("wandb_url"),
1740
+ wandb_id=wandb_link.get("wandb_id"),
1673
1741
  ),
1674
1742
  )
@@ -42,6 +42,8 @@ from flash.engine.worker.sft import (
42
42
  from flash.engine.worker.verl_common import (
43
43
  export_peft_adapter,
44
44
  latest_global_step_dir,
45
+ parse_wandb_link,
46
+ render_wandb_link_shim,
45
47
  resolve_verl_python,
46
48
  run_verl_training,
47
49
  stamp_adapter_dir_provenance,
@@ -1282,6 +1284,8 @@ def run_sft_verl(spec=None) -> None:
1282
1284
  total_steps=update_horizon,
1283
1285
  reentrant_gradient_checkpointing=reentrant_gradient_checkpointing,
1284
1286
  )
1287
+ if "wandb" in loggers:
1288
+ shim_source += render_wandb_link_shim()
1285
1289
  with open(os.path.join(shim_dir, "sitecustomize.py"), "w", encoding="utf-8") as file:
1286
1290
  file.write(shim_source)
1287
1291
  with open(custom_dataset_path, "w", encoding="utf-8") as file:
@@ -1327,12 +1331,16 @@ def run_sft_verl(spec=None) -> None:
1327
1331
  resume_step,
1328
1332
  )
1329
1333
  loraplus_applied = resume_step >= update_horizon
1334
+ wandb_link: dict[str, str | None] = {}
1330
1335
 
1331
1336
  def on_line(line: str) -> None:
1332
1337
  nonlocal loraplus_applied
1333
1338
  watcher.raise_if_failed()
1334
1339
  if _LORAPLUS_READY_MARKER in line:
1335
1340
  loraplus_applied = True
1341
+ link = parse_wandb_link(line)
1342
+ if link is not None:
1343
+ wandb_link.update(link)
1336
1344
  step_match = _VERL_STEP_RE.search(line)
1337
1345
  if step_match is None:
1338
1346
  return
@@ -1458,5 +1466,9 @@ def run_sft_verl(spec=None) -> None:
1458
1466
  "ulysses_sequence_parallel_size": gpu_count,
1459
1467
  "wandb_project": project_name if "wandb" in loggers else None,
1460
1468
  "wandb_run_name": experiment_name if "wandb" in loggers else None,
1469
+ # the sdk's link_wandb reads notes["wandb_url"]; trl gets it from the parent's live
1470
+ # wandb.run, verl from the child marker (see verl_common.render_wandb_link_shim).
1471
+ "wandb_url": wandb_link.get("wandb_url"),
1472
+ "wandb_id": wandb_link.get("wandb_id"),
1461
1473
  },
1462
1474
  )
@@ -12,6 +12,7 @@ from __future__ import annotations
12
12
 
13
13
  import contextlib
14
14
  import json
15
+ import math
15
16
  import os
16
17
  import re
17
18
  import shutil
@@ -317,3 +318,175 @@ def _kill_process_group(proc: subprocess.Popen) -> None:
317
318
  os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
318
319
  with contextlib.suppress(subprocess.TimeoutExpired):
319
320
  proc.wait(timeout=10)
321
+
322
+
323
+ # --------------------------- w&b run link (all three verl backends) ---------------------------
324
+ # trl spreads wandb_run_info() into its notes, giving the sdk's link_wandb a clickable
325
+ # notes["wandb_url"]. verl calls wandb.init INSIDE the training subprocess, so the flash parent's
326
+ # wandb.run is None and that spread would be a silent no-op. the url is not derivable up here
327
+ # either: it needs entity/project/runs/<wandb-generated-id>, and flash only knows the project and
328
+ # its own run NAME. so the child reports it back over the marker channel the parent already scans.
329
+ FLASH_WANDB_LINK_MARKER = "FLASH_WANDB_LINK"
330
+
331
+ _WANDB_LINK_RE = re.compile(rf"{FLASH_WANDB_LINK_MARKER}\s+(\{{.*\}})\s*$")
332
+
333
+
334
+ def render_wandb_link_shim() -> str:
335
+ """child-side sitecustomize fragment that reports the live w&b run's url and id.
336
+
337
+ wraps wandb.init rather than reading wandb.run at import time: sitecustomize runs long before
338
+ verl's tracking module initializes the run, so anything read here would always be None.
339
+
340
+ never raises. wandb is optional and a logging link must not be able to abort paid training, so
341
+ every failure path leaves the run unlinked instead of dead.
342
+ """
343
+ return f'''
344
+ # --- flash: report the w&b run link to the parent (see verl_common.render_wandb_link_shim) ---
345
+ try:
346
+ import json as _flash_wandb_json
347
+
348
+ import wandb as _flash_wandb
349
+
350
+ _flash_wandb_init = _flash_wandb.init
351
+
352
+ def _flash_wandb_init_reporting(*args, **kwargs):
353
+ _run = _flash_wandb_init(*args, **kwargs)
354
+ try:
355
+ _url = getattr(_run, "url", None)
356
+ _id = getattr(_run, "id", None)
357
+ if _url:
358
+ print(
359
+ "{FLASH_WANDB_LINK_MARKER} "
360
+ + _flash_wandb_json.dumps({{"wandb_url": _url, "wandb_id": _id}}),
361
+ flush=True,
362
+ )
363
+ except Exception:
364
+ pass
365
+ return _run
366
+
367
+ _flash_wandb.init = _flash_wandb_init_reporting
368
+ except Exception:
369
+ pass
370
+ '''
371
+
372
+
373
+ def parse_wandb_link(line: str) -> dict | None:
374
+ """the {{wandb_url, wandb_id}} a marker line carries, or None for every other line.
375
+
376
+ returns None rather than raising on a malformed payload: this parses child stdout, and a
377
+ truncated or interleaved line under multi-rank logging must not take down the run.
378
+ """
379
+ match = _WANDB_LINK_RE.search(line)
380
+ if match is None:
381
+ return None
382
+ try:
383
+ payload = json.loads(match.group(1))
384
+ except ValueError:
385
+ return None
386
+ if not isinstance(payload, dict):
387
+ return None
388
+ url = payload.get("wandb_url")
389
+ # https only: the sdk renders this as a clickable dashboard link, and child stdout also carries
390
+ # rollout text, so the field must not be able to become an arbitrary scheme.
391
+ if not isinstance(url, str) or not url.startswith("https://"):
392
+ return None
393
+ wandb_id = payload.get("wandb_id")
394
+ return {"wandb_url": url, "wandb_id": wandb_id if isinstance(wandb_id, str) else None}
395
+
396
+
397
+ # --------------------------- per-step grpo metrics (verl -> `flash runs log -f`) ---------------------------
398
+ # trl feeds `metrics_last` from a TrainerCallback (heartbeat.make_reward_heartbeat_callback), which
399
+ # verl cannot use: its trainer runs out of process. verl's LocalLogger prints exactly one line per
400
+ # optimizer update -- "step:N - key:value - key:value" over every scalar metric -- so the parent
401
+ # reconstructs the same backlog from that line. the payload schema is the CLI's, not verl's: keys
402
+ # below are what flash/cli/commands.py:_FOLLOW_METRIC_FIELDS renders.
403
+ #
404
+ # not anchored at line start: ray tags worker stdout with a "(TaskRunner pid=123) " prefix, so an
405
+ # anchored match would parse nothing at all in production. the existing progress regex in rl_verl
406
+ # is unanchored for the same reason.
407
+ _VERL_STEP_RE = re.compile(r"(?:^|\s)step:(\d+) - ")
408
+
409
+ # verl metric key -> flash `metrics_last` field. verl has no counterpart for trl's
410
+ # frac_reward_zero_std (an advantage-collapse fraction trl computes per group), so that column is
411
+ # simply absent for verl runs rather than faked; the renderer skips fields it does not find.
412
+ _VERL_METRIC_FIELDS = (
413
+ ("critic/rewards/mean", "reward"),
414
+ ("actor/grad_norm", "grad_norm"),
415
+ ("actor/kl_loss", "kl"),
416
+ ("actor/entropy", "entropy"),
417
+ ("response_length/mean", "mean_completion_tokens"),
418
+ ("response_length/clip_ratio", "truncation_rate"),
419
+ )
420
+
421
+ # verl reduces most metrics with np.mean and formats them through pprint, so under numpy>=2 a value
422
+ # prints as "np.float32(1.25)" rather than "1.25". verl's own requirements pin numpy<2, but the
423
+ # production interpreter comes from FLASH_VERL_PYTHON (a prebuilt image flash does not own), so
424
+ # accept both spellings -- the numpy-2 form would otherwise drop these columns silently.
425
+ _NUMPY_SCALAR_RE = re.compile(r"^np\.\w+\((.*)\)$")
426
+
427
+
428
+ def _metric_value(raw: str) -> float | None:
429
+ """one verl-printed scalar as a finite float, or None when it is not usable."""
430
+ wrapper = _NUMPY_SCALAR_RE.match(raw)
431
+ if wrapper is not None:
432
+ raw = wrapper.group(1)
433
+ try:
434
+ value = float(raw)
435
+ except ValueError:
436
+ return None
437
+ # nan/inf survive float() ("nan", "inf") but would render as a meaningless column and can poison
438
+ # a json payload downstream, so drop them the way the trl callback does.
439
+ return value if math.isfinite(value) else None
440
+
441
+
442
+ def parse_verl_metric(line: str, verl_key: str) -> float | None:
443
+ """one verl metric's finite float value from a step line, or None when absent.
444
+
445
+ anchors the key on a separator so a metric whose name merely ends with another's
446
+ (response_length_non_aborted/mean vs response_length/mean) cannot cross-match, and accepts the
447
+ numpy>=2 ``np.float64(...)`` spelling (see _NUMPY_SCALAR_RE).
448
+ """
449
+ hit = re.search(rf"(?:^|[\s-]){re.escape(verl_key)}:(\S+)", line)
450
+ return None if hit is None else _metric_value(hit.group(1))
451
+
452
+
453
+ def parse_verl_step_metrics(line: str) -> dict | None:
454
+ """the flash `metrics_last` entry a verl step line carries, or None when it carries none.
455
+
456
+ returns None rather than raising: this parses child stdout under multi-rank logging, where a
457
+ truncated or interleaved line must not take down a paid run.
458
+
459
+ a step line with no renderable metric yields None rather than a bare ``{"step": n}``: verl logs
460
+ its pre-training validation pass as its own record at the *current* step counter
461
+ (ray_trainer.py `logger.log(data=val_metrics, step=self.global_steps)`), and every key on that
462
+ line is namespaced val-core/ or val-aux/. keeping it would render a row with a step number and
463
+ no metrics, and -- because the backlog is deduplicated by step -- a resumed run's validation
464
+ pass would land on the resume step and displace a real training row.
465
+ """
466
+ match = _VERL_STEP_RE.search(line)
467
+ if match is None:
468
+ return None
469
+ metrics: dict[str, float | int] = {}
470
+ for verl_key, flash_key in _VERL_METRIC_FIELDS:
471
+ value = parse_verl_metric(line, verl_key)
472
+ if value is not None:
473
+ metrics[flash_key] = value
474
+ if not metrics:
475
+ return None
476
+ return {"step": int(match.group(1)), **metrics}
477
+
478
+
479
+ def append_step_metrics(backlog: list[dict], metrics: dict, *, limit: int) -> None:
480
+ """record one step in a bounded, de-duplicated backlog, mirroring the trl callback.
481
+
482
+ verl reprints a step on a validation pass, and a resumed run replays its resume step, so a
483
+ repeat must replace rather than append -- otherwise the CLI renders the same step twice.
484
+
485
+ the heartbeat thread reads ``backlog`` while the stdout loop writes it, so the new contents are
486
+ published in ONE slice assignment: a filter/append/truncate sequence would let that reader
487
+ observe a torn intermediate state with the step momentarily missing.
488
+ """
489
+ step = metrics.get("step")
490
+ kept = [item for item in backlog if item.get("step") != step]
491
+ kept.append(metrics)
492
+ backlog[:] = kept[-limit:]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "1.0.45"
3
+ version = "1.0.47"
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).
@@ -116,7 +116,7 @@ artifacts = ["flash/py.typed"]
116
116
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
117
117
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
118
118
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
119
- version = "1.0.45"
119
+ version = "1.0.47"
120
120
 
121
121
  [tool.pytest.ini_options]
122
122
  pythonpath = ["."]