freesolo-flash-dev 0.2.47__tar.gz → 0.2.49__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 (297) hide show
  1. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/commands.py +7 -5
  3. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/training_doc.py +22 -19
  4. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/analytical.py +2 -2
  5. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/spec.py +67 -17
  6. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/types.py +5 -5
  7. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/recipe.py +4 -5
  8. freesolo_flash_dev-0.2.49/flash/engine/steps.py +18 -0
  9. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/vram.py +73 -20
  10. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/grpo.py +2 -2
  11. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd.py +269 -252
  12. freesolo_flash_dev-0.2.49/flash/engine/worker/opd_vllm.py +274 -0
  13. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/lifecycle.py +13 -1
  14. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/memory.py +1 -1
  15. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/rl.py +24 -9
  16. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/lora_rank.py +8 -7
  17. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_worker.py +0 -4
  18. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/schema/__init__.py +22 -23
  19. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/spec.py +4 -6
  20. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/pyproject.toml +2 -2
  21. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_agent_flash_cli_contract.py +2 -1
  22. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_algorithms.py +4 -4
  23. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_allocator.py +456 -61
  24. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_b200_rollout_opt.py +1 -1
  25. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_backend_jobspec_contract.py +7 -5
  26. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_billing_retry.py +1 -1
  27. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_charge_pricing.py +1 -1
  28. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_checkpoints.py +2 -2
  29. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_commands.py +8 -6
  30. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_errors.py +5 -3
  31. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_estimate.py +33 -17
  32. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_managed.py +2 -1
  33. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_client_server_integration.py +1 -1
  34. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_config_overrides.py +9 -5
  35. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_analytical.py +27 -14
  36. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_publish.py +1 -1
  37. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_rate_limit_resolve.py +1 -1
  38. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flash_mvp.py +4 -2
  39. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flash_worker.py +8 -8
  40. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_gpus.py +7 -6
  41. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_params.py +66 -29
  42. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_sleep_gate.py +4 -4
  43. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_jobs.py +72 -17
  44. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_lora_rank_preflight.py +2 -3
  45. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_oom_escalate_gpu.py +10 -1
  46. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_opd.py +375 -82
  47. freesolo_flash_dev-0.2.49/tests/test_opd_vllm.py +184 -0
  48. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_open_model_policy.py +1 -1
  49. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_orchestrator_flash.py +1 -1
  50. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runmgmt.py +2 -2
  51. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_api.py +13 -13
  52. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_billing.py +1 -1
  53. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_spec_and_validation.py +11 -8
  54. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_train_context_preflight.py +19 -14
  55. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_weight_cache.py +1 -1
  56. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_dryrun.py +13 -0
  57. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/uv.lock +1 -1
  58. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.dockerignore +0 -0
  59. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.env.example +0 -0
  60. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/auto-rebake.yml +0 -0
  61. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/bake-kernel-cache.yml +0 -0
  62. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/ci.yml +0 -0
  63. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/main-source-guard.yml +0 -0
  64. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/notify-tests-repo.yml +0 -0
  65. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-dev.yml +0 -0
  66. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-image.yml +0 -0
  67. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/publish.yml +0 -0
  68. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/version-parity.yml +0 -0
  69. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.github/workflows/worker-image.yml +0 -0
  70. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/.gitignore +0 -0
  71. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/Dockerfile +0 -0
  72. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/Dockerfile.worker +0 -0
  73. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/LICENSE +0 -0
  74. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/README.md +0 -0
  75. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.gitignore +0 -0
  76. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.keep +0 -0
  77. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache +0 -0
  78. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache.relayer +0 -0
  79. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/bake_kernel_cache.py +0 -0
  80. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/bake_pod_entry.py +0 -0
  81. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/kernel_fingerprint.py +0 -0
  82. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/docker/make_rp_handler.py +0 -0
  83. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/__init__.py +0 -0
  84. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_channel.py +0 -0
  85. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_fileio.py +0 -0
  86. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_logging.py +0 -0
  87. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/_update_check.py +0 -0
  88. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/adapter_artifacts.py +0 -0
  89. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/catalog.py +0 -0
  90. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/__init__.py +0 -0
  91. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/__main__.py +0 -0
  92. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/_tty.py +0 -0
  93. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/envpush.py +0 -0
  94. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cli/render.py +0 -0
  95. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/__init__.py +0 -0
  96. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/config.py +0 -0
  97. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/http.py +0 -0
  98. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/runtime_secrets.py +0 -0
  99. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/client/specs.py +0 -0
  100. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/__init__.py +0 -0
  101. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/cost/facts.py +0 -0
  102. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/__init__.py +0 -0
  103. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/accounting.py +0 -0
  104. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/chalk_kernels.py +0 -0
  105. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/multiturn_rollout.py +0 -0
  106. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__init__.py +0 -0
  107. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__main__.py +0 -0
  108. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/_pkg.py +0 -0
  109. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/adapter.py +0 -0
  110. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/decoding.py +0 -0
  111. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/finalize.py +0 -0
  112. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/gpu_setup.py +0 -0
  113. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/heartbeat.py +0 -0
  114. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/hf.py +0 -0
  115. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/kernel_warmup.py +0 -0
  116. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/lora.py +0 -0
  117. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd_gkd.py +0 -0
  118. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/packing.py +0 -0
  119. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/__init__.py +0 -0
  120. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/attn.py +0 -0
  121. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/diagnostics.py +0 -0
  122. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/liger.py +0 -0
  123. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/loraplus.py +0 -0
  124. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/sft.py +0 -0
  125. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/teacher.py +0 -0
  126. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/tokenizer_align.py +0 -0
  127. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/engine/worker/wandb_log.py +0 -0
  128. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/__init__.py +0 -0
  129. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/adapter.py +0 -0
  130. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/archive_policy.py +0 -0
  131. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/base.py +0 -0
  132. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/loader.py +0 -0
  133. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/pull.py +0 -0
  134. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/envs/registry.py +0 -0
  135. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/__init__.py +0 -0
  136. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_auth.py +0 -0
  137. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_artifacts.py +0 -0
  138. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_retry.py +0 -0
  139. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_http.py +0 -0
  140. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance.py +0 -0
  141. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_bootstrap.py +0 -0
  142. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_poll.py +0 -0
  143. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_provider.py +0 -0
  144. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/_poll.py +0 -0
  145. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/allocator.py +0 -0
  146. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/base.py +0 -0
  147. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/__init__.py +0 -0
  148. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/api.py +0 -0
  149. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/auth.py +0 -0
  150. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/gpus.py +0 -0
  151. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  152. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  153. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/preflight.py +0 -0
  154. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/pricing.py +0 -0
  155. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/preflight.py +0 -0
  156. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/realized.py +0 -0
  157. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/__init__.py +0 -0
  158. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/api.py +0 -0
  159. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/auth.py +0 -0
  160. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/cost.py +0 -0
  161. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/gpus.py +0 -0
  162. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/jobs.py +0 -0
  163. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/keys.py +0 -0
  164. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preflight.py +0 -0
  165. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preload.py +0 -0
  166. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/pricing.py +0 -0
  167. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/slots.py +0 -0
  168. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/__init__.py +0 -0
  169. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/deps.py +0 -0
  170. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/endpoints.py +0 -0
  171. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/__init__.py +0 -0
  172. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/api.py +0 -0
  173. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/auth.py +0 -0
  174. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/__init__.py +0 -0
  175. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/builders.py +0 -0
  176. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/preflight.py +0 -0
  177. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/providers/vast/pricing.py +0 -0
  178. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/py.typed +0 -0
  179. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/__init__.py +0 -0
  180. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/checkpoints.py +0 -0
  181. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/deploy.py +0 -0
  182. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/runner/lifecycle.py +0 -0
  183. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/schema/fields.py +0 -0
  184. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/__init__.py +0 -0
  185. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/deploy.py +0 -0
  186. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/export.py +0 -0
  187. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/serve/pricing.py +0 -0
  188. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/__init__.py +0 -0
  189. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/__main__.py +0 -0
  190. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_deps.py +0 -0
  191. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_internal_client.py +0 -0
  192. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_locks.py +0 -0
  193. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/_runtime.py +0 -0
  194. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/app.py +0 -0
  195. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/auth.py +0 -0
  196. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/billing.py +0 -0
  197. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/billing_retry.py +0 -0
  198. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/checkpoints.py +0 -0
  199. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/db.py +0 -0
  200. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/environment_registry.py +0 -0
  201. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/envs.py +0 -0
  202. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/reconcile.py +0 -0
  203. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/__init__.py +0 -0
  204. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/envs.py +0 -0
  205. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/meta.py +0 -0
  206. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/runs.py +0 -0
  207. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/routes/serving.py +0 -0
  208. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/flash/server/run_registry.py +0 -0
  209. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/infisical-entrypoint.sh +0 -0
  210. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/scripts/build_dev_dist.py +0 -0
  211. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/__init__.py +0 -0
  212. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/__init__.py +0 -0
  213. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/runner.py +0 -0
  214. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/specs.py +0 -0
  215. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/_helpers/vast.py +0 -0
  216. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/conftest.py +0 -0
  217. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_eval.jsonl +0 -0
  218. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_train.jsonl +0 -0
  219. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/__init__.py +0 -0
  220. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/conftest.py +0 -0
  221. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_lambda_live.py +0 -0
  222. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_runpod_live.py +0 -0
  223. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/live/test_vast_live.py +0 -0
  224. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cancel_remote.py +0 -0
  225. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_catalog_consistency.py +0 -0
  226. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_chalk_kernels.py +0 -0
  227. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_help.py +0 -0
  228. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cli_render_theme.py +0 -0
  229. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_client.py +0 -0
  230. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_equation.py +0 -0
  231. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_estimate.py +0 -0
  232. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_hardware.py +0 -0
  233. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_models.py +0 -0
  234. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_cost_rewards.py +0 -0
  235. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_dev_channel.py +0 -0
  236. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_disk_gb.py +0 -0
  237. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_endpoint_name.py +0 -0
  238. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_cache_evict.py +0 -0
  239. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_delete.py +0 -0
  240. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_download.py +0 -0
  241. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_download_api.py +0 -0
  242. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_no_install.py +0 -0
  243. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_pull.py +0 -0
  244. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_pull_managed_control_plane.py +0 -0
  245. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_env_push.py +0 -0
  246. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_envs_coverage.py +0 -0
  247. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_export.py +0 -0
  248. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_flashinfer_cache_dirs.py +0 -0
  249. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_github_urlopen_retry.py +0 -0
  250. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_grpo_mask_aware.py +0 -0
  251. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_health_liveness.py +0 -0
  252. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_idle_endpoint_reaper.py +0 -0
  253. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_internal_client.py +0 -0
  254. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kernel_cache.py +0 -0
  255. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kernel_fingerprint.py +0 -0
  256. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_kv_util.py +0 -0
  257. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_lambda_runner.py +0 -0
  258. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_logging.py +0 -0
  259. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_login_perms.py +0 -0
  260. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_managed_hf_repo.py +0 -0
  261. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_metrics_schema_agent_contract.py +0 -0
  262. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_mig_guard.py +0 -0
  263. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_multiturn_rollout.py +0 -0
  264. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_packing.py +0 -0
  265. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_poll_helpers.py +0 -0
  266. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_preflight.py +0 -0
  267. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_provider_routing.py +0 -0
  268. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_provider_teardown_robustness.py +0 -0
  269. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_providers_symmetry.py +0 -0
  270. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_realized_cost.py +0 -0
  271. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_resolve_params_b.py +0 -0
  272. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_resume_on_retry.py +0 -0
  273. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_api_delete.py +0 -0
  274. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_fingerprint.py +0 -0
  275. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_waterfall.py +0 -0
  276. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_runpod_slots.py +0 -0
  277. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_serve.py +0 -0
  278. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_server_db.py +0 -0
  279. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_serving_contract.py +0 -0
  280. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_sft_example_selection.py +0 -0
  281. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_sft_gc_off.py +0 -0
  282. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_thinking_config.py +0 -0
  283. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_update_check.py +0 -0
  284. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_api.py +0 -0
  285. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_offers.py +0 -0
  286. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vast_runner.py +0 -0
  287. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_verifiers.py +0 -0
  288. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_version.py +0 -0
  289. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  290. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_recombine.py +0 -0
  291. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_wandb_naming.py +0 -0
  292. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_warmstart_cross_repo.py +0 -0
  293. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_hardexit.py +0 -0
  294. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_image.py +0 -0
  295. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_init_heartbeat.py +0 -0
  296. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/tests/test_worker_stack.py +0 -0
  297. {freesolo_flash_dev-0.2.47 → freesolo_flash_dev-0.2.49}/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: 0.2.47
3
+ Version: 0.2.49
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
@@ -447,11 +447,11 @@ def cmd_env_setup(args) -> int:
447
447
  '# secrets = ["SERPAPI_API_KEY"]\n\n'
448
448
  )
449
449
  # `thinking = true` opts the run into reasoning mode. Reasoning shares the generation budget with
450
- # the answer, so GRPO also gets a raised max_tokens. These strings are empty when reasoning is
451
- # off, keeping the default scaffold byte-for-byte identical.
450
+ # the answer, so GRPO also gets a raised completion budget. These strings are empty when reasoning
451
+ # is off, keeping the default scaffold byte-for-byte identical.
452
452
  thinking_line = "thinking = true\n" if reasoning else ""
453
453
  rl_reasoning_train = (
454
- "max_tokens = 2048 # reasoning shares this budget with the answer; raised so it isn't truncated\n"
454
+ "max_completion_tokens = 2048 # reasoning shares this budget with the answer; raised so it isn't truncated\n"
455
455
  if reasoning
456
456
  else ""
457
457
  )
@@ -469,7 +469,8 @@ def cmd_env_setup(args) -> int:
469
469
  "\n"
470
470
  f"{env_comment}"
471
471
  "[train]\n"
472
- "steps = 150\n"
472
+ "epochs = 1\n"
473
+ "max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
473
474
  f"{rl_reasoning_train}"
474
475
  "lora_rank = 32\n"
475
476
  "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
@@ -511,7 +512,8 @@ def cmd_env_setup(args) -> int:
511
512
  "[environment]\n"
512
513
  'id = ""\n\n'
513
514
  "[train]\n"
514
- "steps = 100 # opd is step-driven (like GRPO)\n"
515
+ "epochs = 1\n"
516
+ "max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
515
517
  "lora_rank = 32\n"
516
518
  "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
517
519
  "# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
@@ -103,7 +103,7 @@ id = "your-org/my-env" # the id printed by `flash env push`
103
103
  # never stored in the spec
104
104
 
105
105
  [train]
106
- epochs = 1 # SFT is epoch-driven; GRPO is step-driven (steps = N)
106
+ epochs = 1 # one pass over the retained train rows
107
107
  max_examples = 2 # rows to train on (the starter dataset has 2)
108
108
  lora_rank = 32
109
109
  lora_alpha = 64
@@ -114,7 +114,7 @@ GPU and HF artifacts are **fully managed** — do not pick `gpu.type` or set
114
114
  `train.hf_repo`; the allocator picks the cheapest validated managed GPU class that fits,
115
115
  and run artifacts are stored in a private environment-scoped repo with content-addressed
116
116
  Flash code snapshots. Compose or tweak configs without editing files: `--config
117
- extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.steps=300`).
117
+ extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.epochs=3`).
118
118
 
119
119
  ### 4. Submit
120
120
 
@@ -201,20 +201,20 @@ spending another GPU run:
201
201
  | --- | --- | --- |
202
202
  | Environment id is blank or stale | `flash train --dry-run` fails, or the worker uses old reward/data | Run `flash env push --name my-env .` after every environment/data edit and paste the returned id into every config you submit. |
203
203
  | Local-only env path in config | Config validation says there is no local path mode | Publish first, then use the returned slug in `[environment] id`. `flash train` only runs published env ids, not local paths. |
204
- | Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `steps`, `epochs`, `group_size`, `max_tokens`, `temperature`, `max_length`, LoRA, and other training knobs under `[train]`. |
204
+ | Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `epochs`, `group_size`, `max_completion_tokens`, `temperature`, `max_context_tokens`, LoRA, and other training knobs under `[train]`. |
205
205
  | Trying to pin managed infrastructure | `gpu.type`, `train.hf_repo`, or `model_policy` changes do not do what you expected | Treat GPU choice, model policy, and the run artifact repo as managed. Tune the model, algorithm, environment, and `[train]` knobs instead. |
206
206
  | Secrets are not available on the worker | Reward code works locally but remote logs show missing API keys or auth failures | List secret names under `[environment] secrets = [...]`, export those env vars locally before submit, or put them in local `.env` / `.env.local`. Never put secret values in `[worker_env]` or hard-code them in the config. |
207
207
  | Wrong model / thinking setting | Config validation fails, or chat behavior does not match the run | Use `flash models`; set `thinking = true` only for supported models. Thinking is a training-time/run-level choice and serving preserves that parity, so `flash chat` does not expose an override flag. |
208
208
  | Thinking reward grades the wrong text | Rewards accidentally score hidden reasoning, or ignore reasoning you meant to inspect | By default, score the answer text. In thinking mode the response object is still string-compatible, but also exposes `.completion`, `.thinking`, and `.raw` when a reward intentionally needs those fields. |
209
209
  | All-zero or flat GRPO reward | `reward_mean` stays near 0 and outputs do not improve | Make the reward dense: give partial credit for parse/format/execution/correctness tiers, and log a separate clean `success` metric. Do not keep rerunning an all-zero reward. |
210
210
  | Reward rises but behavior is worse | Short, templated, malformed, or reward-hacked outputs score well | Deploy the adapter and probe real examples. Add hard validity gates before judge calls, penalize degenerate shortcuts, and judge the outcome rather than the surface string. |
211
- | Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_tokens` for GRPO rollouts or `max_length` for SFT only after seeing truncation. Oversizing them by default just burns memory/cost. |
212
- | Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash log <run-id>`, reduce footprint (`max_length`, `max_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
211
+ | Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_completion_tokens` for GRPO/OPD rollouts or `max_context_tokens` for total prompt+completion context only after seeing truncation. Oversizing them by default just burns memory/cost. |
212
+ | Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash log <run-id>`, reduce footprint (`max_context_tokens`, `max_completion_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
213
213
  | Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash log <run-id> --follow` to reattach, `flash log <run-id>` for the console/error output, or `flash cancel <run-id>` if you intentionally want to stop it. |
214
214
  | Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id>/step-N`, and compare with held-out probes before exporting or relying on the final adapter. |
215
215
  | Export fails before upload | CLI says no HuggingFace token | Pass `flash export --api-key hf_...`, or set `HF_TOKEN` in your shell, `.env`, or `.env.local`. Exports are private unless you pass `--public`. |
216
216
  | SFT loss improves but quality does not | Train loss falls while held-out behavior stalls or degrades | Keep a held-out split outside training. Deploy and score that split; if quality drops, reduce epochs or improve data instead of adding more passes. |
217
- | Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, cap steps/epochs for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
217
+ | Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, keep `epochs` and `max_examples` small for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
218
218
 
219
219
  ---
220
220
 
@@ -335,8 +335,8 @@ Pick SFT when you already have good answers and want the model to imitate them.
335
335
  split the run never trains on, then **deploy the adapter and score it on that split**
336
336
  (`flash deploy` + `flash chat`). If held-out quality stalls or drops while train loss
337
337
  keeps falling, reduce `epochs` or add more data — not more passes.
338
- - **Start `max_length` small and grow it on evidence.** Begin from the smallest
339
- `max_length` that plausibly fits prompt + completion, and only raise it when you see
338
+ - **Start `max_context_tokens` small and grow it on evidence.** Begin from the smallest
339
+ `max_context_tokens` that plausibly fits prompt + completion, and only raise it when you see
340
340
  truncation (outputs cut off mid-thought, degraded loss). A bigger context just costs
341
341
  more.
342
342
  - **For Qwen3.5 thinking multi-turn SFT, put reasoning only in the final assistant
@@ -370,7 +370,9 @@ lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effec
370
370
  lora_alpha = 32
371
371
  ```
372
372
 
373
- SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
373
+ SFT, GRPO, and OPD all accept **epoch-driven** configs (`epochs`). For GRPO/OPD,
374
+ an epoch is one pass over the retained prompt pool after `max_examples` and prompt-budget filtering;
375
+ optimizer-step counts are derived from those epochs.
374
376
 
375
377
  ---
376
378
 
@@ -379,8 +381,8 @@ SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
379
381
  Pick distillation when a much stronger **teacher** model can grade your student's work
380
382
  token-by-token. The student samples on-policy (like GRPO), the managed GLM 5.2 teacher scores
381
383
  each of *its own* completions, and a dense per-token loss teaches the student to match the teacher —
382
- far more sample-efficient than reward-based RL and with no reward to design. It is **step-driven**
383
- (`steps`) and produces a LoRA served exactly like SFT.
384
+ far more sample-efficient than reward-based RL and with no reward to design. It supports `epochs`
385
+ like SFT/GRPO and produces a LoRA served exactly like SFT.
384
386
 
385
387
  - **No teacher key or model override to set up.** The GLM 5.2 teacher and its Fireworks key are platform-managed: the
386
388
  service supplies its own key to every opd run, so there is nothing to export or declare — an opd
@@ -397,7 +399,7 @@ far more sample-efficient than reward-based RL and with no reward to design. It
397
399
  (driving `step_episode` / observations just like GRPO) and distils EVERY assistant turn against the
398
400
  teacher, each conditioned on the transcript up to that turn — the episode's total reverse-KL over
399
401
  the student's generated tokens is the sum of its per-turn reverse-KLs. Env/observation tokens are
400
- never distilled (they're context, not the student's output). Set `[train] max_length` to bound the
402
+ never distilled (they're context, not the student's output). Set `[train] max_context_tokens` to bound the
401
403
  transcript; the teacher must cover it (GLM-5.2's context far exceeds the default budget).
402
404
  - **Judge it like SFT.** Distillation logs a falling per-token loss; a low loss alone is not proof.
403
405
  Keep a held-out split, `flash deploy` the adapter, and score it — confirm the student actually
@@ -412,7 +414,8 @@ algorithm = "opd"
412
414
  id = "your-org/my-env"
413
415
 
414
416
  [train]
415
- steps = 100
417
+ epochs = 1
418
+ max_examples = 2
416
419
  lora_rank = 32
417
420
  # kl_penalty_coef = 1.0 # reverse-KL scale
418
421
  ```
@@ -427,7 +430,7 @@ in a sensible value, so only override with a reason.
427
430
  | Knob | Convention |
428
431
  | --- | --- |
429
432
  | `group_size` | Completions sampled per prompt (default 8). More = more signal and more cost; drop to 4 to trim cost. The group needs *within-group variance* for an advantage to exist. |
430
- | `max_tokens` | Completion budget per rollout. Size it to the expected output length — too small silently truncates good answers and poisons the reward; too large just costs more. |
433
+ | `max_completion_tokens` | Completion budget per rollout. Size it to the expected output length — too small silently truncates good answers and poisons the reward; too large just costs more. |
431
434
  | `temperature` | Rollout sampling temperature. Keep it near 1.0 for GRPO — too low collapses diversity (and the model can collapse within a few steps); raise it to widen exploration against uniform-reward groups. |
432
435
  | `kl_penalty_coef` | Keeps the trained model from drifting too far from the base. Raise it to anchor against entropy collapse; lower it for more freedom to move. |
433
436
  | `thinking_length_penalty_coef` | Per-reasoning-token reward deduction — curb overthinking, but watch it doesn't push the model into terse degeneracy. |
@@ -449,7 +452,7 @@ rollouts score somewhere between all-fail and all-pass — is where GRPO has the
449
452
  signal.
450
453
 
451
454
  - If nearly every prompt is solved (most groups score ~1.0): **increase difficulty** —
452
- harder prompts, tighter format/reward, more steps.
455
+ harder prompts, tighter format/reward, more epochs, or more data.
453
456
  - If nearly nothing is solved (most groups score ~0.0): **decrease difficulty** —
454
457
  easier or few-shot prompts, a more lenient (denser) reward, or warm-start with SFT.
455
458
  - In between: good signal — keep iterating at this difficulty.
@@ -468,7 +471,7 @@ targeted fix rather than leaning on the reward gate to slowly select against it.
468
471
  | --- | --- | --- |
469
472
  | Repetition / looping collapse | the same phrase repeats until truncation | repetition or length penalty; lower `temperature` |
470
473
  | Overthinking / verbose reasoning | reasoning eats the whole token budget | `thinking_length_penalty_coef`; tighten the prompt |
471
- | Max-token truncation | answers cut off mid-thought | raise `max_tokens` / `max_length` |
474
+ | Completion truncation | answers cut off mid-thought | raise `max_completion_tokens` / `max_context_tokens` |
472
475
  | Unparsed / over-escaped output | reward can't read the answer | robust parser; return `0.0` on parse fail; format gate |
473
476
  | Wrapper / markdown around structured output | prose around the JSON/answer | a format gate; `stop_sequences` |
474
477
  | Uniform-reward groups | every rollout in a group scores the same → no gradient | shape the reward for partial credit; raise `temperature` |
@@ -500,8 +503,8 @@ on a beyond-noise improvement.
500
503
 
501
504
  ## Scale the evidence
502
505
 
503
- - **A smoke test is not proof.** A single-digit `steps`, a tiny dataset, or a handful
504
- of rollouts only validates the wiring. Scale `steps` / `epochs`, the dataset size,
506
+ - **A smoke test is not proof.** A single-digit derived step count, a tiny dataset, or a handful
507
+ of rollouts only validates the wiring. Scale `epochs`, the dataset size,
505
508
  and `group_size` to the model and the data you actually have before you trust a
506
509
  result. Don't cite budget alone as the reason for an underpowered run.
507
510
  - **Use the data you have.** Deliberately assign every usable row to training or to a
@@ -513,7 +516,7 @@ on a beyond-noise improvement.
513
516
  ## Treat crashes as infra, not model size
514
517
 
515
518
  > A CUDA / OOM / vLLM / kernel / infrastructure error is an **infrastructure** problem, not a
516
- > sign the model is too big. Lower `max_length`, `max_tokens`, or `group_size` to shrink
519
+ > sign the model is too big. Lower `max_context_tokens`, `max_completion_tokens`, or `group_size` to shrink
517
520
  > the run's footprint and let the allocator retry onto the next fitting GPU class — do
518
521
  > **not** switch to a smaller model to make a crash disappear. That silently destroys
519
522
  > quality.
@@ -73,11 +73,11 @@ def _fmt_duration(seconds: float) -> str:
73
73
 
74
74
  def setup_seconds(config: RunConfig) -> float:
75
75
  """Cold-start wall time before the first optimizer step: container boot + deps + model load
76
- (a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for GRPO.
76
+ (a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for rollouts.
77
77
  This elapsed setup time is reported but not included in customer-facing cost."""
78
78
  model_load = MODEL_LOAD_BASE_S + download_weight_gb(config.model_id) / DOWNLOAD_RATE_GBPS
79
79
  s = WORKER_BOOT_S + DEPS_INSTALL_S + model_load
80
- if config.is_grpo:
80
+ if config.has_rollout:
81
81
  s += VLLM_INIT_S
82
82
  return s
83
83
 
@@ -10,6 +10,7 @@ import math
10
10
  from flash.catalog import samples_on_policy
11
11
  from flash.cost.analytical import estimate_cost
12
12
  from flash.cost.types import CostEstimate, RunConfig
13
+ from flash.engine.steps import on_policy_steps
13
14
 
14
15
 
15
16
  def _sft_epochs(spec) -> int:
@@ -19,13 +20,21 @@ def _sft_epochs(spec) -> int:
19
20
  return int(t.epochs) if t.epochs is not None else RECIPE.sft.num_epochs
20
21
 
21
22
 
23
+ def _on_policy_epochs(spec) -> int:
24
+ from flash.engine.recipe import RECIPE
25
+
26
+ t = spec.train
27
+ default = RECIPE.rl.num_epochs if spec.algorithm == "grpo" else RECIPE.opd.num_epochs
28
+ return int(t.epochs) if t.epochs is not None else default
29
+
30
+
22
31
  def _sft_seq_len(spec) -> int:
23
32
  from flash.engine.recipe import RECIPE
24
33
 
25
34
  t = spec.train
26
35
  return (
27
- int(t.max_length)
28
- if t.max_length is not None
36
+ int(t.max_context_tokens)
37
+ if t.max_context_tokens is not None
29
38
  else (RECIPE.sft.max_seq_len_thinking if spec.thinking else RECIPE.sft.max_seq_len)
30
39
  )
31
40
 
@@ -41,6 +50,43 @@ def _sft_example_count(spec) -> int:
41
50
  )
42
51
 
43
52
 
53
+ def _on_policy_example_count(spec) -> int:
54
+ t = spec.train
55
+ pinned_examples = int(t.max_examples) if t.max_examples else 0
56
+ if pinned_examples > 0:
57
+ return pinned_examples
58
+ env_examples = _env_max_examples(spec)
59
+ if env_examples > 0:
60
+ return env_examples
61
+ return _on_policy_requested_prompts_per_step(spec)
62
+
63
+
64
+ def _env_max_examples(spec) -> int:
65
+ params = getattr(getattr(spec, "environment", None), "params", {}) or {}
66
+ if not isinstance(params, dict):
67
+ return 0
68
+ try:
69
+ value = int(params.get("max_examples") or 0)
70
+ except (TypeError, ValueError):
71
+ return 0
72
+ return max(0, value)
73
+
74
+
75
+ def _on_policy_requested_prompts_per_step(spec) -> int:
76
+ from flash.engine.recipe import RECIPE
77
+
78
+ t = spec.train
79
+ default = (
80
+ RECIPE.rl.prompts_per_step if spec.algorithm == "grpo" else RECIPE.opd.prompts_per_step
81
+ )
82
+ return max(1, int(t.batch_size) if t.batch_size is not None else default)
83
+
84
+
85
+ def _on_policy_prompts_per_step(spec, examples: int) -> int:
86
+ requested = _on_policy_requested_prompts_per_step(spec)
87
+ return max(1, min(requested, max(1, int(examples))))
88
+
89
+
44
90
  def _sft_realized_batch(spec) -> int:
45
91
  from flash.catalog import vocab_size_for
46
92
  from flash.engine.recipe import RECIPE
@@ -68,22 +114,26 @@ def _sft_steps_from_examples(spec, examples: int, *, apply_cap: bool) -> int:
68
114
 
69
115
 
70
116
  def spec_steps(spec) -> int:
71
- """Per-seed optimizer steps implied by a train spec (mirrors the worker). GRPO: ``train.steps``
72
- (else recipe default). SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by
73
- ``max_steps``, where ``num_examples`` must be pinned by ``max_examples``."""
74
- from flash.engine.recipe import RECIPE
117
+ """Per-seed optimizer steps implied by a train spec (mirrors the worker).
75
118
 
76
- t = spec.train
119
+ SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by ``max_steps``. GRPO/OPD:
120
+ ``epochs`` means passes over ``max_examples`` rows when pinned, otherwise the estimate uses one
121
+ prompt batch per epoch.
122
+ """
77
123
  if spec.algorithm == "grpo":
78
- if t.steps is not None:
79
- return max(1, int(t.steps))
80
- return RECIPE.rl.num_steps
124
+ examples = _on_policy_example_count(spec)
125
+ return on_policy_steps(
126
+ epochs=_on_policy_epochs(spec),
127
+ prompt_count=examples,
128
+ prompts_per_step=_on_policy_prompts_per_step(spec, examples),
129
+ )
81
130
  if spec.algorithm == "opd":
82
- # Step-driven like GRPO (on-policy sampling), NOT example-driven — so a opd quote never
83
- # demands [train].max_examples (the SFT fallback below would raise).
84
- if t.steps is not None:
85
- return max(1, int(t.steps))
86
- return RECIPE.opd.num_steps
131
+ examples = _on_policy_example_count(spec)
132
+ return on_policy_steps(
133
+ epochs=_on_policy_epochs(spec),
134
+ prompt_count=examples,
135
+ prompts_per_step=_on_policy_prompts_per_step(spec, examples),
136
+ )
87
137
  # max_examples is a CAP; 0 (like None) means "no cap" (worker trains the full dataset), so
88
138
  # don't let max_examples=0 price a single step.
89
139
  return _sft_steps_from_examples(spec, _sft_example_count(spec), apply_cap=True)
@@ -101,8 +151,8 @@ def runconfig_from_spec(spec) -> RunConfig:
101
151
  model_id=spec.model,
102
152
  method=spec.algorithm,
103
153
  steps=spec_steps(spec),
104
- seq_len=t.max_length,
105
- completion_len=t.max_tokens if has_rollout else None,
154
+ seq_len=t.max_context_tokens,
155
+ completion_len=t.max_completion_tokens if has_rollout else None,
106
156
  batch_size=t.batch_size,
107
157
  group_size=t.group_size if has_rollout else None,
108
158
  lora_rank=t.lora_rank,
@@ -17,10 +17,10 @@ class RunConfig:
17
17
  method: str # "sft" | "grpo" | "opd"
18
18
  steps: int
19
19
 
20
- # Engine context length (forwarded as [train].max_length, NOT prompt length). When unset the
20
+ # Engine context length (forwarded as [train].max_context_tokens, NOT prompt length). When unset the
21
21
  # GRPO default mirrors the worker's max(1024, max_prompt_len + completion); see normalized().
22
22
  seq_len: int | None = None
23
- completion_len: int | None = None # GRPO only (max_tokens)
23
+ completion_len: int | None = None # GRPO/OPD only (max_completion_tokens)
24
24
  batch_size: int | None = None # SFT effective batch / GRPO prompts_per_step
25
25
  group_size: int | None = None # GRPO completions per prompt (G)
26
26
  lora_rank: int | None = None
@@ -82,7 +82,7 @@ class RunConfig:
82
82
  comp = self.completion_len
83
83
  if comp is None:
84
84
  comp = rc.max_completion_len_thinking if self.thinking else rc.max_completion_len
85
- # Explicit pin wins; else mirror the allocator's rollout sizing of an unset max_length:
85
+ # Explicit pin wins; else mirror the allocator's rollout sizing of an unset context:
86
86
  # max(1024, max_prompt_len + completion), not bare max_prompt_len (which under-sizes).
87
87
  seq = (
88
88
  self.seq_len
@@ -116,9 +116,9 @@ class RunConfig:
116
116
  if self.batch_size is not None:
117
117
  knobs["batch_size"] = self.batch_size
118
118
  if n.seq_len is not None:
119
- knobs["max_length"] = n.seq_len
119
+ knobs["max_context_tokens"] = n.seq_len
120
120
  if n.completion_len is not None:
121
- knobs["max_tokens"] = n.completion_len
121
+ knobs["max_completion_tokens"] = n.completion_len
122
122
  if n.group_size is not None:
123
123
  knobs["group_size"] = n.group_size
124
124
  return knobs
@@ -36,11 +36,11 @@ class RLConfig:
36
36
  learning_rate: float = 1e-5
37
37
  max_prompt_len: int = 2048
38
38
  max_completion_len: int = 320
39
- # 320 is too short for <think> blocks; overridable via [train].max_tokens.
39
+ # 320 is too short for <think> blocks; overridable via [train].max_completion_tokens.
40
40
  max_completion_len_thinking: int = 1536
41
41
  prompts_per_step: int = 64
42
42
  group_size: int = 8
43
- num_steps: int = 150
43
+ num_epochs: int = 1
44
44
  sampling_temperature: float = 1.0
45
45
  sampling_top_p: float = 1.0
46
46
 
@@ -54,17 +54,16 @@ class OPDConfig:
54
54
  # platform-managed and intentionally not configurable from [train].
55
55
  teacher_model: str = "accounts/fireworks/models/glm-5p2"
56
56
  teacher_base_url: str = "https://api.fireworks.ai/inference/v1"
57
- # OPD is step-driven like GRPO (on-policy sampling), not epoch-driven like SFT.
58
- num_steps: int = 100
59
57
  learning_rate: float = 1e-5
60
58
  max_prompt_len: int = 1024
61
59
  max_completion_len: int = 512
62
- # 512 is short for <think> traces; overridable via [train].max_tokens.
60
+ # 512 is short for <think> traces; overridable via [train].max_completion_tokens.
63
61
  max_completion_len_thinking: int = 1536
64
62
  prompts_per_step: int = 8
65
63
  # Student samples per prompt. 1 is enough for a direct KD loss (no group-relative baseline
66
64
  # as in GRPO); raise for more teacher-scored coverage per prompt at higher cost.
67
65
  group_size: int = 1
66
+ num_epochs: int = 1
68
67
  sampling_temperature: float = 1.0
69
68
  sampling_top_p: float = 1.0
70
69
  # Reverse-KL coefficient for the groupwise reverse-KL loss; scales the per-span
@@ -0,0 +1,18 @@
1
+ """Training-step derivation shared by worker and cost-estimate paths."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+
7
+
8
+ def on_policy_steps(
9
+ *,
10
+ epochs: int,
11
+ prompt_count: int,
12
+ prompts_per_step: int,
13
+ ) -> int:
14
+ """Resolve GRPO/OPD optimizer steps from full passes over the retained prompt pool."""
15
+ prompt_count = int(prompt_count)
16
+ if prompt_count <= 0:
17
+ raise ValueError("cannot derive epoch-based steps without at least one retained prompt")
18
+ return max(1, math.ceil(prompt_count * int(epochs) / max(1, int(prompts_per_step))))
@@ -96,6 +96,20 @@ def opd_rollout_seq_len(
96
96
  return int(max_length or max(1024, RECIPE.opd.max_prompt_len + completion))
97
97
 
98
98
 
99
+ def opd_rollout_concurrency(prompts_per_step: int = 1, group_size: int = 1) -> int:
100
+ """Concurrent student generations in one OPD vLLM rollout batch."""
101
+
102
+ def _positive_int(value: object, default: int) -> int:
103
+ try:
104
+ if isinstance(value, bool):
105
+ return default
106
+ return max(1, int(value))
107
+ except (TypeError, ValueError):
108
+ return default
109
+
110
+ return _positive_int(prompts_per_step, 1) * _positive_int(group_size, 1)
111
+
112
+
99
113
  def _resident_kv_gb(
100
114
  params_b: float | None, vllm_max_len: int, num_generations: int = 8, fp8_kv: bool = False
101
115
  ) -> float:
@@ -188,6 +202,12 @@ def colocate_kv_util(
188
202
  _TRAIN_COEF = 0.27
189
203
  # Small-model colocated GRPO floor: 0.8B OOMs 20 GB; 2B OOMs 24 GB -> both need 32 GB tier.
190
204
  _VLLM_COLOCATE_FLOOR_GB = 28.0
205
+ # OPD builds its resident vLLM engine after the HF/PEFT student is already loaded. A real
206
+ # Qwen3.5-2B OPD run with vLLM failed vLLM startup on a 32 GB RTX 5090 despite the raw equation
207
+ # estimating ~28 GB with headroom, because vLLM requires the requested executor budget to be free at
208
+ # init time. Keep 2B+ OPD off <=40 GB classes so the allocator picks an 80 GB-class card instead of a
209
+ # consumer GPU that passes the aggregate estimate but fails the vLLM free-memory preflight.
210
+ _OPD_VLLM_COLOCATE_FLOOR_GB = 41.0
191
211
  _VOCAB_DEFAULT = 248_320
192
212
  _LOGITS_BUDGET_GB = 6.0
193
213
  # 16 B/elem: fp32 logits+grad + bf16 logits+grad + CE temp. 8 B/elem under-counts (live OOM confirmed).
@@ -328,6 +348,14 @@ def estimate_vram_gb(
328
348
  train = activations + logits
329
349
  return base + (max(rollout, train) if sleep_offload else rollout + train)
330
350
  if is_opd:
351
+ # OPD always samples the student through a resident colocated vLLM engine, while the HF/PEFT
352
+ # trainer model stays resident for the GKD loss forward/backward. Budget the second bf16
353
+ # weight copy plus a viable KV pool through the training step. ``use_vllm`` is intentionally
354
+ # ignored for OPD; it only exists for legacy GRPO sizing callers.
355
+ rollout_concurrency = opd_rollout_concurrency(batch_size, group_size)
356
+ rollout = weights + max(
357
+ _KV_CAP, _resident_kv_gb(eff_b, seq_len, rollout_concurrency, fp8_kv=fp8_kv)
358
+ )
331
359
  # opd's gkd loss forward materializes DENSE logits — there is NO fused cross-entropy: the
332
360
  # student forward yields full-sequence bf16 logits [seq, vocab], then the completion rows are
333
361
  # gathered in fp32 [completion, vocab] for the logsumexp. The SFT fused path budgets ZERO
@@ -357,8 +385,11 @@ def estimate_vram_gb(
357
385
  logits_fwd = (seq_len * 2 + completion * 4) * vocab
358
386
  logits_bwd = (seq_len * 2 + completion * 4) * vocab
359
387
  logits = (logits_fwd + logits_bwd) / 1e9
360
- return base + activations + logits
361
- fused = sft_logits_fused(params_b, seq_len) if sft_fused_ce is None else bool(sft_fused_ce)
388
+ return base + rollout + activations + logits
389
+ # Actual TRL SFT keeps fused CE disabled (see worker/sft.py), so dense logits materialize even
390
+ # for long-context / >=3B models. Callers can pass sft_fused_ce=True only for theoretical
391
+ # comparisons; the default must mirror the worker.
392
+ fused = False if sft_fused_ce is None else bool(sft_fused_ce)
362
393
  pd = sft_per_device(batch_size, seq_len=seq_len, vocab=vocab, fused=fused)
363
394
  activations = _ACT_COEF * pd * (seq_len / 1024.0) * width
364
395
  # Don't clamp to budget: pd=1 is irreducible and the logits can exceed the budget at near-2048 ctx.
@@ -516,7 +547,7 @@ def model_required_vram_gb(
516
547
  except (TypeError, ValueError):
517
548
  return default
518
549
 
519
- max_tokens = _pos_int(_g(train, "max_tokens"), None)
550
+ max_tokens = _pos_int(_g(train, "max_completion_tokens"), None)
520
551
  _algo = (algorithm or "").lower()
521
552
  if _algo in ("grpo", "rl"):
522
553
  _default_len = grpo_rollout_seq_len(0, max_tokens, thinking)
@@ -525,10 +556,18 @@ def model_required_vram_gb(
525
556
  _default_len = opd_rollout_seq_len(0, max_tokens, thinking)
526
557
  else:
527
558
  _default_len = 1024
528
- seq_len = _pos_int(_g(train, "max_length"), _default_len)
559
+ seq_len = _pos_int(_g(train, "max_context_tokens"), _default_len)
529
560
  lora_rank = _pos_int(_g(train, "lora_rank"), 32)
530
- group_size = _pos_int(_g(train, "group_size"), 8)
531
- batch_size = _pos_int(_g(train, "batch_size"), _sft_per_device_bs())
561
+ if _algo == "opd":
562
+ from flash.engine.recipe import RECIPE
563
+
564
+ batch_size_default = int(RECIPE.opd.prompts_per_step)
565
+ group_size_default = int(RECIPE.opd.group_size)
566
+ else:
567
+ batch_size_default = _sft_per_device_bs()
568
+ group_size_default = 8
569
+ group_size = _pos_int(_g(train, "group_size"), group_size_default)
570
+ batch_size = _pos_int(_g(train, "batch_size"), batch_size_default)
532
571
 
533
572
  def _need(
534
573
  params_b: float,
@@ -557,17 +596,21 @@ def model_required_vram_gb(
557
596
  return math.ceil(est * headroom)
558
597
 
559
598
  from flash.catalog import MODELS, vocab_size_for
560
- from flash.engine.chalk_kernels import chalk_supports_fused_ce_model
561
599
 
562
600
  info = MODELS.get(model_id)
563
601
  model_vocab = vocab_size_for(model_id)
564
- is_grpo = (algorithm or "").lower() in ("grpo", "rl")
565
- sft_fused_ce = (
566
- None
567
- if is_grpo
568
- else sft_logits_fused(resolve_params_b(model_id), seq_len)
569
- and chalk_supports_fused_ce_model(model_id)
602
+ is_grpo = _algo in ("grpo", "rl")
603
+ is_opd = _algo == "opd"
604
+ is_vllm_rollout = is_grpo or is_opd
605
+ vllm_concurrency = (
606
+ opd_rollout_concurrency(batch_size, group_size) if is_opd else group_size
570
607
  )
608
+ # Worker parity: TRL SFT deliberately calls install_chalk_kernels(..., fused_ce=False), because
609
+ # SFTTrainer.compute_loss reads outputs.logits and crashes when fused CE returns logits=None. So
610
+ # every SFT run materializes dense logits, even for long context / >=3B models where the old
611
+ # allocator assumed a fused CE path. Size SFT with fused CE OFF up front or long-context Qwen SFT
612
+ # routes to consumer cards and OOMs on the first backward.
613
+ sft_fused_ce = None if is_grpo else False
571
614
  if info is not None:
572
615
  params_b = info.params_b # curated, authoritative (required field) — no string parsing
573
616
  quant = getattr(info, "quant", "bf16") or "bf16"
@@ -620,29 +663,39 @@ def model_required_vram_gb(
620
663
  else:
621
664
  floor += grpo_seq_escalation_gb(active_b or params_b, seq_len)
622
665
  need = max(need, floor)
623
- if is_grpo and use_vllm:
666
+ if is_vllm_rollout and use_vllm:
624
667
  floor_gb = 24 if (params_b or 0.0) <= 1.0 else int(_VLLM_COLOCATE_FLOOR_GB)
668
+ if is_opd and (params_b or 0.0) >= 2.0:
669
+ floor_gb = max(floor_gb, int(_OPD_VLLM_COLOCATE_FLOOR_GB))
625
670
  need = max(need, floor_gb)
626
671
  # vLLM KV-cache init preflight: the card must leave a viable cache-block pool
627
672
  # under the colocate utilization cap, or the engine dies at init ("No available
628
673
  # memory for the cache blocks") on a card the training-peak estimate accepted.
629
674
  need = max(
630
675
  need,
631
- grpo_kv_floor_gb(params_b or 4.0, seq_len, group_size, active_params_b=active_b),
676
+ grpo_kv_floor_gb(
677
+ params_b or 4.0,
678
+ seq_len,
679
+ vllm_concurrency,
680
+ active_params_b=active_b,
681
+ ),
632
682
  )
633
683
  return need
634
684
  params_b = fetch_hf_params_b(model_id)
635
685
  if params_b is None:
636
686
  return 24
637
687
  # Size the uncataloged (model_policy="allow") fallback with the ACTUAL algorithm, not a hardcoded
638
- # "grpo". An open-model OPD run was quoted/allocated as a colocated-vLLM GRPO job, so the new OPD
639
- # dense-logit estimator (estimate_vram_gb's is_opd path) was never applied -- rejecting fitting runs
640
- # or routing them to pricier GPUs. The cataloged path above already threads `algorithm` through
641
- # _need; do the same here (codex[bot]). The grpo-only escalations below stay gated on is_grpo.
688
+ # "grpo". The cataloged path above already threads `algorithm` through _need; do the same here so
689
+ # open-model OPD uses its own dense-logit + colocated-vLLM estimator. The grpo-only sequence
690
+ # escalation stays gated on is_grpo.
642
691
  need = _need(params_b, algorithm, vocab=model_vocab)
643
692
  if is_grpo:
644
693
  need += grpo_seq_escalation_gb(params_b, seq_len)
645
- need = max(need, grpo_kv_floor_gb(params_b, seq_len, group_size))
694
+ if is_vllm_rollout:
695
+ floor_gb = 24 if params_b <= 1.0 else int(_VLLM_COLOCATE_FLOOR_GB)
696
+ if is_opd and params_b >= 2.0:
697
+ floor_gb = max(floor_gb, int(_OPD_VLLM_COLOCATE_FLOOR_GB))
698
+ need = max(need, floor_gb, grpo_kv_floor_gb(params_b, seq_len, vllm_concurrency))
646
699
  return need
647
700
 
648
701
 
@@ -137,7 +137,7 @@ def grpo_overrides() -> dict:
137
137
  cfg = {
138
138
  "group_size": train.group_size,
139
139
  "temperature": train.temperature,
140
- "max_tokens": train.max_tokens,
140
+ "max_tokens": train.max_completion_tokens,
141
141
  "kl_penalty_coef": train.kl_penalty_coef,
142
142
  "advantage_clip": train.advantage_clip,
143
143
  "thinking_length_penalty_coef": train.thinking_length_penalty_coef,
@@ -173,7 +173,7 @@ def resolve_grpo_sleep_mode() -> tuple[bool, int, float, bool]:
173
173
  spec = _w.JOB_SPEC
174
174
  train = spec.train if spec else None
175
175
  model_id = spec.model if spec else ""
176
- ctx = int(train.max_length if train and train.max_length else 0)
176
+ ctx = int(train.max_context_tokens if train and train.max_context_tokens else 0)
177
177
  gcfg = _w.grpo_overrides()
178
178
  group_size = int(gcfg.get("group_size") or RECIPE.rl.group_size)
179
179
  lora_rank = int(train.lora_rank) if train and train.lora_rank else 32