freesolo-flash-dev 0.2.46__tar.gz → 0.2.48__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 (296) hide show
  1. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/PKG-INFO +2 -2
  2. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/commands.py +5 -6
  3. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/training_doc.py +14 -14
  4. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cost/analytical.py +8 -12
  5. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cost/facts.py +4 -7
  6. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cost/spec.py +4 -7
  7. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cost/types.py +7 -8
  8. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/recipe.py +4 -5
  9. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/vram.py +73 -20
  10. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/grpo.py +2 -2
  11. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/opd.py +237 -249
  12. freesolo_flash_dev-0.2.48/flash/engine/worker/opd_vllm.py +274 -0
  13. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/lifecycle.py +13 -1
  14. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/memory.py +1 -1
  15. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/rl.py +6 -5
  16. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/lora_rank.py +8 -7
  17. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/schema/__init__.py +21 -30
  18. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/spec.py +4 -7
  19. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/pyproject.toml +3 -3
  20. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_allocator.py +456 -61
  21. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_b200_rollout_opt.py +1 -1
  22. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_backend_jobspec_contract.py +1 -1
  23. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_commands.py +4 -4
  24. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_estimate.py +4 -4
  25. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_analytical.py +43 -39
  26. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_gpus.py +4 -4
  27. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_grpo_params.py +51 -15
  28. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_grpo_sleep_gate.py +4 -4
  29. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_jobs.py +54 -0
  30. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_oom_escalate_gpu.py +10 -1
  31. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_opd.py +363 -75
  32. freesolo_flash_dev-0.2.48/tests/test_opd_vllm.py +184 -0
  33. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_open_model_policy.py +1 -1
  34. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_spec_and_validation.py +9 -5
  35. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_train_context_preflight.py +19 -14
  36. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/uv.lock +1 -1
  37. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.dockerignore +0 -0
  38. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.env.example +0 -0
  39. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/auto-rebake.yml +0 -0
  40. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/bake-kernel-cache.yml +0 -0
  41. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/ci.yml +0 -0
  42. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/main-source-guard.yml +0 -0
  43. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/notify-tests-repo.yml +0 -0
  44. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/publish-dev.yml +0 -0
  45. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/publish-image.yml +0 -0
  46. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/publish.yml +0 -0
  47. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/version-parity.yml +0 -0
  48. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.github/workflows/worker-image.yml +0 -0
  49. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/.gitignore +0 -0
  50. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/Dockerfile +0 -0
  51. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/Dockerfile.worker +0 -0
  52. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/LICENSE +0 -0
  53. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/README.md +0 -0
  54. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/build/kernel_cache/.gitignore +0 -0
  55. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/build/kernel_cache/.keep +0 -0
  56. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/Dockerfile.kernelcache +0 -0
  57. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/Dockerfile.kernelcache.relayer +0 -0
  58. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/bake_kernel_cache.py +0 -0
  59. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/bake_pod_entry.py +0 -0
  60. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/kernel_fingerprint.py +0 -0
  61. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/docker/make_rp_handler.py +0 -0
  62. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/__init__.py +0 -0
  63. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/_channel.py +0 -0
  64. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/_fileio.py +0 -0
  65. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/_logging.py +0 -0
  66. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/_update_check.py +0 -0
  67. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/adapter_artifacts.py +0 -0
  68. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/catalog.py +0 -0
  69. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/__init__.py +0 -0
  70. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/__main__.py +0 -0
  71. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/_tty.py +0 -0
  72. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/envpush.py +0 -0
  73. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cli/render.py +0 -0
  74. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/client/__init__.py +0 -0
  75. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/client/config.py +0 -0
  76. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/client/http.py +0 -0
  77. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/client/runtime_secrets.py +0 -0
  78. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/client/specs.py +0 -0
  79. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/cost/__init__.py +0 -0
  80. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/__init__.py +0 -0
  81. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/accounting.py +0 -0
  82. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/chalk_kernels.py +0 -0
  83. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/multiturn_rollout.py +0 -0
  84. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/__init__.py +0 -0
  85. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/__main__.py +0 -0
  86. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/_pkg.py +0 -0
  87. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/adapter.py +0 -0
  88. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/decoding.py +0 -0
  89. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/finalize.py +0 -0
  90. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/gpu_setup.py +0 -0
  91. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/heartbeat.py +0 -0
  92. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/hf.py +0 -0
  93. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/kernel_warmup.py +0 -0
  94. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/lora.py +0 -0
  95. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/opd_gkd.py +0 -0
  96. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/packing.py +0 -0
  97. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/__init__.py +0 -0
  98. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/attn.py +0 -0
  99. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/diagnostics.py +0 -0
  100. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/liger.py +0 -0
  101. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/perf/loraplus.py +0 -0
  102. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/sft.py +0 -0
  103. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/teacher.py +0 -0
  104. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/tokenizer_align.py +0 -0
  105. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/engine/worker/wandb_log.py +0 -0
  106. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/__init__.py +0 -0
  107. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/adapter.py +0 -0
  108. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/archive_policy.py +0 -0
  109. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/base.py +0 -0
  110. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/loader.py +0 -0
  111. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/pull.py +0 -0
  112. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/envs/registry.py +0 -0
  113. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/__init__.py +0 -0
  114. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_auth.py +0 -0
  115. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_hf_artifacts.py +0 -0
  116. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_hf_retry.py +0 -0
  117. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_http.py +0 -0
  118. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_instance.py +0 -0
  119. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_instance_bootstrap.py +0 -0
  120. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_instance_poll.py +0 -0
  121. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_instance_provider.py +0 -0
  122. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_poll.py +0 -0
  123. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/_worker.py +0 -0
  124. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/allocator.py +0 -0
  125. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/base.py +0 -0
  126. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/__init__.py +0 -0
  127. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/api.py +0 -0
  128. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/auth.py +0 -0
  129. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/gpus.py +0 -0
  130. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  131. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  132. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/preflight.py +0 -0
  133. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/lambdalabs/pricing.py +0 -0
  134. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/preflight.py +0 -0
  135. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/realized.py +0 -0
  136. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/__init__.py +0 -0
  137. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/api.py +0 -0
  138. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/auth.py +0 -0
  139. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/cost.py +0 -0
  140. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/gpus.py +0 -0
  141. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/jobs.py +0 -0
  142. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/keys.py +0 -0
  143. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/preflight.py +0 -0
  144. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/preload.py +0 -0
  145. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/pricing.py +0 -0
  146. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/slots.py +0 -0
  147. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/train/__init__.py +0 -0
  148. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/train/deps.py +0 -0
  149. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/runpod/train/endpoints.py +0 -0
  150. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/__init__.py +0 -0
  151. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/api.py +0 -0
  152. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/auth.py +0 -0
  153. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/jobs/__init__.py +0 -0
  154. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/jobs/builders.py +0 -0
  155. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/preflight.py +0 -0
  156. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/providers/vast/pricing.py +0 -0
  157. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/py.typed +0 -0
  158. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/runner/__init__.py +0 -0
  159. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/runner/checkpoints.py +0 -0
  160. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/runner/deploy.py +0 -0
  161. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/runner/lifecycle.py +0 -0
  162. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/schema/fields.py +0 -0
  163. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/serve/__init__.py +0 -0
  164. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/serve/deploy.py +0 -0
  165. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/serve/export.py +0 -0
  166. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/serve/pricing.py +0 -0
  167. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/__init__.py +0 -0
  168. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/__main__.py +0 -0
  169. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/_deps.py +0 -0
  170. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/_internal_client.py +0 -0
  171. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/_locks.py +0 -0
  172. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/_runtime.py +0 -0
  173. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/app.py +0 -0
  174. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/auth.py +0 -0
  175. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/billing.py +0 -0
  176. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/billing_retry.py +0 -0
  177. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/checkpoints.py +0 -0
  178. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/db.py +0 -0
  179. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/environment_registry.py +0 -0
  180. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/envs.py +0 -0
  181. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/reconcile.py +0 -0
  182. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/routes/__init__.py +0 -0
  183. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/routes/envs.py +0 -0
  184. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/routes/meta.py +0 -0
  185. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/routes/runs.py +0 -0
  186. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/routes/serving.py +0 -0
  187. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/flash/server/run_registry.py +0 -0
  188. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/infisical-entrypoint.sh +0 -0
  189. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/scripts/build_dev_dist.py +0 -0
  190. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/__init__.py +0 -0
  191. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/_helpers/__init__.py +0 -0
  192. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/_helpers/runner.py +0 -0
  193. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/_helpers/specs.py +0 -0
  194. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/_helpers/vast.py +0 -0
  195. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/conftest.py +0 -0
  196. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/fixtures/math_eval.jsonl +0 -0
  197. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/fixtures/math_train.jsonl +0 -0
  198. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/live/__init__.py +0 -0
  199. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/live/conftest.py +0 -0
  200. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/live/test_lambda_live.py +0 -0
  201. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/live/test_runpod_live.py +0 -0
  202. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/live/test_vast_live.py +0 -0
  203. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_agent_flash_cli_contract.py +0 -0
  204. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_algorithms.py +0 -0
  205. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_billing_retry.py +0 -0
  206. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cancel_remote.py +0 -0
  207. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_catalog_consistency.py +0 -0
  208. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_chalk_kernels.py +0 -0
  209. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_charge_pricing.py +0 -0
  210. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_checkpoints.py +0 -0
  211. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_errors.py +0 -0
  212. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_help.py +0 -0
  213. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_managed.py +0 -0
  214. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cli_render_theme.py +0 -0
  215. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_client.py +0 -0
  216. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_client_server_integration.py +0 -0
  217. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_config_overrides.py +0 -0
  218. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_equation.py +0 -0
  219. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_estimate.py +0 -0
  220. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_hardware.py +0 -0
  221. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_models.py +0 -0
  222. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_cost_rewards.py +0 -0
  223. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_dev_channel.py +0 -0
  224. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_disk_gb.py +0 -0
  225. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_endpoint_name.py +0 -0
  226. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_cache_evict.py +0 -0
  227. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_delete.py +0 -0
  228. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_download.py +0 -0
  229. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_download_api.py +0 -0
  230. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_no_install.py +0 -0
  231. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_publish.py +0 -0
  232. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_pull.py +0 -0
  233. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_pull_managed_control_plane.py +0 -0
  234. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_push.py +0 -0
  235. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_env_rate_limit_resolve.py +0 -0
  236. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_envs_coverage.py +0 -0
  237. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_export.py +0 -0
  238. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_flash_mvp.py +0 -0
  239. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_flash_worker.py +0 -0
  240. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_flashinfer_cache_dirs.py +0 -0
  241. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_github_urlopen_retry.py +0 -0
  242. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_grpo_mask_aware.py +0 -0
  243. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_health_liveness.py +0 -0
  244. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_idle_endpoint_reaper.py +0 -0
  245. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_internal_client.py +0 -0
  246. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_kernel_cache.py +0 -0
  247. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_kernel_fingerprint.py +0 -0
  248. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_kv_util.py +0 -0
  249. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_lambda_runner.py +0 -0
  250. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_logging.py +0 -0
  251. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_login_perms.py +0 -0
  252. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_lora_rank_preflight.py +0 -0
  253. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_managed_hf_repo.py +0 -0
  254. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_metrics_schema_agent_contract.py +0 -0
  255. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_mig_guard.py +0 -0
  256. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_multiturn_rollout.py +0 -0
  257. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_orchestrator_flash.py +0 -0
  258. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_packing.py +0 -0
  259. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_poll_helpers.py +0 -0
  260. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_preflight.py +0 -0
  261. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_provider_routing.py +0 -0
  262. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_provider_teardown_robustness.py +0 -0
  263. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_providers_symmetry.py +0 -0
  264. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_realized_cost.py +0 -0
  265. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_resolve_params_b.py +0 -0
  266. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_resume_on_retry.py +0 -0
  267. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_runmgmt.py +0 -0
  268. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_runpod_api_delete.py +0 -0
  269. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_runpod_key_fingerprint.py +0 -0
  270. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_runpod_key_waterfall.py +0 -0
  271. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_runpod_slots.py +0 -0
  272. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_serve.py +0 -0
  273. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_server_api.py +0 -0
  274. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_server_billing.py +0 -0
  275. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_server_db.py +0 -0
  276. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_serving_contract.py +0 -0
  277. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_sft_example_selection.py +0 -0
  278. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_sft_gc_off.py +0 -0
  279. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_thinking_config.py +0 -0
  280. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_update_check.py +0 -0
  281. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_vast_api.py +0 -0
  282. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_vast_offers.py +0 -0
  283. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_vast_runner.py +0 -0
  284. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_verifiers.py +0 -0
  285. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_version.py +0 -0
  286. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  287. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_vl_warmstart_recombine.py +0 -0
  288. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_wandb_naming.py +0 -0
  289. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_warmstart_cross_repo.py +0 -0
  290. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_weight_cache.py +0 -0
  291. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_dryrun.py +0 -0
  292. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_hardexit.py +0 -0
  293. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_image.py +0 -0
  294. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_init_heartbeat.py +0 -0
  295. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_stack.py +0 -0
  296. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.48}/tests/test_worker_thinking.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.46
4
- Summary: Flash — managed LoRA post-training (SFT/GRPO) for Freesolo environments, driven by the `flash` CLI
3
+ Version: 0.2.48
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
7
7
  Author: Freesolo
@@ -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
  )
@@ -504,16 +504,15 @@ def cmd_env_setup(args) -> int:
504
504
  opd.write_text(
505
505
  f"{opd_multiturn_note}"
506
506
  'model = "Qwen/Qwen3.5-4B"\n'
507
- 'algorithm = "opd" # on-policy distillation from a Fireworks GLM teacher\n\n'
507
+ 'algorithm = "opd" # on-policy distillation from the managed GLM 5.2 teacher\n\n'
508
508
  "# Environment: upload this project folder with\n"
509
509
  "# `flash env push --name my-env .`, then paste the returned id below.\n"
510
- "# The GLM teacher key is platform-managed — nothing to set up or export.\n"
510
+ "# The GLM 5.2 teacher and key are platform-managed — nothing to set up or export.\n"
511
511
  "[environment]\n"
512
512
  'id = ""\n\n'
513
513
  "[train]\n"
514
514
  "steps = 100 # opd is step-driven (like GRPO)\n"
515
515
  "lora_rank = 32\n"
516
- '# teacher_model = "accounts/fireworks/models/glm-5p2" # Fireworks GLM teacher (default)\n'
517
516
  "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
518
517
  "# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
519
518
  )
@@ -201,15 +201,15 @@ 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 `steps`, `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`. |
@@ -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
@@ -377,15 +377,16 @@ SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
377
377
  ## On-policy distillation (`algorithm = "opd"`)
378
378
 
379
379
  Pick distillation when a much stronger **teacher** model can grade your student's work
380
- token-by-token. The student samples on-policy (like GRPO), a Fireworks-hosted GLM teacher scores
380
+ token-by-token. The student samples on-policy (like GRPO), the managed GLM 5.2 teacher scores
381
381
  each of *its own* completions, and a dense per-token loss teaches the student to match the teacher —
382
382
  far more sample-efficient than reward-based RL and with no reward to design. It is **step-driven**
383
383
  (`steps`) and produces a LoRA served exactly like SFT.
384
384
 
385
- - **No teacher key to set up.** The Fireworks key for the GLM teacher is platform-managed: the
385
+ - **No teacher key or model override to set up.** The GLM 5.2 teacher and its Fireworks key are platform-managed: the
386
386
  service supplies its own key to every opd run, so there is nothing to export or declare — an opd
387
- run submits like any other. (Bring-your-own teacher keys are not supported; a `FIREWORKS_API_KEY`
388
- in your shell is simply ignored.) The key is never stored in the spec or needed at serving time.
387
+ run submits like any other. Bring-your-own teacher models and keys are not supported; a
388
+ `FIREWORKS_API_KEY` in your shell is simply ignored. The key is never stored in the spec or
389
+ needed at serving time.
389
390
  - **The student (Qwen / MiniCPM / Kimi) and the teacher (GLM) have different tokenizers.** Flash
390
391
  bridges the vocabulary mismatch with **groupwise reverse-KL** (the collinear-ai *spider* / Tinker
391
392
  method): it aligns the two tokenizations by shared decoded-text spans and applies per-span reverse
@@ -396,7 +397,7 @@ far more sample-efficient than reward-based RL and with no reward to design. It
396
397
  (driving `step_episode` / observations just like GRPO) and distils EVERY assistant turn against the
397
398
  teacher, each conditioned on the transcript up to that turn — the episode's total reverse-KL over
398
399
  the student's generated tokens is the sum of its per-turn reverse-KLs. Env/observation tokens are
399
- never distilled (they're context, not the student's output). Set `[train] max_length` to bound the
400
+ never distilled (they're context, not the student's output). Set `[train] max_context_tokens` to bound the
400
401
  transcript; the teacher must cover it (GLM-5.2's context far exceeds the default budget).
401
402
  - **Judge it like SFT.** Distillation logs a falling per-token loss; a low loss alone is not proof.
402
403
  Keep a held-out split, `flash deploy` the adapter, and score it — confirm the student actually
@@ -413,7 +414,6 @@ id = "your-org/my-env"
413
414
  [train]
414
415
  steps = 100
415
416
  lora_rank = 32
416
- # teacher_model = "accounts/fireworks/models/glm-5p2" # Fireworks GLM teacher (default)
417
417
  # kl_penalty_coef = 1.0 # reverse-KL scale
418
418
  ```
419
419
 
@@ -427,7 +427,7 @@ in a sensible value, so only override with a reason.
427
427
  | Knob | Convention |
428
428
  | --- | --- |
429
429
  | `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. |
430
+ | `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
431
  | `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
432
  | `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
433
  | `thinking_length_penalty_coef` | Per-reasoning-token reward deduction — curb overthinking, but watch it doesn't push the model into terse degeneracy. |
@@ -468,7 +468,7 @@ targeted fix rather than leaning on the reward gate to slowly select against it.
468
468
  | --- | --- | --- |
469
469
  | Repetition / looping collapse | the same phrase repeats until truncation | repetition or length penalty; lower `temperature` |
470
470
  | 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` |
471
+ | Completion truncation | answers cut off mid-thought | raise `max_completion_tokens` / `max_context_tokens` |
472
472
  | Unparsed / over-escaped output | reward can't read the answer | robust parser; return `0.0` on parse fail; format gate |
473
473
  | Wrapper / markdown around structured output | prose around the JSON/answer | a format gate; `stop_sequences` |
474
474
  | Uniform-reward groups | every rollout in a group scores the same → no gradient | shape the reward for partial credit; raise `temperature` |
@@ -513,7 +513,7 @@ on a beyond-noise improvement.
513
513
  ## Treat crashes as infra, not model size
514
514
 
515
515
  > 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
516
+ > sign the model is too big. Lower `max_context_tokens`, `max_completion_tokens`, or `group_size` to shrink
517
517
  > the run's footprint and let the allocator retry onto the next fitting GPU class — do
518
518
  > **not** switch to a smaller model to make a crash disappear. That silently destroys
519
519
  > quality.
@@ -44,11 +44,6 @@ MFU_DECODE = 0.12 # batched vLLM rollout (decode is memory-bandwidth-bound)
44
44
  # wall is ceil(completions / slots) waves x latency, not completions x latency.
45
45
  REWARD_CONCURRENCY = 16.0
46
46
 
47
- # Teacher scoring is CONCURRENT too: run_opd's primary path fans a step's completions across up to
48
- # TEACHER_CONCURRENCY Fireworks calls (mirror of opd.py _TEACHER_SCORE_MAX_WORKERS), so the teacher
49
- # wall is ceil(completions / slots) waves x latency, not completions x latency.
50
- TEACHER_CONCURRENCY = 8.0
51
-
52
47
  # Cold-start overhead (seconds): container boot + deps + model load (+ vLLM init for GRPO).
53
48
  #
54
49
  # Calibrated against a real fresh-worker run (0.8B SFT, RTX 3090 @ $0.239/hr) whose elapsed wall
@@ -78,11 +73,11 @@ def _fmt_duration(seconds: float) -> str:
78
73
 
79
74
  def setup_seconds(config: RunConfig) -> float:
80
75
  """Cold-start wall time before the first optimizer step: container boot + deps + model load
81
- (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.
82
77
  This elapsed setup time is reported but not included in customer-facing cost."""
83
78
  model_load = MODEL_LOAD_BASE_S + download_weight_gb(config.model_id) / DOWNLOAD_RATE_GBPS
84
79
  s = WORKER_BOOT_S + DEPS_INSTALL_S + model_load
85
- if config.is_grpo:
80
+ if config.has_rollout:
86
81
  s += VLLM_INIT_S
87
82
  return s
88
83
 
@@ -112,11 +107,12 @@ def seconds_per_step(config: RunConfig, gpu: str) -> float:
112
107
  gen_s = (GRPO_GEN_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (peak * MFU_DECODE)
113
108
  update_s = (OPD_UPDATE_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (peak * MFU_TRAIN)
114
109
  teacher_lat = teacher_seconds_per_completion()
115
- # run_opd's primary path scores a step's completions CONCURRENTLY over Fireworks, bounded by
116
- # TEACHER_CONCURRENCY workers (opd.py _TEACHER_SCORE_MAX_WORKERS), so the teacher wall is
117
- # ceil(completions / slots) waves x latency NOT the full serial sum (that describes only the
118
- # CPU-test fallback that can't batch-generate). ceil keeps a partial last wave at one latency.
119
- teacher_s = math.ceil(completions / TEACHER_CONCURRENCY) * teacher_lat
110
+ # run_opd's primary path scores a step's completions CONCURRENTLY over Fireworks with a fan-out
111
+ # cap of the step's OWN completion count (prompts_per_step * group_size, opd.py Phase 2), so
112
+ # every completion in a step is scored in ONE parallel wave the teacher wall is a single
113
+ # latency, NOT the full serial sum (that describes only the CPU-test fallback that can't
114
+ # batch-generate). The teacher endpoint's rate limit is the real ceiling on this fan-out.
115
+ teacher_s = teacher_lat if completions else 0.0
120
116
  return gen_s + teacher_s + update_s
121
117
 
122
118
  if not n.is_grpo:
@@ -166,10 +166,9 @@ def reward_seconds_per_completion(override: float | None = None) -> float:
166
166
  # NO FIREWORKS_API_KEY. Source: https://fireworks.ai/models/fireworks/glm-5p2 lists
167
167
  # $1.40 / $0.14 / $4.40 (input / cached input / output) per 1M. opd echo-scores completions
168
168
  # (max_tokens=0), so only INPUT tokens are billed (the teacher never generates) — but the table keeps
169
- # both so a mispriced entry is obvious. glm-5p1 shares the GLM-5 serverless rate.
169
+ # both so a mispriced entry is obvious.
170
170
  TEACHER_USD_PER_1M: dict[str, tuple[float, float]] = {
171
171
  "accounts/fireworks/models/glm-5p2": (1.40, 4.40),
172
- "accounts/fireworks/models/glm-5p1": (1.40, 4.40),
173
172
  }
174
173
  # Fireworks echo-scoring round-trip per completion (wall time, concurrency-bound like reward grading).
175
174
  AVG_TEACHER_SECONDS_PER_COMPLETION = 2.0
@@ -178,11 +177,9 @@ AVG_TEACHER_SECONDS_PER_COMPLETION = 2.0
178
177
  def teacher_price_per_1m(teacher_model: str) -> tuple[float, float]:
179
178
  """(input, output) $/1M tokens for a teacher model.
180
179
 
181
- The empty-string sentinel (an opd run that OMITS [train] teacher_model) resolves to the recipe's
182
- default teacher (``RECIPE.opd.teacher_model``) the SINGLE source of "who is the default
183
- teacher" — so the quote uses that model's real rate with no forked default-price constant. An
184
- unknown non-default teacher falls back to the default teacher's rate (spec-parse already rejects
185
- an unpriced teacher_model, so this is only a defensive backstop)."""
180
+ OPD uses the recipe's fixed GLM 5.2 teacher. ``teacher_model`` is accepted only for internal
181
+ diagnostics/tests; user configs cannot override it. Unknown values fall back defensively to
182
+ the fixed teacher rate."""
186
183
  from flash.engine.recipe import RECIPE
187
184
 
188
185
  default = TEACHER_USD_PER_1M[RECIPE.opd.teacher_model]
@@ -24,8 +24,8 @@ def _sft_seq_len(spec) -> int:
24
24
 
25
25
  t = spec.train
26
26
  return (
27
- int(t.max_length)
28
- if t.max_length is not None
27
+ int(t.max_context_tokens)
28
+ if t.max_context_tokens is not None
29
29
  else (RECIPE.sft.max_seq_len_thinking if spec.thinking else RECIPE.sft.max_seq_len)
30
30
  )
31
31
 
@@ -101,8 +101,8 @@ def runconfig_from_spec(spec) -> RunConfig:
101
101
  model_id=spec.model,
102
102
  method=spec.algorithm,
103
103
  steps=spec_steps(spec),
104
- seq_len=t.max_length,
105
- completion_len=t.max_tokens if has_rollout else None,
104
+ seq_len=t.max_context_tokens,
105
+ completion_len=t.max_completion_tokens if has_rollout else None,
106
106
  batch_size=t.batch_size,
107
107
  group_size=t.group_size if has_rollout else None,
108
108
  lora_rank=t.lora_rank,
@@ -110,9 +110,6 @@ def runconfig_from_spec(spec) -> RunConfig:
110
110
  provider="auto",
111
111
  max_wall_seconds=g.max_wall_seconds,
112
112
  environment=spec.environment.id or None,
113
- # "" is the unset sentinel (RunConfig.teacher_model); facts.teacher_price_per_1m resolves it
114
- # to the recipe default. Non-opd carries no teacher.
115
- teacher_model=t.teacher_model if spec.algorithm == "opd" else "",
116
113
  )
117
114
 
118
115
 
@@ -17,19 +17,18 @@ 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
27
27
  thinking: bool = False
28
28
  # GRPO only: seconds to score one completion. None -> the single average grader latency.
29
29
  reward_seconds_per_completion: float | None = None
30
- # OPD only: the Fireworks teacher id, so the teacher-token quote uses the right per-model rate.
31
- # "" (the unset sentinel, matching TrainSpec.teacher_model) resolves to the recipe's default
32
- # teacher (RECIPE.opd.teacher_model) at the pricing boundary (facts.teacher_price_per_1m).
30
+ # OPD only: internal Fireworks teacher id for teacher-token diagnostics. User configs cannot
31
+ # override this; an empty value resolves to the recipe's fixed GLM 5.2 teacher.
33
32
  teacher_model: str = ""
34
33
 
35
34
  max_wall_seconds: int | None = None # wall cap (spec gpu.max_wall_seconds); None = 24h
@@ -83,7 +82,7 @@ class RunConfig:
83
82
  comp = self.completion_len
84
83
  if comp is None:
85
84
  comp = rc.max_completion_len_thinking if self.thinking else rc.max_completion_len
86
- # 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:
87
86
  # max(1024, max_prompt_len + completion), not bare max_prompt_len (which under-sizes).
88
87
  seq = (
89
88
  self.seq_len
@@ -117,9 +116,9 @@ class RunConfig:
117
116
  if self.batch_size is not None:
118
117
  knobs["batch_size"] = self.batch_size
119
118
  if n.seq_len is not None:
120
- knobs["max_length"] = n.seq_len
119
+ knobs["max_context_tokens"] = n.seq_len
121
120
  if n.completion_len is not None:
122
- knobs["max_tokens"] = n.completion_len
121
+ knobs["max_completion_tokens"] = n.completion_len
123
122
  if n.group_size is not None:
124
123
  knobs["group_size"] = n.group_size
125
124
  return knobs
@@ -36,7 +36,7 @@ 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
@@ -50,9 +50,8 @@ class OPDConfig:
50
50
  """On-policy distillation: student samples, a remote teacher scores its tokens, and a groupwise
51
51
  reverse-KL loss (the collinear-ai spider / Tinker cross-tokenizer method) trains the student."""
52
52
 
53
- # Fireworks-hosted teacher reached over the OpenAI-compatible API; overridable via
54
- # [train].teacher_model. GLM-5.2 is a strong reasoning model whose per-token logprobs
55
- # supervise the (much smaller) student.
53
+ # Fixed Fireworks-hosted GLM 5.2 teacher reached over the OpenAI-compatible API. This is
54
+ # platform-managed and intentionally not configurable from [train].
56
55
  teacher_model: str = "accounts/fireworks/models/glm-5p2"
57
56
  teacher_base_url: str = "https://api.fireworks.ai/inference/v1"
58
57
  # OPD is step-driven like GRPO (on-policy sampling), not epoch-driven like SFT.
@@ -60,7 +59,7 @@ class OPDConfig:
60
59
  learning_rate: float = 1e-5
61
60
  max_prompt_len: int = 1024
62
61
  max_completion_len: int = 512
63
- # 512 is short for <think> traces; overridable via [train].max_tokens.
62
+ # 512 is short for <think> traces; overridable via [train].max_completion_tokens.
64
63
  max_completion_len_thinking: int = 1536
65
64
  prompts_per_step: int = 8
66
65
  # Student samples per prompt. 1 is enough for a direct KD loss (no group-relative baseline
@@ -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