freesolo-flash-dev 1.0.8__tar.gz → 1.0.9__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 (367) hide show
  1. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.env.example +1 -1
  2. freesolo_flash_dev-1.0.9/AB_PLAN.md +86 -0
  3. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/PKG-INFO +3 -3
  4. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/README.md +2 -2
  5. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/__init__.py +1 -1
  6. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/training_doc.py +2 -2
  7. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/vram.py +72 -1
  8. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/hf.py +101 -5
  9. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd.py +7 -1
  10. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_vllm.py +120 -20
  11. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/lora_rank.py +5 -0
  12. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/jobs.py +24 -5
  13. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/__init__.py +11 -2
  14. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/deploy.py +1 -1
  15. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/preflight.py +1 -5
  16. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/pyproject.toml +2 -2
  17. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_async_uploads.py +55 -0
  18. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_help.py +1 -1
  19. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_jobs.py +66 -0
  20. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_vllm.py +191 -3
  21. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_api.py +26 -0
  22. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/uv.lock +1 -1
  23. freesolo_flash_dev-1.0.8/AB_PLAN.md +0 -45
  24. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.claude/skills/verify/SKILL.md +0 -0
  25. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.dockerignore +0 -0
  26. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/auto-rebake.yml +0 -0
  27. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/bake-kernel-cache.yml +0 -0
  28. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/ci.yml +0 -0
  29. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/main-source-guard.yml +0 -0
  30. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/notify-tests-repo.yml +0 -0
  31. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-dev.yml +0 -0
  32. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish-image.yml +0 -0
  33. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/publish.yml +0 -0
  34. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/version-parity.yml +0 -0
  35. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.github/workflows/worker-image.yml +0 -0
  36. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/.gitignore +0 -0
  37. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/Dockerfile +0 -0
  38. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/Dockerfile.worker +0 -0
  39. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/LICENSE +0 -0
  40. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.gitignore +0 -0
  41. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/build/kernel_cache/.keep +0 -0
  42. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache +0 -0
  43. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/Dockerfile.kernelcache.relayer +0 -0
  44. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/bake_kernel_cache.py +0 -0
  45. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/bake_pod_entry.py +0 -0
  46. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/kernel_fingerprint.py +0 -0
  47. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/docker/make_rp_handler.py +0 -0
  48. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/__init__.py +0 -0
  49. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_channel.py +0 -0
  50. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_fileio.py +0 -0
  51. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_logging.py +0 -0
  52. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/_update_check.py +0 -0
  53. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/adapter_artifacts.py +0 -0
  54. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/catalog.py +0 -0
  55. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/__main__.py +0 -0
  56. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/_tty.py +0 -0
  57. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/commands.py +0 -0
  58. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/env_setup.py +0 -0
  59. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/envpush.py +0 -0
  60. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cli/render.py +0 -0
  61. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/__init__.py +0 -0
  62. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/config.py +0 -0
  63. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/http.py +0 -0
  64. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/runtime_secrets.py +0 -0
  65. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/client/specs.py +0 -0
  66. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/__init__.py +0 -0
  67. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/analytical.py +0 -0
  68. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/facts.py +0 -0
  69. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/spec.py +0 -0
  70. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/cost/types.py +0 -0
  71. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/diagnostics.py +0 -0
  72. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/__init__.py +0 -0
  73. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/accounting.py +0 -0
  74. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/chalk_kernels.py +0 -0
  75. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_reward_scoring.py +0 -0
  76. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/multiturn_rollout.py +0 -0
  77. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/recipe.py +0 -0
  78. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/steps.py +0 -0
  79. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/structured_outputs.py +0 -0
  80. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__init__.py +0 -0
  81. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/__main__.py +0 -0
  82. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/_pkg.py +0 -0
  83. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/adapter.py +0 -0
  84. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/decoding.py +0 -0
  85. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/finalize.py +0 -0
  86. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/gpu_setup.py +0 -0
  87. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo.py +0 -0
  88. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_multimodal.py +0 -0
  89. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
  90. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/heartbeat.py +0 -0
  91. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/kernel_warmup.py +0 -0
  92. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/lora.py +0 -0
  93. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/opd_gkd.py +0 -0
  94. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/packing.py +0 -0
  95. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/__init__.py +0 -0
  96. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/attn.py +0 -0
  97. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/diagnostics.py +0 -0
  98. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/lifecycle.py +0 -0
  99. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/liger.py +0 -0
  100. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/loraplus.py +0 -0
  101. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/perf/memory.py +0 -0
  102. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rl.py +0 -0
  103. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rng.py +0 -0
  104. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/rollout_samples.py +0 -0
  105. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/sft.py +0 -0
  106. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/teacher.py +0 -0
  107. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/tokenizer_align.py +0 -0
  108. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker/wandb_log.py +0 -0
  109. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/engine/worker_entrypoint.py +0 -0
  110. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/__init__.py +0 -0
  111. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/adapter.py +0 -0
  112. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/archive.py +0 -0
  113. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/archive_policy.py +0 -0
  114. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/base.py +0 -0
  115. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/loader.py +0 -0
  116. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/pull.py +0 -0
  117. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/envs/registry.py +0 -0
  118. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/multimodal.py +0 -0
  119. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/opd_retry_contract.py +0 -0
  120. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/__init__.py +0 -0
  121. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_auth.py +0 -0
  122. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_deadline.py +0 -0
  123. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_artifacts.py +0 -0
  124. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_hf_retry.py +0 -0
  125. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_http.py +0 -0
  126. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance.py +0 -0
  127. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_bootstrap.py +0 -0
  128. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_poll.py +0 -0
  129. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_instance_provider.py +0 -0
  130. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_poll.py +0 -0
  131. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/_worker.py +0 -0
  132. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/allocator.py +0 -0
  133. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/base.py +0 -0
  134. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/__init__.py +0 -0
  135. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/api.py +0 -0
  136. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/auth.py +0 -0
  137. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/gpus.py +0 -0
  138. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  139. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  140. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/preflight.py +0 -0
  141. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/lambdalabs/pricing.py +0 -0
  142. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/preflight.py +0 -0
  143. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/realized.py +0 -0
  144. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/__init__.py +0 -0
  145. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/api.py +0 -0
  146. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/auth.py +0 -0
  147. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/cost.py +0 -0
  148. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/gpus.py +0 -0
  149. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/keys.py +0 -0
  150. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preflight.py +0 -0
  151. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/preload.py +0 -0
  152. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/pricing.py +0 -0
  153. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/slots.py +0 -0
  154. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/__init__.py +0 -0
  155. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/runpod/train/endpoints.py +0 -0
  156. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/__init__.py +0 -0
  157. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/api.py +0 -0
  158. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/auth.py +0 -0
  159. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/__init__.py +0 -0
  160. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/jobs/builders.py +0 -0
  161. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/preflight.py +0 -0
  162. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/providers/vast/pricing.py +0 -0
  163. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/py.typed +0 -0
  164. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/checkpoints.py +0 -0
  165. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/deploy.py +0 -0
  166. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/lifecycle.py +0 -0
  167. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/runner/verified_revisions.py +0 -0
  168. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/schema/__init__.py +0 -0
  169. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/schema/fields.py +0 -0
  170. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/__init__.py +0 -0
  171. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/export.py +0 -0
  172. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/pricing.py +0 -0
  173. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/serve/urls.py +0 -0
  174. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/__init__.py +0 -0
  175. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/__main__.py +0 -0
  176. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_deps.py +0 -0
  177. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_internal_client.py +0 -0
  178. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_locks.py +0 -0
  179. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/_runtime.py +0 -0
  180. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/app.py +0 -0
  181. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/auth.py +0 -0
  182. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/billing.py +0 -0
  183. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/billing_retry.py +0 -0
  184. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/checkpoints.py +0 -0
  185. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/db.py +0 -0
  186. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/environment_registry.py +0 -0
  187. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/envs.py +0 -0
  188. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/reconcile.py +0 -0
  189. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/repo_cleanup.py +0 -0
  190. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/__init__.py +0 -0
  191. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/envs.py +0 -0
  192. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/meta.py +0 -0
  193. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/runs.py +0 -0
  194. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/routes/serving.py +0 -0
  195. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/server/run_registry.py +0 -0
  196. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/flash/spec.py +0 -0
  197. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/infisical-entrypoint.sh +0 -0
  198. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/scripts/build_dev_dist.py +0 -0
  199. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/__init__.py +0 -0
  200. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/__init__.py +0 -0
  201. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/runner.py +0 -0
  202. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/specs.py +0 -0
  203. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/_helpers/vast.py +0 -0
  204. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/conftest.py +0 -0
  205. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_eval.jsonl +0 -0
  206. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/fixtures/math_train.jsonl +0 -0
  207. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/__init__.py +0 -0
  208. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/conftest.py +0 -0
  209. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_lambda_live.py +0 -0
  210. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_runpod_live.py +0 -0
  211. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/live/test_vast_live.py +0 -0
  212. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_accounting_coverage.py +0 -0
  213. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_agent_flash_cli_contract.py +0 -0
  214. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_algorithms.py +0 -0
  215. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_allocator.py +0 -0
  216. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_b200_rollout_opt.py +0 -0
  217. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_backend_jobspec_contract.py +0 -0
  218. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_base_model_provenance.py +0 -0
  219. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_billing_retry.py +0 -0
  220. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cancel_remote.py +0 -0
  221. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_catalog_consistency.py +0 -0
  222. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_chalk_kernels.py +0 -0
  223. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_charge_pricing.py +0 -0
  224. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_checkpoints.py +0 -0
  225. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands.py +0 -0
  226. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_commands_coverage.py +0 -0
  227. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_errors.py +0 -0
  228. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_estimate.py +0 -0
  229. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_main_coverage.py +0 -0
  230. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_managed.py +0 -0
  231. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_coverage.py +0 -0
  232. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cli_render_theme.py +0 -0
  233. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client.py +0 -0
  234. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client_server_integration.py +0 -0
  235. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_client_stream_reads.py +0 -0
  236. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_config_overrides.py +0 -0
  237. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_controlled_experiment_repairs.py +0 -0
  238. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_analytical.py +0 -0
  239. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_equation.py +0 -0
  240. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_estimate.py +0 -0
  241. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_hardware.py +0 -0
  242. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_models.py +0 -0
  243. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_cost_rewards.py +0 -0
  244. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_deploy_latency.py +0 -0
  245. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_dev_channel.py +0 -0
  246. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_disk_gb.py +0 -0
  247. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_endpoint_name.py +0 -0
  248. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_cache_evict.py +0 -0
  249. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_delete.py +0 -0
  250. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_download.py +0 -0
  251. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_download_api.py +0 -0
  252. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_no_install.py +0 -0
  253. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_publish.py +0 -0
  254. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull.py +0 -0
  255. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_coverage.py +0 -0
  256. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_loader_coverage.py +0 -0
  257. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_pull_managed_control_plane.py +0 -0
  258. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_push.py +0 -0
  259. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_env_rate_limit_resolve.py +0 -0
  260. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_envs_coverage.py +0 -0
  261. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_export.py +0 -0
  262. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flash_mvp.py +0 -0
  263. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flash_worker.py +0 -0
  264. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_flashinfer_cache_dirs.py +0 -0
  265. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_github_urlopen_retry.py +0 -0
  266. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_gpus.py +0 -0
  267. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_cudagraphs.py +0 -0
  268. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_mask_aware.py +0 -0
  269. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_params.py +0 -0
  270. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_perturn.py +0 -0
  271. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_grpo_sleep_gate.py +0 -0
  272. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_health_liveness.py +0 -0
  273. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_hf_retry.py +0 -0
  274. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_idle_endpoint_reaper.py +0 -0
  275. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_instance_bootstrap_coverage.py +0 -0
  276. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_internal_client.py +0 -0
  277. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kernel_cache.py +0 -0
  278. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kernel_fingerprint.py +0 -0
  279. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_kv_util.py +0 -0
  280. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lambda_api_coverage.py +0 -0
  281. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lambda_runner.py +0 -0
  282. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_logging.py +0 -0
  283. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_login_perms.py +0 -0
  284. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_coverage.py +0 -0
  285. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_lora_rank_preflight.py +0 -0
  286. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_managed_hf_repo.py +0 -0
  287. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_metrics_schema_agent_contract.py +0 -0
  288. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_mig_guard.py +0 -0
  289. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multi_gpu_gate.py +0 -0
  290. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_input_grads.py +0 -0
  291. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multimodal_training.py +0 -0
  292. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_image_rollout.py +0 -0
  293. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_per_turn_reward.py +0 -0
  294. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout.py +0 -0
  295. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_coverage.py +0 -0
  296. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_records.py +0 -0
  297. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_multiturn_rollout_request_policy.py +0 -0
  298. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_named_reward_metrics.py +0 -0
  299. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_oom_escalate_gpu.py +0 -0
  300. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd.py +0 -0
  301. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_full_state_resume.py +0 -0
  302. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_opd_resume_safety.py +0 -0
  303. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_open_model_policy.py +0 -0
  304. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_orchestrator_flash.py +0 -0
  305. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_packing.py +0 -0
  306. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_packing_coverage.py +0 -0
  307. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_poll_helpers.py +0 -0
  308. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_preflight.py +0 -0
  309. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_preflight_coverage.py +0 -0
  310. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_routing.py +0 -0
  311. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_provider_teardown_robustness.py +0 -0
  312. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_providers_symmetry.py +0 -0
  313. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_realized_cost.py +0 -0
  314. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_repo_cleanup.py +0 -0
  315. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_resolve_params_b.py +0 -0
  316. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_resume_on_retry.py +0 -0
  317. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_rollout_samples.py +0 -0
  318. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runmgmt.py +0 -0
  319. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_api_delete.py +0 -0
  320. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_fingerprint.py +0 -0
  321. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_key_waterfall.py +0 -0
  322. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_runpod_slots.py +0 -0
  323. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serve.py +0 -0
  324. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_app_coverage.py +0 -0
  325. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_auth_singleflight.py +0 -0
  326. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_billing.py +0 -0
  327. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_checkpoints_coverage.py +0 -0
  328. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_db.py +0 -0
  329. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_envs_coverage.py +0 -0
  330. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_server_locks_coverage.py +0 -0
  331. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_context_preflight.py +0 -0
  332. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_contract.py +0 -0
  333. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_pricing_coverage.py +0 -0
  334. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_serving_schema_coverage.py +0 -0
  335. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_activation_shaping.py +0 -0
  336. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_chunked_nll.py +0 -0
  337. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_example_selection.py +0 -0
  338. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_gc_off.py +0 -0
  339. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_max_context.py +0 -0
  340. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_sft_tokenize_cache.py +0 -0
  341. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_single_turn_multimodal_grpo.py +0 -0
  342. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_spec_and_validation.py +0 -0
  343. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_structured_outputs.py +0 -0
  344. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_thinking_config.py +0 -0
  345. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_train_context_preflight.py +0 -0
  346. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_training_controls.py +0 -0
  347. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_update_check.py +0 -0
  348. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_api.py +0 -0
  349. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_api_coverage.py +0 -0
  350. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_offers.py +0 -0
  351. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_provider_coverage.py +0 -0
  352. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vast_runner.py +0 -0
  353. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_verified_revisions.py +0 -0
  354. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_verifiers.py +0 -0
  355. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_version.py +0 -0
  356. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  357. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_wandb_log_coverage.py +0 -0
  358. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_wandb_naming.py +0 -0
  359. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_adapter_download.py +0 -0
  360. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_warmstart_cross_repo.py +0 -0
  361. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_weight_cache.py +0 -0
  362. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_dryrun.py +0 -0
  363. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_hardexit.py +0 -0
  364. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_image.py +0 -0
  365. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_init_heartbeat.py +0 -0
  366. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_stack.py +0 -0
  367. {freesolo_flash_dev-1.0.8 → freesolo_flash_dev-1.0.9}/tests/test_worker_thinking.py +0 -0
@@ -31,4 +31,4 @@ FREESOLO_BASE_URL=http://backend:8000
31
31
  # Modal app that serves every adapter on one GPU per base model, scaling to zero when
32
32
  # idle). `flash deploy`/`undeploy`/`chat` register/deregister/chat against it. Defaults
33
33
  # to the hosted Modal URL when unset.
34
- FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
34
+ FREESOLO_SERVING_URL=https://serve.freesolo.co
@@ -0,0 +1,86 @@
1
+ # OPD rollout-memory utilization A/B plan
2
+
3
+ ## Question
4
+
5
+ Does sizing the colocated vLLM executor from measured post-load free VRAM, while reserving the modeled OPD training peak and allocator margin, eliminate the Qwen3.5 4B negative-KV startup failure and improve rollout throughput without changing training behavior or causing an OOM?
6
+
7
+ No paid GPU run is part of this change.
8
+
9
+ ## Frozen comparison
10
+
11
+ - control: `origin/dev` at `8fdfc858`
12
+ - treatment: branch `perf/opd-rollout-util`
13
+ - hardware: one H100 80 GB for each arm, from the same RunPod GPU type and image
14
+ - launch both arms concurrently on separate matched GPUs
15
+ - use the same base checkpoint, dataset revision, environment revision, seed, teacher, token budget, batch order, and worker image inputs
16
+ - use immutable commits for both arms
17
+
18
+ An H100 80 GB is the smallest card for this A/B. The reported failure is a large-card utilization defect, with roughly 60 GB left idle and a negative-KV startup failure for Qwen3.5 4B. An RTX 4090 24 GB does not have enough residual memory to expose the intended utilization increase and therefore cannot demonstrate the optimization.
19
+
20
+ ## OPD configuration
21
+
22
+ Use the existing reproducing environment and dataset with this training shape held constant:
23
+
24
+ ```toml
25
+ model = "Qwen/Qwen3.5-4B"
26
+ algorithm = "opd"
27
+ seed = 42
28
+
29
+ [environment]
30
+ id = "<frozen-reproduction-environment>"
31
+
32
+ [train]
33
+ epochs = 1
34
+ batch_size = 8
35
+ group_size = 1
36
+ max_context_tokens = 1536
37
+ max_completion_tokens = 512
38
+ lora_rank = 32
39
+ teacher_model = "glm-5.2"
40
+ ```
41
+
42
+ Use the same bounded number of optimizer steps in both arms. Do not tune either arm after launch.
43
+
44
+ ## Measurements
45
+
46
+ Record at vLLM initialization and for every rollout step:
47
+
48
+ 1. measured free and total VRAM immediately before vLLM construction
49
+ 2. modeled OPD training peak reserve, allocator margin, rollout budget, and final `gpu_memory_utilization`
50
+ 3. vLLM executor budget in GiB and available KV-cache budget or cache blocks
51
+ 4. rollout batch size and generated tokens per second
52
+ 5. peak allocated and peak reserved VRAM across the OPD loss forward/backward
53
+ 6. initialization outcome, negative-KV/cache-block errors, CUDA OOMs, and worker health
54
+ 7. per-step OPD loss, reward or task metric already emitted by the environment, and completed optimizer steps
55
+
56
+ ## Gates
57
+
58
+ ### Feasibility and memory safety
59
+
60
+ - treatment must initialize vLLM and complete every planned optimizer step with zero negative-KV/cache-block failures and zero CUDA OOMs
61
+ - treatment executor budget must never exceed measured free VRAM minus the logged training reserve and allocator margin
62
+ - treatment loss forward/backward peak must retain at least the allocator margin after accounting for the executor budget
63
+ - if the control reproduces the startup crash, treatment must complete the same configuration; otherwise the A/B is inconclusive on crash recovery but can still evaluate utilization and throughput
64
+
65
+ ### Utilization and throughput
66
+
67
+ - direction: treatment `gpu_memory_utilization` and KV budget must be higher than control on the H100
68
+ - threshold: treatment executor budget must increase by at least 16 GiB or utilization by at least 0.20 absolute
69
+ - direction: treatment rollout generated tokens per second must be no worse than control
70
+ - threshold: when both arms complete, treatment median steady-state rollout throughput must improve by at least 10 percent; if control crashes, report treatment throughput without claiming a relative speedup
71
+
72
+ ### Training parity
73
+
74
+ - both arms must consume the same prompts and completion-token budget and complete the same optimizer-step count
75
+ - per-checkpoint OPD loss curves must remain within normal deterministic/runtime noise, with no sustained treatment regression above 2 percent relative to control
76
+ - final task metric must not regress by more than 1 percentage point absolute
77
+ - any change in gradients, optimizer state, sampled inputs, or training math invalidates the A/B
78
+
79
+ ## Decision
80
+
81
+ Accept the optimization only if the treatment passes all memory-safety and training-parity gates and either:
82
+
83
+ 1. recovers the exact control startup failure, or
84
+ 2. meets both the executor-budget and throughput thresholds when both arms complete.
85
+
86
+ Reject or revise if treatment OOMs, violates the protected-memory budget, changes the training curve beyond the parity threshold, or fails to increase the H100 rollout budget materially.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 1.0.8
3
+ Version: 1.0.9
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
@@ -186,12 +186,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
186
186
  with a hint to deploy first.
187
187
 
188
188
  Operators can also call the serving backend directly after the adapter is registered.
189
- The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
189
+ The default serving backend is `https://serve.freesolo.co`, and
190
190
  operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
191
191
  Use that same base URL when calling the backend directly; pass the run id as `model`:
192
192
 
193
193
  ```bash
194
- export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
194
+ export FREESOLO_SERVING_URL=https://serve.freesolo.co
195
195
 
196
196
  curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
197
197
  -H "Content-Type: application/json" \
@@ -129,12 +129,12 @@ for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `4
129
129
  with a hint to deploy first.
130
130
 
131
131
  Operators can also call the serving backend directly after the adapter is registered.
132
- The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
132
+ The default serving backend is `https://serve.freesolo.co`, and
133
133
  operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
134
134
  Use that same base URL when calling the backend directly; pass the run id as `model`:
135
135
 
136
136
  ```bash
137
- export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
137
+ export FREESOLO_SERVING_URL=https://serve.freesolo.co
138
138
 
139
139
  curl -X POST "$FREESOLO_SERVING_URL/v1/chat/completions" \
140
140
  -H "Content-Type: application/json" \
@@ -162,7 +162,7 @@ class _FlashParser(_ThemedParser):
162
162
  f"train after publishing: `{CLI_NAME} env push --name my-env .`, "
163
163
  f"then `{CLI_NAME} train configs/sft.toml`",
164
164
  f"any command in depth: `{CLI_NAME} <command> --help`",
165
- "docs: https://freesolo.co/docs",
165
+ "docs: https://docs.freesolo.co",
166
166
  ]
167
167
  return render.help_page(
168
168
  "managed LoRA post-training", usage, _HELP_GROUPS, _HELP_OPTIONS, footers
@@ -8,7 +8,7 @@ TRAINING_MD = r"""# TRAINING.md — how to actually improve a model with Flash
8
8
  > `flash env setup` dropped this file next to your `environment.py` and `configs/`.
9
9
  > It is the playbook Freesolo's own training agents follow to turn a *finished*
10
10
  > run into a model that *measurably improved*. The mechanics live in the hosted
11
- > docs (https://freesolo.co/docs); this file is the judgment that sits on top of them.
11
+ > docs (https://docs.freesolo.co); this file is the judgment that sits on top of them.
12
12
 
13
13
  A run that reaches `done` is **not** the same as a run that worked. Submitting a run
14
14
  is not a result. The whole job is to design the learning signal, read what the run
@@ -713,5 +713,5 @@ flash export --adapter-id <run-id> --repository <you>/<repo> # export final ada
713
713
  flash export --adapter-id <run-id>/step-N --repository <you>/<repo> # export a checkpoint
714
714
  ```
715
715
 
716
- See the full reference at https://freesolo.co/docs.
716
+ See the full reference at https://docs.freesolo.co.
717
717
  """
@@ -27,6 +27,9 @@ _BYTES_PER_PARAM = {
27
27
  _BASE_OVERHEAD_GB = 4.0
28
28
  _ACT_COEF = 0.12
29
29
  _SFT_PER_DEVICE_BS_DEFAULT = 4
30
+ _VOCAB_DEFAULT = 248_320
31
+ _OPD_TRAINING_RESERVE_MULTIPLIER = 1.15
32
+ _OPD_ALLOCATOR_MARGIN_MAX_GB = 6.0
30
33
  OPD_CE_CHUNK_SIZE = 64
31
34
  _OPD_CE_PEAK_BYTES_PER_LOGIT = 16
32
35
 
@@ -126,6 +129,75 @@ def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size:
126
129
  return max(1, min(total, default))
127
130
 
128
131
 
132
+ def opd_training_peak_gb(
133
+ params_b: float,
134
+ seq_len: int,
135
+ *,
136
+ max_tokens: int | None = None,
137
+ prompts_per_step: int = 1,
138
+ group_size: int = 1,
139
+ thinking: bool = False,
140
+ vocab: int = _VOCAB_DEFAULT,
141
+ active_params_b: float | None = None,
142
+ ) -> float:
143
+ """Additional OPD loss-forward/backward memory above the resident student model."""
144
+ eff_b = float(active_params_b) if active_params_b else float(params_b)
145
+ width = math.sqrt(max(eff_b, 0.1))
146
+ loss_mb = opd_loss_microbatch(params_b, prompts_per_step, group_size)
147
+ activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
148
+ completion = opd_completion_len(max_tokens, thinking)
149
+ # the backward peak holds both full-sequence bf16 logits and fp32 completion rows plus gradients.
150
+ dense_logits = 2 * loss_mb * (seq_len * 2 + completion * 4) * vocab / 1e9
151
+ return activations + dense_logits
152
+
153
+
154
+ def opd_training_reserve_gb(
155
+ params_b: float,
156
+ seq_len: int,
157
+ *,
158
+ max_tokens: int | None = None,
159
+ prompts_per_step: int = 1,
160
+ group_size: int = 1,
161
+ thinking: bool = False,
162
+ vocab: int = _VOCAB_DEFAULT,
163
+ active_params_b: float | None = None,
164
+ ) -> float:
165
+ """OPD loss-forward/backward peak with the runtime safety multiplier."""
166
+ return _OPD_TRAINING_RESERVE_MULTIPLIER * opd_training_peak_gb(
167
+ params_b,
168
+ seq_len,
169
+ max_tokens=max_tokens,
170
+ prompts_per_step=prompts_per_step,
171
+ group_size=group_size,
172
+ thinking=thinking,
173
+ vocab=vocab,
174
+ active_params_b=active_params_b,
175
+ )
176
+
177
+
178
+ def opd_allocator_margin_gb(total_vram_gb: float) -> float:
179
+ """Allocator fragmentation slack protected by the OPD startup budget."""
180
+ return max(2.0, min(_OPD_ALLOCATOR_MARGIN_MAX_GB, float(total_vram_gb) * 0.05))
181
+
182
+
183
+ def opd_post_init_reserve_gb(params_b: float, lora_rank: int) -> float:
184
+ """Pending LoRA gradient, AdamW state, and persistent post-init growth above model residency.
185
+
186
+ OPD trains all linear layers with bf16 LoRA parameters. PyTorch AdamW lazily allocates two
187
+ parameter-dtype moment tensors on the first step, so each estimated trainable parameter needs
188
+ 2 bytes for its pending gradient plus 4 bytes for optimizer state. Use total model parameters for
189
+ the rank-linear geometry so MoE experts are not undercounted, then reserve the measured 35B-class
190
+ first-generation and post-init growth with a size-scaled 12 GB ceiling.
191
+ """
192
+ model_params_b = max(0.1, float(params_b))
193
+ rank = max(1, int(lora_rank))
194
+ trainable_params_b = (rank / 16.0) * (0.05 + model_params_b / 150.0)
195
+ gradient_gb = trainable_params_b * 2.0
196
+ adamw_state_gb = trainable_params_b * 4.0
197
+ persistent_growth_gb = max(1.0, min(12.0, model_params_b * 0.35))
198
+ return gradient_gb + adamw_state_gb + persistent_growth_gb
199
+
200
+
129
201
  def _resident_kv_gb(
130
202
  params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
131
203
  ) -> float:
@@ -224,7 +296,6 @@ _VLLM_COLOCATE_FLOOR_GB = 28.0
224
296
  # init time. Keep 2B+ OPD off <=40 GB classes so the allocator picks an 80 GB-class card instead of a
225
297
  # consumer GPU that passes the aggregate estimate but fails the vLLM free-memory preflight.
226
298
  _OPD_VLLM_COLOCATE_FLOOR_GB = 41.0
227
- _VOCAB_DEFAULT = 248_320
228
299
  _LOGITS_BUDGET_GB = 6.0
229
300
  # 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
230
301
  _SFT_LOGITS_BYTES_PER_ELEM = 16.0
@@ -197,6 +197,7 @@ class _OptionalUpload:
197
197
  label: str
198
198
  staged_dir: str
199
199
  run: Callable[[], None]
200
+ on_coalesce: Callable[[_OptionalUpload], None] | None = None
200
201
 
201
202
 
202
203
  class _SingleSlotUploader:
@@ -209,12 +210,18 @@ class _SingleSlotUploader:
209
210
  self._next_sequence = 0
210
211
  self._thread: threading.Thread | None = None
211
212
 
212
- def enqueue(self, label: str, staged_dir: str, run: Callable[[], None]) -> None:
213
+ def enqueue(
214
+ self,
215
+ label: str,
216
+ staged_dir: str,
217
+ run: Callable[[], None],
218
+ on_coalesce: Callable[[_OptionalUpload], None] | None = None,
219
+ ) -> None:
213
220
  replaced: _OptionalUpload | None = None
214
221
  thread: threading.Thread | None = None
215
222
  with self._condition:
216
223
  self._next_sequence += 1
217
- task = _OptionalUpload(self._next_sequence, label, staged_dir, run)
224
+ task = _OptionalUpload(self._next_sequence, label, staged_dir, run, on_coalesce)
218
225
  replaced, self._pending = self._pending, task
219
226
  if self._thread is None:
220
227
  thread = threading.Thread(
@@ -229,7 +236,11 @@ class _SingleSlotUploader:
229
236
  f"[upload] coalesced optional {replaced.label} into newer {label} "
230
237
  f"(sequence {task.sequence})"
231
238
  )
232
- shutil.rmtree(replaced.staged_dir, ignore_errors=True)
239
+ if replaced.on_coalesce is not None:
240
+ # the coalesce hook takes over cleanup of the replaced staged tree.
241
+ replaced.on_coalesce(replaced)
242
+ else:
243
+ shutil.rmtree(replaced.staged_dir, ignore_errors=True)
233
244
  if thread is not None:
234
245
  thread.start()
235
246
 
@@ -264,8 +275,68 @@ class _SingleSlotUploader:
264
275
  return True
265
276
 
266
277
 
278
+ class _FifoUploader:
279
+ """run uploads one at a time in FIFO order, never dropping a pending task.
280
+
281
+ unlike _SingleSlotUploader this keeps every enqueued upload, so per-step artifacts that must
282
+ each land (deployable adapters) are not coalesced away by a newer save.
283
+ """
284
+
285
+ def __init__(self) -> None:
286
+ self._condition = threading.Condition()
287
+ self._in_flight: _OptionalUpload | None = None
288
+ self._pending: list[_OptionalUpload] = []
289
+ self._next_sequence = 0
290
+ self._thread: threading.Thread | None = None
291
+
292
+ def enqueue(self, label: str, staged_dir: str, run: Callable[[], None]) -> None:
293
+ thread: threading.Thread | None = None
294
+ with self._condition:
295
+ self._next_sequence += 1
296
+ self._pending.append(_OptionalUpload(self._next_sequence, label, staged_dir, run))
297
+ if self._thread is None:
298
+ thread = threading.Thread(
299
+ target=self._drain, name="flash-deployable-uploader", daemon=True
300
+ )
301
+ self._thread = thread
302
+ self._condition.notify_all()
303
+ if thread is not None:
304
+ thread.start()
305
+
306
+ def _drain(self) -> None:
307
+ while True:
308
+ with self._condition:
309
+ if not self._pending:
310
+ self._thread = None
311
+ self._condition.notify_all()
312
+ return
313
+ task = self._pending.pop(0)
314
+ self._in_flight = task
315
+ try:
316
+ task.run()
317
+ except Exception as e:
318
+ print(f"deployable upload warn ({task.label}): {sanitize_diagnostic(e, limit=500)}")
319
+ finally:
320
+ shutil.rmtree(task.staged_dir, ignore_errors=True)
321
+ with self._condition:
322
+ self._in_flight = None
323
+ self._condition.notify_all()
324
+
325
+ def flush(self, timeout_s: float) -> bool:
326
+ """wait for the in-flight and all pending FIFO uploads to finish."""
327
+ deadline = time.monotonic() + max(0.0, timeout_s)
328
+ with self._condition:
329
+ while self._in_flight is not None or self._pending:
330
+ remaining = deadline - time.monotonic()
331
+ if remaining <= 0:
332
+ return False
333
+ self._condition.wait(remaining)
334
+ return True
335
+
336
+
267
337
  _OPTIONAL_CHECKPOINT_UPLOADER = _SingleSlotUploader()
268
338
  _OPTIONAL_AUX_UPLOADER = _SingleSlotUploader()
339
+ _OPTIONAL_DEPLOYABLE_UPLOADER = _FifoUploader()
269
340
  _DEBUG_UPLOAD_LOCK = threading.Lock()
270
341
 
271
342
 
@@ -331,8 +402,10 @@ def flush_optional_uploads(timeout_s: float = _OPTIONAL_UPLOAD_FLUSH_TIMEOUT_S)
331
402
  started = time.monotonic()
332
403
  checkpoints_flushed = _OPTIONAL_CHECKPOINT_UPLOADER.flush(timeout_s)
333
404
  remaining = max(0.0, timeout_s - (time.monotonic() - started))
405
+ deployables_flushed = _OPTIONAL_DEPLOYABLE_UPLOADER.flush(remaining)
406
+ remaining = max(0.0, timeout_s - (time.monotonic() - started))
334
407
  aux_flushed = _OPTIONAL_AUX_UPLOADER.flush(remaining)
335
- return checkpoints_flushed and aux_flushed
408
+ return checkpoints_flushed and deployables_flushed and aux_flushed
336
409
 
337
410
 
338
411
  def upload_debug_jsonl(name: str, rows: list[dict], *, keep_last: int = 200) -> None:
@@ -985,14 +1058,37 @@ def make_checkpoint_upload_callback(save_at_steps=()):
985
1058
  ckpt_dir, f"checkpoint-{step}"
986
1059
  )
987
1060
  except Exception as e:
988
- print(f"[ckpt] step {step} snapshot warn: {sanitize_diagnostic(e, limit=500)}")
1061
+ # surface the miss explicitly rather than logging a soft warning and continuing as if
1062
+ # the periodic save reached hf.
1063
+ print(
1064
+ f"[ckpt] step {step} snapshot failed; step not published: "
1065
+ f"{sanitize_diagnostic(e, limit=500)}"
1066
+ )
989
1067
  return
1068
+
1069
+ def _publish_coalesced_deployable(
1070
+ replaced: _OptionalUpload,
1071
+ step: int = step,
1072
+ staged_checkpoint: str = staged_checkpoint,
1073
+ ) -> None:
1074
+ # a newer optional save coalesced this resume checkpoint away; still publish this step's
1075
+ # small durable deployable through the non-coalescing fifo path (which owns the staged
1076
+ # tree cleanup) so per-step deployables are never dropped.
1077
+ _OPTIONAL_DEPLOYABLE_UPLOADER.enqueue(
1078
+ f"coalesced deployable step {step}",
1079
+ replaced.staged_dir,
1080
+ lambda: publish_deployable_checkpoint(
1081
+ staged_checkpoint, step, _provenance_ready=True, _emit_heartbeat=False
1082
+ ),
1083
+ )
1084
+
990
1085
  _OPTIONAL_CHECKPOINT_UPLOADER.enqueue(
991
1086
  f"checkpoint step {step}",
992
1087
  staged_dir,
993
1088
  lambda: _upload(
994
1089
  step, staged_checkpoint, provenance_ready=True, emit_heartbeat=False
995
1090
  ),
1091
+ on_coalesce=_publish_coalesced_deployable,
996
1092
  )
997
1093
 
998
1094
  class _CheckpointUpload(TrainerCallback):
@@ -819,10 +819,16 @@ def run_opd():
819
819
 
820
820
  # resolve the model/tokenizer halt set once for vllm sampling and termination classification.
821
821
  generation_eos_ids = _generation_eos_ids(model, tok)
822
- vllm_kwargs = _opd_vllm_kwargs(model_id, knobs, seq_cap)
823
822
  lora_rank = _opd_lora_rank(
824
823
  model, getattr(_w.JOB_SPEC.train, "lora_rank", 32) if _w.JOB_SPEC else 32
825
824
  )
825
+ vllm_kwargs = _opd_vllm_kwargs(
826
+ model_id,
827
+ knobs,
828
+ seq_cap,
829
+ prompts_per_step=prompts_per_step,
830
+ lora_rank=lora_rank,
831
+ )
826
832
  print(
827
833
  f"[opd] rollout backend: colocated vLLM model={rollout_model_source} "
828
834
  f"ctx={seq_cap} lora_rank={lora_rank} util={vllm_kwargs['gpu_memory_utilization']:.2f} "
@@ -92,7 +92,7 @@ def _startup_oom_error(
92
92
  free_gb: float,
93
93
  total_gb: float,
94
94
  requested_util: float,
95
- margin_gb: float,
95
+ reserve_gb: float,
96
96
  rollout_batch_size: int,
97
97
  ) -> BaseException:
98
98
  requested_gb = float(requested_util) * float(total_gb)
@@ -100,8 +100,8 @@ def _startup_oom_error(
100
100
  "Free memory on device cuda:0 "
101
101
  f"({free_gb:.2f}/{total_gb:.2f} GiB) on startup is less than desired GPU "
102
102
  f"memory utilization ({requested_util:.6f} -> {requested_gb:.2f} GiB) "
103
- f"with {margin_gb:.2f} GiB startup margin after reducing OPD rollout_batch_size "
104
- f"to {rollout_batch_size}. Retry on a larger GPU."
103
+ f"with {reserve_gb:.2f} GiB reserved for the OPD training peak and allocator "
104
+ f"after reducing OPD rollout_batch_size to {rollout_batch_size}. Retry on a larger GPU."
105
105
  )
106
106
  try:
107
107
  import torch
@@ -118,7 +118,34 @@ def _decode_only_compilation_config() -> dict[str, Any]:
118
118
  }
119
119
 
120
120
 
121
- def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
121
+ def _sizing_lora_rank(knobs: Any, default: int = 32) -> int:
122
+ rank = getattr(knobs, "lora_rank", None)
123
+ if rank is None:
124
+ try:
125
+ from flash.engine.worker._pkg import W as _w
126
+
127
+ train = getattr(getattr(_w, "JOB_SPEC", None), "train", None)
128
+ rank = getattr(train, "lora_rank", None)
129
+ except Exception:
130
+ rank = None
131
+ try:
132
+ return max(1, int(rank if rank is not None else default))
133
+ except (TypeError, ValueError):
134
+ return default
135
+
136
+
137
+ def _rollout_uplift_validated(params_b: float | None, lora_rank: int) -> bool:
138
+ return params_b is not None and float(params_b) <= 4.7 and int(lora_rank) <= 32
139
+
140
+
141
+ def opd_vllm_kwargs(
142
+ model_id: str,
143
+ knobs: Any,
144
+ seq_cap: int,
145
+ *,
146
+ prompts_per_step: int | None = None,
147
+ lora_rank: int | None = None,
148
+ ) -> dict[str, Any]:
122
149
  """Direct vLLM LLM(...) kwargs mirroring the GRPO colocate rollout tuning."""
123
150
  kwargs: dict[str, Any] = {
124
151
  "gpu_memory_utilization": 0.10,
@@ -155,18 +182,32 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
155
182
 
156
183
  if card_gb > 0:
157
184
  try:
158
- from flash.catalog import MODELS
185
+ from flash.catalog import MODELS, vocab_size_for
159
186
  from flash.engine.vram import (
160
187
  colocate_kv_util,
188
+ opd_allocator_margin_gb,
189
+ opd_post_init_reserve_gb,
161
190
  opd_rollout_concurrency,
191
+ opd_training_reserve_gb,
162
192
  resolve_params_b,
163
193
  )
164
194
 
165
195
  info = MODELS.get(model_id)
166
196
  params_b = resolve_params_b(model_id)
197
+ sizing_params_b = float(params_b or 1.0)
198
+ resolved_lora_rank = (
199
+ _sizing_lora_rank(knobs)
200
+ if lora_rank is None
201
+ else max(1, int(lora_rank))
202
+ )
203
+ resolved_prompts_per_step = (
204
+ getattr(knobs, "prompts_per_step", 1)
205
+ if prompts_per_step is None
206
+ else prompts_per_step
207
+ )
167
208
  active_b = float(getattr(info, "active_params_b", 0.0) or 0.0) if info else 0.0
168
209
  target_concurrency = opd_rollout_concurrency(
169
- getattr(knobs, "prompts_per_step", 1),
210
+ resolved_prompts_per_step,
170
211
  getattr(knobs, "group_size", 1),
171
212
  )
172
213
  rollout_concurrency = target_concurrency
@@ -184,12 +225,26 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
184
225
 
185
226
  util = _util_for(rollout_concurrency)
186
227
  if free_gb > 0:
187
- # vLLM rejects startup unless its whole executor budget is free right now. OPD builds
188
- # the rollout engine after the student + warm-start adapter are resident, so choose the
189
- # largest rollout chunk that fits the observed residual memory instead of reserving for
190
- # every prompt in the optimizer step.
191
- startup_margin_gb = max(1.0, min(4.0, card_gb * 0.03))
192
- max_startup_util = max(0.0, (free_gb - startup_margin_gb) / max(1.0, card_gb))
228
+ # free memory is measured after the student and adapter are resident. protect the
229
+ # modeled loss forward/backward peak plus allocator slack, then give the remaining
230
+ # memory to the rollout executor instead of leaving it idle behind the generic cap.
231
+ training_reserve_gb = opd_training_reserve_gb(
232
+ sizing_params_b,
233
+ int(seq_cap),
234
+ max_tokens=getattr(knobs, "max_completion", None),
235
+ prompts_per_step=resolved_prompts_per_step,
236
+ group_size=getattr(knobs, "group_size", 1),
237
+ vocab=vocab_size_for(model_id),
238
+ active_params_b=active_b,
239
+ )
240
+ post_init_reserve_gb = opd_post_init_reserve_gb(
241
+ sizing_params_b, resolved_lora_rank
242
+ )
243
+ allocator_margin_gb = opd_allocator_margin_gb(card_gb)
244
+ protected_gb = training_reserve_gb + post_init_reserve_gb + allocator_margin_gb
245
+ rollout_budget_gb = max(0.0, free_gb - protected_gb)
246
+ max_startup_util = rollout_budget_gb / max(1.0, card_gb)
247
+ lean_trimmed = False
193
248
  while rollout_concurrency > 1 and util > max_startup_util:
194
249
  rollout_concurrency -= 1
195
250
  util = _util_for(rollout_concurrency)
@@ -198,16 +253,61 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
198
253
  "[opd] reduced vLLM rollout batch "
199
254
  f"{target_concurrency}->{rollout_concurrency} to fit startup memory "
200
255
  f"(free={free_gb:.1f} GiB, request={util * card_gb:.1f} GiB, "
201
- f"margin={startup_margin_gb:.1f} GiB)"
256
+ f"protected={protected_gb:.1f} GiB)"
202
257
  )
203
258
  if util > max_startup_util:
204
- startup_oom = _startup_oom_error(
205
- free_gb=free_gb,
206
- total_gb=card_gb,
207
- requested_util=util,
208
- margin_gb=startup_margin_gb,
209
- rollout_batch_size=rollout_concurrency,
210
- )
259
+ # concurrency bottomed out but the resident-KV floor (vLLM weight copy + _KV_CAP)
260
+ # keeps the minimal executor above the protected budget. before hard-OOMing, trim
261
+ # the allocator slack to the old ~1 GiB startup margin so configs that fit before
262
+ # the protected-budget change are not newly rejected; the modeled training peak and
263
+ # post-init reserve stay intact.
264
+ lean_protected_gb = training_reserve_gb + post_init_reserve_gb + 1.0
265
+ lean_startup_util = max(0.0, free_gb - lean_protected_gb) / max(1.0, card_gb)
266
+ if util <= lean_startup_util:
267
+ print(
268
+ "[opd] trimmed allocator margin "
269
+ f"{allocator_margin_gb:.1f}->1.0 GiB to fit rollout executor at "
270
+ f"concurrency {rollout_concurrency} "
271
+ f"(free={free_gb:.1f} GiB, request={util * card_gb:.1f} GiB)"
272
+ )
273
+ protected_gb = lean_protected_gb
274
+ max_startup_util = lean_startup_util
275
+ lean_trimmed = True
276
+ # the concurrency loop above bottomed out against the stricter full
277
+ # budget; re-seek how many sequences now fit under the relaxed lean
278
+ # ceiling so the lean path is not frozen at the reduced floor.
279
+ while (
280
+ rollout_concurrency < target_concurrency
281
+ and _util_for(rollout_concurrency + 1) <= max_startup_util
282
+ ):
283
+ rollout_concurrency += 1
284
+ util = _util_for(rollout_concurrency)
285
+ else:
286
+ startup_oom = _startup_oom_error(
287
+ free_gb=free_gb,
288
+ total_gb=card_gb,
289
+ requested_util=util,
290
+ reserve_gb=protected_gb,
291
+ rollout_batch_size=rollout_concurrency,
292
+ )
293
+ if (
294
+ startup_oom is None
295
+ and not lean_trimmed
296
+ and _rollout_uplift_validated(params_b, resolved_lora_rank)
297
+ ):
298
+ # 0.80 is a final ceiling for the validated qwen3.5-4b/rank-32 size class; larger
299
+ # or higher-rank models keep the existing colocate ceiling until gpu validation.
300
+ # skip uplift when we had to lean-trim: the lean budget only leaves ~1 GiB of
301
+ # headroom for the training peak, so spending it on the rollout executor risks OOM.
302
+ util = max(util, min(0.80, max_startup_util))
303
+ print(
304
+ "[opd] vLLM memory budget "
305
+ f"free={free_gb:.1f}/{card_gb:.1f} GiB "
306
+ f"training_reserve={training_reserve_gb:.1f} GiB "
307
+ f"post_init_reserve={post_init_reserve_gb:.1f} GiB "
308
+ f"allocator_margin={allocator_margin_gb:.1f} GiB "
309
+ f"rollout={util * card_gb:.1f} GiB util={util:.3f}"
310
+ )
211
311
  kwargs["gpu_memory_utilization"] = util
212
312
  kwargs["max_num_seqs"] = rollout_concurrency
213
313
  kwargs["rollout_batch_size"] = rollout_concurrency
@@ -13,6 +13,11 @@ from typing import TYPE_CHECKING, Any
13
13
 
14
14
  from flash.catalog import serving_context_cap, serving_lora_rank_cap
15
15
 
16
+
17
+ class ServingPreflightError(ValueError):
18
+ """Serving-path preflight rejection. Homed here (dependency-light) so unstructured
19
+ preparation can raise/catch it without importing the heavy flash.serve.preflight module."""
20
+
16
21
  if TYPE_CHECKING:
17
22
  from flash.spec import JobSpec
18
23