freesolo-flash-dev 1.0.87__tar.gz → 1.0.89__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 (424) hide show
  1. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/Dockerfile.worker +46 -7
  2. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/PKG-INFO +43 -1
  3. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/README.md +42 -0
  4. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/catalog.py +2 -1
  5. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/TRAINING.md +6 -9
  6. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/__init__.py +17 -15
  7. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/commands.py +215 -23
  8. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_eval.py +288 -161
  9. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_setup.py +8 -4
  10. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/render.py +16 -0
  11. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/http.py +42 -10
  12. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/analytical.py +333 -11
  13. freesolo_flash_dev-1.0.89/flash/cost/spec.py +248 -0
  14. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/types.py +20 -0
  15. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/accounting.py +2 -0
  16. freesolo_flash_dev-1.0.89/flash/engine/rollout_profile.py +253 -0
  17. freesolo_flash_dev-1.0.89/flash/engine/sft_workload.py +442 -0
  18. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/__init__.py +40 -7
  19. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/backend_common.py +270 -6
  20. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/heartbeat.py +1 -1
  21. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/lora.py +0 -139
  22. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_train.py +44 -7
  23. freesolo_flash_dev-1.0.89/flash/engine/worker/packing.py +192 -0
  24. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/__init__.py +0 -2
  25. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/liger.py +0 -16
  26. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/loraplus.py +2 -1
  27. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rl_train.py +139 -53
  28. freesolo_flash_dev-1.0.89/flash/engine/worker/rng.py +41 -0
  29. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/sft.py +4 -4
  30. freesolo_flash_dev-1.0.89/flash/engine/worker/sft_profile.py +63 -0
  31. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/sft_train.py +117 -269
  32. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/wandb_log.py +1 -1
  33. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/evaluations.py +1 -0
  34. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/allocator.py +39 -8
  35. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/train/endpoints.py +5 -1
  36. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/__init__.py +309 -29
  37. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/deploy.py +16 -1
  38. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/lifecycle.py +10 -0
  39. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/schema/__init__.py +6 -6
  40. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/db.py +72 -2
  41. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/runs.py +70 -1
  42. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/spec.py +22 -0
  43. freesolo_flash_dev-1.0.89/flash/workload_profile.py +652 -0
  44. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/pyproject.toml +2 -2
  45. freesolo_flash_dev-1.0.89/tests/_helpers/profile.py +197 -0
  46. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/conftest.py +8 -0
  47. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_backend_common.py +177 -14
  48. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cancel_remote.py +51 -0
  49. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_charge_pricing.py +44 -0
  50. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_commands.py +5 -1
  51. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_credential_shadowing.py +4 -4
  52. freesolo_flash_dev-1.0.89/tests/test_cli_estimate.py +743 -0
  53. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_help.py +5 -4
  54. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client.py +43 -0
  55. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_controlled_experiment_repairs.py +18 -5
  56. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_estimate.py +50 -9
  57. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_disk_gb.py +5 -0
  58. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_evaluations.py +623 -352
  59. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flash_worker.py +19 -10
  60. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_reward_batching.py +66 -0
  61. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_managed_hf_repo.py +4 -0
  62. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multi_gpu_gate.py +19 -7
  63. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multimodal_training.py +47 -18
  64. freesolo_flash_dev-1.0.89/tests/test_packing.py +199 -0
  65. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_packing_coverage.py +0 -6
  66. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_routing.py +142 -26
  67. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_repo_cleanup.py +13 -1
  68. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_resume_on_retry.py +11 -1
  69. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_reward_profile.py +5 -1
  70. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_rl_train.py +32 -2
  71. freesolo_flash_dev-1.0.89/tests/test_rollout_profile_quote.py +110 -0
  72. freesolo_flash_dev-1.0.89/tests/test_rollout_profile_sampling.py +278 -0
  73. freesolo_flash_dev-1.0.89/tests/test_rollout_workload_profile.py +500 -0
  74. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runmgmt.py +92 -56
  75. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_api.py +191 -0
  76. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_billing.py +131 -0
  77. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_db.py +129 -0
  78. freesolo_flash_dev-1.0.89/tests/test_sft_max_context.py +97 -0
  79. freesolo_flash_dev-1.0.89/tests/test_sft_profile_worker.py +244 -0
  80. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_train.py +173 -2
  81. freesolo_flash_dev-1.0.89/tests/test_sft_workload.py +234 -0
  82. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_spec_and_validation.py +20 -9
  83. freesolo_flash_dev-1.0.89/tests/test_step_floor.py +222 -0
  84. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_training_controls.py +35 -0
  85. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_weight_cache.py +4 -0
  86. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_stack.py +108 -16
  87. freesolo_flash_dev-1.0.89/tests/test_workload_profile.py +246 -0
  88. freesolo_flash_dev-1.0.89/tests/test_workload_profile_lifecycle.py +394 -0
  89. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/uv.lock +1 -1
  90. freesolo_flash_dev-1.0.87/flash/cost/spec.py +0 -183
  91. freesolo_flash_dev-1.0.87/flash/engine/worker/packing.py +0 -377
  92. freesolo_flash_dev-1.0.87/flash/engine/worker/rng.py +0 -29
  93. freesolo_flash_dev-1.0.87/tests/test_cli_estimate.py +0 -427
  94. freesolo_flash_dev-1.0.87/tests/test_grpo_mask_aware.py +0 -190
  95. freesolo_flash_dev-1.0.87/tests/test_packing.py +0 -922
  96. freesolo_flash_dev-1.0.87/tests/test_sft_max_context.py +0 -60
  97. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.dockerignore +0 -0
  98. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.env.example +0 -0
  99. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  100. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  101. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  102. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  103. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/dependabot.yml +0 -0
  104. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/auto-rebake.yml +0 -0
  105. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/bake-kernel-cache.yml +0 -0
  106. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/ci.yml +0 -0
  107. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/main-source-guard.yml +0 -0
  108. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/notify-tests-repo.yml +0 -0
  109. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish-dev.yml +0 -0
  110. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish-image.yml +0 -0
  111. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish.yml +0 -0
  112. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/version-parity.yml +0 -0
  113. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/worker-image.yml +0 -0
  114. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.gitignore +0 -0
  115. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/CODE_OF_CONDUCT.md +0 -0
  116. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/CONTRIBUTING.md +0 -0
  117. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/Dockerfile +0 -0
  118. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/LICENSE +0 -0
  119. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/NOTICE +0 -0
  120. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/SECURITY.md +0 -0
  121. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/SELF_HOSTING.md +0 -0
  122. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/build/kernel_cache/.gitignore +0 -0
  123. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/build/kernel_cache/.keep +0 -0
  124. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/Dockerfile +0 -0
  125. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/README.md +0 -0
  126. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/entrypoint.sh +0 -0
  127. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/Dockerfile.kernelcache +0 -0
  128. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/Dockerfile.kernelcache.relayer +0 -0
  129. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/bake_kernel_cache.py +0 -0
  130. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/bake_pod_entry.py +0 -0
  131. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/kernel_fingerprint.py +0 -0
  132. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/make_rp_handler.py +0 -0
  133. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/__init__.py +0 -0
  134. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_channel.py +0 -0
  135. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_fileio.py +0 -0
  136. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_logging.py +0 -0
  137. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_paths.py +0 -0
  138. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/adapter_artifacts.py +0 -0
  139. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/__main__.py +0 -0
  140. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/_tty.py +0 -0
  141. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_test.py +0 -0
  142. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/envpush.py +0 -0
  143. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/traces.py +0 -0
  144. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/training_doc.py +0 -0
  145. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/__init__.py +0 -0
  146. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/config.py +0 -0
  147. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/runtime_secrets.py +0 -0
  148. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/specs.py +0 -0
  149. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/__init__.py +0 -0
  150. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/facts.py +0 -0
  151. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/diagnostics.py +0 -0
  152. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/__init__.py +0 -0
  153. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/multiturn_reward_scoring.py +0 -0
  154. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/recipe.py +0 -0
  155. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/reward_profile.py +0 -0
  156. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/steps.py +0 -0
  157. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/structured_outputs.py +0 -0
  158. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/vram.py +0 -0
  159. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/__main__.py +0 -0
  160. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/_pkg.py +0 -0
  161. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/adapter.py +0 -0
  162. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/decoding.py +0 -0
  163. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/finalize.py +0 -0
  164. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo.py +0 -0
  165. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo_multiturn.py +0 -0
  166. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo_plugin.py +0 -0
  167. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/hf.py +0 -0
  168. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/kernel_warmup.py +0 -0
  169. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/multiturn_glue.py +0 -0
  170. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd.py +0 -0
  171. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_gkd.py +0 -0
  172. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_multiturn.py +0 -0
  173. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_plugin.py +0 -0
  174. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_structured.py +0 -0
  175. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/attn.py +0 -0
  176. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/diagnostics.py +0 -0
  177. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/lifecycle.py +0 -0
  178. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/memory.py +0 -0
  179. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rl.py +0 -0
  180. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rollout_samples.py +0 -0
  181. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/teacher.py +0 -0
  182. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/teacher_encoding.py +0 -0
  183. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/tokenizer_align.py +0 -0
  184. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker_entrypoint.py +0 -0
  185. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/__init__.py +0 -0
  186. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/adapter.py +0 -0
  187. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/archive.py +0 -0
  188. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/archive_policy.py +0 -0
  189. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/base.py +0 -0
  190. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/loader.py +0 -0
  191. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/pull.py +0 -0
  192. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/registry.py +0 -0
  193. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/lora_rank.py +0 -0
  194. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/multimodal.py +0 -0
  195. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_limits.py +0 -0
  196. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_retry_contract.py +0 -0
  197. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_validation.py +0 -0
  198. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/__init__.py +0 -0
  199. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_auth.py +0 -0
  200. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_deadline.py +0 -0
  201. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_hf_artifacts.py +0 -0
  202. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_hf_retry.py +0 -0
  203. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_http.py +0 -0
  204. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance.py +0 -0
  205. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_bootstrap.py +0 -0
  206. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_poll.py +0 -0
  207. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_provider.py +0 -0
  208. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_poll.py +0 -0
  209. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_worker.py +0 -0
  210. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/base.py +0 -0
  211. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/__init__.py +0 -0
  212. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/api.py +0 -0
  213. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/auth.py +0 -0
  214. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/gpus.py +0 -0
  215. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  216. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  217. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/preflight.py +0 -0
  218. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/pricing.py +0 -0
  219. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/preflight.py +0 -0
  220. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/realized.py +0 -0
  221. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/__init__.py +0 -0
  222. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/api.py +0 -0
  223. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/auth.py +0 -0
  224. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/cost.py +0 -0
  225. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/gpus.py +0 -0
  226. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/jobs.py +0 -0
  227. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/keys.py +0 -0
  228. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/preflight.py +0 -0
  229. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/pricing.py +0 -0
  230. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/slots.py +0 -0
  231. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/train/__init__.py +0 -0
  232. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/__init__.py +0 -0
  233. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/api.py +0 -0
  234. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/auth.py +0 -0
  235. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/jobs/__init__.py +0 -0
  236. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/jobs/builders.py +0 -0
  237. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/preflight.py +0 -0
  238. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/pricing.py +0 -0
  239. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/weight_cache_preload.py +0 -0
  240. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/py.typed +0 -0
  241. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/checkpoints.py +0 -0
  242. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/verified_revisions.py +0 -0
  243. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/schema/fields.py +0 -0
  244. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/__init__.py +0 -0
  245. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/deploy.py +0 -0
  246. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/export.py +0 -0
  247. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/preflight.py +0 -0
  248. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/pricing.py +0 -0
  249. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/urls.py +0 -0
  250. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/__init__.py +0 -0
  251. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/__main__.py +0 -0
  252. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_deps.py +0 -0
  253. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_internal_client.py +0 -0
  254. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_locks.py +0 -0
  255. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_runtime.py +0 -0
  256. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/app.py +0 -0
  257. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/auth.py +0 -0
  258. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/billing.py +0 -0
  259. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/billing_retry.py +0 -0
  260. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/checkpoints.py +0 -0
  261. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/environment_registry.py +0 -0
  262. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/envs.py +0 -0
  263. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/projects.py +0 -0
  264. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/reconcile.py +0 -0
  265. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/repo_cleanup.py +0 -0
  266. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/__init__.py +0 -0
  267. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/envs.py +0 -0
  268. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/meta.py +0 -0
  269. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/serving.py +0 -0
  270. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/teacher.py +0 -0
  271. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/run_registry.py +0 -0
  272. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/teacher_broker.py +0 -0
  273. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/thinking.py +0 -0
  274. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/scripts/build_dev_dist.py +0 -0
  275. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/scripts/scrub_history.sh +0 -0
  276. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/__init__.py +0 -0
  277. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/__init__.py +0 -0
  278. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/runner.py +0 -0
  279. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/specs.py +0 -0
  280. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/vast.py +0 -0
  281. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/fixtures/math_eval.jsonl +0 -0
  282. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/fixtures/math_train.jsonl +0 -0
  283. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/__init__.py +0 -0
  284. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/conftest.py +0 -0
  285. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_lambda_live.py +0 -0
  286. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_runpod_live.py +0 -0
  287. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_vast_live.py +0 -0
  288. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_accounting_coverage.py +0 -0
  289. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_agent_flash_cli_contract.py +0 -0
  290. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_algorithms.py +0 -0
  291. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_allocator.py +0 -0
  292. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_async_uploads.py +0 -0
  293. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_backend_jobspec_contract.py +0 -0
  294. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_base_model_provenance.py +0 -0
  295. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_billing_retry.py +0 -0
  296. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_catalog_consistency.py +0 -0
  297. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_checkpoints.py +0 -0
  298. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_commands_coverage.py +0 -0
  299. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_errors.py +0 -0
  300. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_gpus_flag.py +0 -0
  301. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_main_coverage.py +0 -0
  302. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_managed.py +0 -0
  303. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_render_coverage.py +0 -0
  304. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_render_theme.py +0 -0
  305. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_traces.py +0 -0
  306. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_wandb_warning.py +0 -0
  307. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client_server_integration.py +0 -0
  308. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client_stream_reads.py +0 -0
  309. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_config_env_vars.py +0 -0
  310. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_config_overrides.py +0 -0
  311. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_analytical.py +0 -0
  312. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_equation.py +0 -0
  313. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_hardware.py +0 -0
  314. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_models.py +0 -0
  315. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_rewards.py +0 -0
  316. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_deploy_latency.py +0 -0
  317. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_dev_channel.py +0 -0
  318. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_endpoint_name.py +0 -0
  319. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_cache_evict.py +0 -0
  320. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_delete.py +0 -0
  321. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_download.py +0 -0
  322. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_download_api.py +0 -0
  323. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_no_install.py +0 -0
  324. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_publish.py +0 -0
  325. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull.py +0 -0
  326. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_coverage.py +0 -0
  327. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_loader_coverage.py +0 -0
  328. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_managed_control_plane.py +0 -0
  329. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_push.py +0 -0
  330. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_rate_limit_resolve.py +0 -0
  331. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_ref_pinning.py +0 -0
  332. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_test.py +0 -0
  333. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_envs_coverage.py +0 -0
  334. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_export.py +0 -0
  335. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flash_mvp.py +0 -0
  336. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flashinfer_cache_dirs.py +0 -0
  337. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_github_urlopen_retry.py +0 -0
  338. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_gpu_type_single_field.py +0 -0
  339. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_gpus.py +0 -0
  340. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_params.py +0 -0
  341. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_resident_sizing.py +0 -0
  342. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_health_liveness.py +0 -0
  343. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_hf_retry.py +0 -0
  344. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_idle_endpoint_reaper.py +0 -0
  345. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_infisical_overlay.py +0 -0
  346. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_instance_bootstrap_coverage.py +0 -0
  347. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_internal_client.py +0 -0
  348. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_jobs.py +0 -0
  349. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kernel_cache.py +0 -0
  350. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kernel_fingerprint.py +0 -0
  351. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kv_util.py +0 -0
  352. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lambda_api_coverage.py +0 -0
  353. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lambda_runner.py +0 -0
  354. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_logging.py +0 -0
  355. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_login_perms.py +0 -0
  356. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lora_rank_coverage.py +0 -0
  357. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lora_rank_preflight.py +0 -0
  358. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_managed_config_fields.py +0 -0
  359. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_metrics_schema_agent_contract.py +0 -0
  360. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_mig_guard.py +0 -0
  361. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multimodal_input_grads.py +0 -0
  362. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_named_reward_metrics.py +0 -0
  363. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_oom_escalate_gpu.py +0 -0
  364. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd.py +0 -0
  365. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_limits.py +0 -0
  366. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_resume_safety.py +0 -0
  367. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_train.py +0 -0
  368. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_open_model_policy.py +0 -0
  369. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_orchestrator_flash.py +0 -0
  370. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_parasail_teacher.py +0 -0
  371. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_poll_helpers.py +0 -0
  372. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_preflight.py +0 -0
  373. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_projects.py +0 -0
  374. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_preflight_coverage.py +0 -0
  375. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_teardown_robustness.py +0 -0
  376. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_providers_symmetry.py +0 -0
  377. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_realized_cost.py +0 -0
  378. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_resolve_params_b.py +0 -0
  379. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_rollout_samples.py +0 -0
  380. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_api_delete.py +0 -0
  381. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_key_fingerprint.py +0 -0
  382. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_key_waterfall.py +0 -0
  383. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_slots.py +0 -0
  384. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serve.py +0 -0
  385. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serve_reasoning_content.py +0 -0
  386. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_app_coverage.py +0 -0
  387. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_auth_singleflight.py +0 -0
  388. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_checkpoints_coverage.py +0 -0
  389. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_envs_coverage.py +0 -0
  390. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_locks_coverage.py +0 -0
  391. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_standalone.py +0 -0
  392. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_context_preflight.py +0 -0
  393. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_contract.py +0 -0
  394. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_pricing_coverage.py +0 -0
  395. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_schema_coverage.py +0 -0
  396. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_chunked_nll.py +0 -0
  397. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_example_selection.py +0 -0
  398. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_gc_off.py +0 -0
  399. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_step_metrics.py +0 -0
  400. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_structured_outputs.py +0 -0
  401. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_teacher_broker.py +0 -0
  402. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_thinking_config.py +0 -0
  403. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_train_context_preflight.py +0 -0
  404. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_api.py +0 -0
  405. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_api_coverage.py +0 -0
  406. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_offers.py +0 -0
  407. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_provider_coverage.py +0 -0
  408. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_runner.py +0 -0
  409. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_verified_revisions.py +0 -0
  410. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_verifiers.py +0 -0
  411. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_version.py +0 -0
  412. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  413. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vram_architecture.py +0 -0
  414. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_link.py +0 -0
  415. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_log_coverage.py +0 -0
  416. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_naming.py +0 -0
  417. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_adapter_download.py +0 -0
  418. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_cross_repo.py +0 -0
  419. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_error_attribution.py +0 -0
  420. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_dryrun.py +0 -0
  421. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_hardexit.py +0 -0
  422. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_image.py +0 -0
  423. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_init_heartbeat.py +0 -0
  424. {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_thinking.py +0 -0
@@ -268,6 +268,15 @@ print('worker stack OK | torch', torch.__version__, '| cudnn', cudnn, '| triton'
268
268
  # flash's, so a job that sets WANDB_API_KEY loses its curves and run link unless wandb is importable
269
269
  # in here; resolve_verl_loggers() degrades to console when it is not, which is the right behaviour
270
270
  # for a foreign FLASH_VERL_PYTHON but silently drops W&B on the default image (codex[bot]).
271
+ #
272
+ # cachetools/uvicorn/fastapi/torchvision/tqdm/pyarrow are named for the same reason
273
+ # resolve_verl_python names them: verl imports them at MODULE level on the launch path (main_ppo ->
274
+ # ppo.ray_trainer -> rollout.llm_server imports cachetools; rollout.utils imports uvicorn + fastapi)
275
+ # yet declares none of them. Some arrive as transitive vllm deps today, but that is vllm's dependency
276
+ # choice, not a contract verl states. This image sets FLASH_VERL_PYTHON, so live workers never take
277
+ # the resolve_verl_python path where that hardening lives -- letting the two lists drift means the
278
+ # fallback protects nobody and a vllm respin breaks launch on the default image alone. Keep them in
279
+ # lockstep with backend_common.py's install list.
271
280
  ARG VERL_SPEC=verl[vllm]@git+https://github.com/freesolo-co/verl@1bea7d6825bbb9d2164e86e379b3680e7c53bb8a
272
281
  ENV FLASH_VERL_PYTHON=/opt/verl-venv/bin/python
273
282
  RUN printf '%s\n' "numpy==2.2.6" "xgrammar==0.1.25" "vllm==0.19.1" > /tmp/verl-overrides.txt \
@@ -282,20 +291,41 @@ RUN printf '%s\n' "numpy==2.2.6" "xgrammar==0.1.25" "vllm==0.19.1" > /tmp/verl-o
282
291
  "TransferQueue==0.1.7" \
283
292
  liger-kernel "bitsandbytes>=0.49" qwen-vl-utils \
284
293
  "wandb>=0.17" \
294
+ cachetools uvicorn fastapi torchvision tqdm pyarrow \
295
+ "flash-linear-attention @ git+https://github.com/fla-org/flash-linear-attention.git@f0e213dbd8b5fb90c3c7eca869ac1706d5377139" \
296
+ "tilelang==0.1.11" \
297
+ "apache-tvm-ffi==0.1.11" \
285
298
  && rm -f /tmp/verl-overrides.txt \
286
299
  && uv cache clean
287
300
 
288
301
  # flash-attn is REQUIRED in here, not best-effort like it is in the main interpreter above. All three
289
- # backends hard-enable remove-padding (rl_train.py:336, sft_train.py:164, opd_train.py:1505), and verl's
290
- # CUDA remove-padding path imports flash_attn.bert_padding UNGUARDED
291
- # (verl/utils/attention_utils.py:30, torch_functional.py:627,657). There is no SDPA fallback on that
292
- # path: without the module the run dies at the first training batch, which the import-only assert
293
- # below would not catch. Reuses FLASH_ATTN_SPEC so the venv gets the same wheel as the main
294
- # interpreter instead of a second source build.
302
+ # backends default remove-padding ON (rl_train.py, sft_train.py, opd_train.py; only a GDN model whose
303
+ # child cannot reset packed boundaries turns it off), and verl's CUDA remove-padding path imports
304
+ # flash_attn.bert_padding UNGUARDED (verl/utils/attention_utils.py:30, torch_functional.py:627,657).
305
+ # There is no SDPA fallback on that path: without the module the run dies at the first training
306
+ # batch, which the import-only assert below would not catch. Reuses FLASH_ATTN_SPEC so the venv gets
307
+ # the same wheel as the main interpreter instead of a second source build.
295
308
  RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" MAX_JOBS=4 \
296
309
  uv pip install --python /opt/verl-venv/bin/python --no-build-isolation "${FLASH_ATTN_SPEC}" \
297
310
  && uv cache clean
298
311
 
312
+ # causal_conv1d in the VERL VENV, which is where the model actually trains. The main interpreter's
313
+ # copy above does not reach here: /opt/verl-venv is built without --system-site-packages, so the two
314
+ # have disjoint paths. Together with fla's cu_seqlens (installed above) this is what makes the packed
315
+ # GDN boundary resets real rather than decorative: engine.worker.backend_common's boundary shim hands
316
+ # the GDN layer cu_seq_lens_q + seq_idx, but transformers' pure-torch fallbacks take **kwargs and
317
+ # silently DISCARD both, so a child missing these kernels trains across example boundaries while
318
+ # looking patched. verl_child_can_reset_gdn_boundaries probes this venv for exactly that reason.
319
+ # Best-effort with the same uninstall-on-broken-import guard as the main interpreter: a present but
320
+ # ABI-broken package passes transformers' find_spec probe and then crashes at model load, and a
321
+ # failed build must degrade to unpacked training (the gate returns False) rather than fail the image.
322
+ RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" CAUSAL_CONV1D_FORCE_BUILD=TRUE MAX_JOBS=4 \
323
+ uv pip install --python /opt/verl-venv/bin/python "causal-conv1d==1.6.2.post1" --no-build-isolation \
324
+ && /opt/verl-venv/bin/python -c "import causal_conv1d" \
325
+ && echo "causal_conv1d (verl venv): installed (GDN boundary resets enabled)" \
326
+ || { echo "causal_conv1d (verl venv): build/import failed -> GDN trains unpacked (padded fallback)"; \
327
+ uv pip uninstall --python /opt/verl-venv/bin/python causal-conv1d 2>/dev/null || true; }
328
+
299
329
  # Sanity: the verl venv must be able to LAUNCH, not merely install. This is the assert that would
300
330
  # have caught the shipped-but-unrunnable venv. Import the three entrypoints flash actually invokes
301
331
  # (rl_train -> main_ppo, opd_plugin -> main_ppo_sync, sft_train -> sft_trainer; note it is
@@ -303,6 +333,11 @@ RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" MAX_JOBS=4 \
303
333
  # where a version mismatch would surface, and the fork-only rollout field the GRPO path emits as a
304
334
  # hydra override. Also assert the Qwen3.5 archs are registered, since that is the entire reason this
305
335
  # venv carries 0.19.1 instead of 0.11.2.
336
+ #
337
+ # fla is asserted HARD here (causal_conv1d is not, matching its best-effort install above): without
338
+ # it transformers binds chunk_gated_delta_rule to the pure-torch fallback, which accepts cu_seqlens
339
+ # and discards it, so packed GDN runs would train across example boundaries. A silently fla-less
340
+ # child is exactly the failure this assert exists to make loud at build time rather than on a paid GPU.
306
341
  RUN /opt/verl-venv/bin/python -c "import importlib.metadata as M; \
307
342
  import importlib; \
308
343
  [importlib.import_module(m) for m in ('verl.trainer.main_ppo','verl.trainer.main_ppo_sync','verl.trainer.sft_trainer')]; \
@@ -310,6 +345,8 @@ import importlib; \
310
345
  from verl.workers.config.rollout import RolloutConfig; \
311
346
  assert 'mask_truncated_completions' in RolloutConfig.__dataclass_fields__, 'verl is not the freesolo fork (no mask_truncated_completions)'; \
312
347
  importlib.import_module('flash_attn.bert_padding'); \
348
+ from transformers.utils.import_utils import is_flash_linear_attention_available; \
349
+ assert is_flash_linear_attention_available(), 'flash-linear-attention missing from the verl venv: packed GDN boundary resets would be silently discarded'; \
313
350
  assert M.version('verl') == '0.8.0', 'opd structured gate requires verl exactly 0.8.0, got ' + M.version('verl'); \
314
351
  assert M.version('xgrammar') == '0.1.25', 'opd structured gate requires xgrammar exactly 0.1.25, got ' + M.version('xgrammar'); \
315
352
  from vllm import ModelRegistry; \
@@ -318,7 +355,9 @@ missing = {'Qwen3_5ForConditionalGeneration','Qwen3_5MoeForConditionalGeneration
318
355
  assert not missing, 'vllm cannot serve the default-model archs: ' + repr(sorted(missing)); \
319
356
  print('verl venv OK | verl', M.version('verl'), '| vllm', M.version('vllm'), '| torch', M.version('torch'), '| numpy', M.version('numpy'), '| xgrammar', M.version('xgrammar')); \
320
357
  print(' entrypoints: main_ppo (grpo), main_ppo_sync (opd), sft_trainer (sft)'); \
321
- print(' qwen3.5 archs registered; mask_truncated_completions present')"
358
+ print(' qwen3.5 archs registered; mask_truncated_completions present'); \
359
+ from transformers.utils.import_utils import is_causal_conv1d_available; \
360
+ print(' gdn boundary resets: fla yes, causal_conv1d ' + ('yes' if is_causal_conv1d_available() else 'NO -> gdn trains padded'))"
322
361
 
323
362
  # RunPod Serverless worker entrypoint (Pattern D — Flash custom image). Generate /rp_handler.py
324
363
  # from the real _train_body source in providers/runpod/train/endpoints.py (single source of truth;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 1.0.87
3
+ Version: 1.0.89
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
@@ -154,6 +154,48 @@ flash models deploy RUN_ID # serve the trained adapter
154
154
  flash models chat RUN_ID -m "hello" # talk to it
155
155
  ```
156
156
 
157
+ ### Workload profiles (SFT)
158
+
159
+ The first `flash train` or `flash train --cost` on a new SFT config does not print a quote.
160
+ It reports that no workload profile exists yet and starts one:
161
+
162
+ ```
163
+ no exact workload profile exists for this config yet, so there is no training quote to
164
+ print. the server started a separate profile run that loads your environment and tokenizes
165
+ the exact dataset this training would consume.
166
+ that profile run is real work and is billed on its own (estimated $0.03); no training run
167
+ was created, no training gpu was allocated, and nothing was charged for training.
168
+ follow it with `flash runs status profile-sft-...`, then re-run this command once it
169
+ reports done.
170
+ ```
171
+
172
+ A profile run loads your environment at its pinned SHA, renders and tokenizes every example
173
+ this config would train on, and records aggregates: retained and dropped examples, tokens per
174
+ epoch, supervised tokens, realized max length, packed blocks, and the update horizon. The SFT
175
+ quote is then computed from those measured tokens rather than from an assumed average example
176
+ length, and the same profile is what the training worker trains from.
177
+
178
+ Consequences worth knowing before you submit:
179
+
180
+ - **Profiles are separate runs and separate charges.** A profile appears in `flash runs list`
181
+ under its own id and is billed for its own (CPU-only, short) work. It is never rolled into
182
+ the training charge, and a failed profile cannot become training spend.
183
+ - **Quoting fails closed.** If no trustworthy matching profile exists, no training run is
184
+ created, no GPU is allocated, and no quote is persisted. There is no fallback estimate.
185
+ - **The cache key is the workload, not the run.** Profile ids are derived from environment id,
186
+ resolved SHA and params; model, revision and tokenizer revision; `seed`; `thinking`; worker
187
+ env; and the `[train]` fields `epochs`, `batch_size`, `max_context_tokens`, `max_steps` and
188
+ `max_examples`. Change any of them and it is a different workload needing its own profile.
189
+ Everything else about a run, including which GPU it lands on, is outside the key.
190
+ - **Profiles are shared across users.** The id is a hash of that workload, not of your account,
191
+ so if someone else already measured your exact config you wait for their profile and are not
192
+ charged for a second one. Their run is not readable by your key, so the CLI tells you to
193
+ wait rather than pointing you at a run id that would answer 404.
194
+ - **A failed profile is retried, not final.** If a profile fails or is cancelled, the next
195
+ submission of that config starts a replacement rather than reporting the workload as
196
+ permanently unquotable. Because the id is shared, exactly one of the waiting submitters
197
+ launches the replacement and the rest wait on it.
198
+
157
199
  Run management lives under `flash runs` (`status`, `log`, `cancel`, `checkpoint`) and
158
200
  serving under `flash models` (`deploy`, `chat`, `deployments`, `undeploy`, `export`).
159
201
  `flash models` on its own lists supported base models and `flash gpus` lists GPU classes
@@ -91,6 +91,48 @@ flash models deploy RUN_ID # serve the trained adapter
91
91
  flash models chat RUN_ID -m "hello" # talk to it
92
92
  ```
93
93
 
94
+ ### Workload profiles (SFT)
95
+
96
+ The first `flash train` or `flash train --cost` on a new SFT config does not print a quote.
97
+ It reports that no workload profile exists yet and starts one:
98
+
99
+ ```
100
+ no exact workload profile exists for this config yet, so there is no training quote to
101
+ print. the server started a separate profile run that loads your environment and tokenizes
102
+ the exact dataset this training would consume.
103
+ that profile run is real work and is billed on its own (estimated $0.03); no training run
104
+ was created, no training gpu was allocated, and nothing was charged for training.
105
+ follow it with `flash runs status profile-sft-...`, then re-run this command once it
106
+ reports done.
107
+ ```
108
+
109
+ A profile run loads your environment at its pinned SHA, renders and tokenizes every example
110
+ this config would train on, and records aggregates: retained and dropped examples, tokens per
111
+ epoch, supervised tokens, realized max length, packed blocks, and the update horizon. The SFT
112
+ quote is then computed from those measured tokens rather than from an assumed average example
113
+ length, and the same profile is what the training worker trains from.
114
+
115
+ Consequences worth knowing before you submit:
116
+
117
+ - **Profiles are separate runs and separate charges.** A profile appears in `flash runs list`
118
+ under its own id and is billed for its own (CPU-only, short) work. It is never rolled into
119
+ the training charge, and a failed profile cannot become training spend.
120
+ - **Quoting fails closed.** If no trustworthy matching profile exists, no training run is
121
+ created, no GPU is allocated, and no quote is persisted. There is no fallback estimate.
122
+ - **The cache key is the workload, not the run.** Profile ids are derived from environment id,
123
+ resolved SHA and params; model, revision and tokenizer revision; `seed`; `thinking`; worker
124
+ env; and the `[train]` fields `epochs`, `batch_size`, `max_context_tokens`, `max_steps` and
125
+ `max_examples`. Change any of them and it is a different workload needing its own profile.
126
+ Everything else about a run, including which GPU it lands on, is outside the key.
127
+ - **Profiles are shared across users.** The id is a hash of that workload, not of your account,
128
+ so if someone else already measured your exact config you wait for their profile and are not
129
+ charged for a second one. Their run is not readable by your key, so the CLI tells you to
130
+ wait rather than pointing you at a run id that would answer 404.
131
+ - **A failed profile is retried, not final.** If a profile fails or is cancelled, the next
132
+ submission of that config starts a replacement rather than reporting the workload as
133
+ permanently unquotable. Because the id is shared, exactly one of the waiting submitters
134
+ launches the replacement and the rest wait on it.
135
+
94
136
  Run management lives under `flash runs` (`status`, `log`, `cancel`, `checkpoint`) and
95
137
  serving under `flash models` (`deploy`, `chat`, `deployments`, `undeploy`, `export`).
96
138
  `flash models` on its own lists supported base models and `flash gpus` lists GPU classes
@@ -370,7 +370,8 @@ MODELS: dict[str, ModelInfo] = {
370
370
  algos=ALGORITHMS,
371
371
  min_vram_gb=48,
372
372
  min_disk_gb=120,
373
- # NOT QLoRA: peft bnb merge during GRPO rollout diverges trainer precision -> TRL ratio collapses to 0.
373
+ # NOT QLoRA: a peft bnb merge during GRPO rollout diverges the rollout's precision from the
374
+ # trainer's, and the importance ratio those two log-probs form collapses to 0.
374
375
  grpo_min_vram_gb=80,
375
376
  quant="bf16",
376
377
  recommended_gpu="A100 PCIe",
@@ -1024,8 +1024,7 @@ watch `truncation_rate`, which counts completions not ending in EOS and is not s
1024
1024
  For pure multi-turn GRPO, Flash gives each Flash-owned vLLM generation request a managed
1025
1025
  10-to-60-minute absolute deadline and at most two physical attempts. A timed-out request is
1026
1026
  aborted before retry. Enforcement is cooperative between engine polls, and there is no total
1027
- episode elapsed-time cutoff. This policy does not time out OPD, TRL-native tool loops, or
1028
- environment calls.
1027
+ episode elapsed-time cutoff. This policy does not time out OPD or environment calls.
1029
1028
 
1030
1029
  > **The reward-hacking signature:** a smoothed reward rising while mean generated
1031
1030
  > length collapses. Whenever any shortness or format pressure is active, verify the
@@ -1039,13 +1038,11 @@ environment calls.
1039
1038
 
1040
1039
  **First, check that per-turn credit is available to you at all.** It is supported on the
1041
1040
  pure multi-turn rollout path — an `EnvironmentMultiTurn` that drives its own turn loop. A
1042
- multi-turn environment that exposes **tools** runs on TRL's native tool loop instead, and
1043
- there `per_turn` is not implemented: the worker raises
1044
- `credit_assignment='per_turn' is not supported for tool-calling multi-turn environments`
1045
- and the run dies at startup. Nothing catches this earlier — `flash train --dry-run` accepts
1046
- the config, because the trainer choice depends on the environment object, not the spec. If
1047
- your environment exposes tools, stay on `per_episode`; emitting `per_turn_rewards` does not
1048
- unlock it.
1041
+ multi-turn environment that exposes **tools** is not supported at all: the worker raises
1042
+ `verl grpo does not support openai function-calling tool environments; this env reports
1043
+ is_tool_env` and the run dies at startup. Nothing catches this earlier —
1044
+ `flash train --dry-run` accepts the config, because the check depends on the environment
1045
+ object, not the spec.
1049
1046
 
1050
1047
  On the pure multi-turn path, setting `credit_assignment = "per_turn"` is only _half_ the
1051
1048
  request. The trainer reads the
@@ -89,7 +89,7 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
89
89
  ("env setup", "scaffold a starter Freesolo environment"),
90
90
  ("env list", "list local environment sources"),
91
91
  ("env test", "validate a local environment offline"),
92
- ("env eval", "score held-out suites against a deployed model"),
92
+ ("env eval", "score a deployed model against its published environment's suites"),
93
93
  ("env push", "upload a local environment"),
94
94
  ("env pull", "download a published environment or file"),
95
95
  ("env delete", "delete a published environment"),
@@ -396,20 +396,17 @@ def _build_parser() -> argparse.ArgumentParser:
396
396
 
397
397
  env_eval = env_sub.add_parser(
398
398
  "eval",
399
- help="score held-out environment suites against a deployed model",
400
- description="score held-out environment suites against a deployed model",
399
+ help="score a deployed model against its own environment's held-out suites",
400
+ description=(
401
+ "score a deployed model against the held-out suites of the published environment "
402
+ "its run trains on"
403
+ ),
401
404
  )
402
405
  env_eval.add_argument(
403
406
  "target",
404
407
  metavar="TARGET",
405
408
  help="a bare RUN_ID, RUN_ID/step-N, or full immutable adapter revision",
406
409
  )
407
- env_eval.add_argument(
408
- "path",
409
- nargs="?",
410
- default=".",
411
- help="local environment directory or environment.py path",
412
- )
413
410
  # the same two knobs `env test` exposes, and for the same reason: an env whose
414
411
  # `load_environment()` requires a difficulty or reads a non-default split cannot be evaluated
415
412
  # at all without them, and a held-out suite scored against a differently-configured
@@ -449,14 +446,17 @@ def _build_parser() -> argparse.ArgumentParser:
449
446
  help=f"parallel model requests (1-{_MAX_CONCURRENCY})",
450
447
  )
451
448
  env_eval.add_argument(
452
- "--upload",
453
- action="store_true",
454
- help="record the results under a project so they show in the dashboard",
449
+ "--no-upload",
450
+ dest="upload",
451
+ action="store_false",
452
+ help="score locally without recording the results in the dashboard",
455
453
  )
456
454
  env_eval.add_argument(
457
455
  "--project",
458
456
  metavar="PROJECT_ID",
459
- help="project id to record results under (required with --upload)",
457
+ help=(
458
+ "project id to record results under (default: the project the evaluated run belongs to)"
459
+ ),
460
460
  )
461
461
  env_eval.set_defaults(func=cmd_env_eval)
462
462
 
@@ -749,8 +749,10 @@ def _warn_if_login_shadowed(args) -> None:
749
749
  """Surface an ambient FREESOLO_API_KEY that binds a command to another org."""
750
750
  if getattr(args, "func", None) not in _ORG_BINDING_COMMANDS:
751
751
  return
752
- # `train --cost` is catalog-only: it never loads credentials or reaches an organization, so a
753
- # warning that names the environment key's org would describe a request this command never makes.
752
+ # `train --cost` cannot be classified here: the algorithm is only known once the config is
753
+ # parsed, which happens inside the command. grpo/opd stay catalog-only and must not warn about
754
+ # an org they never reach; sft authenticates and can start a billed profile run, so it emits
755
+ # this warning itself (see commands._cmd_train_cost_sft).
754
756
  if getattr(args, "cost", False):
755
757
  return
756
758
  message = shadowed_login_warning()
@@ -21,7 +21,11 @@ from flash.client import (
21
21
  save_credentials,
22
22
  verify_freesolo_key,
23
23
  )
24
- from flash.client.config import load_credentials, load_credentials_with_source
24
+ from flash.client.config import (
25
+ load_credentials,
26
+ load_credentials_with_source,
27
+ shadowed_login_warning,
28
+ )
25
29
  from flash.client.runtime_secrets import runtime_secrets_from_local_env
26
30
  from flash.client.specs import spec_payload
27
31
  from flash.cost.spec import runconfig_from_spec
@@ -29,7 +33,6 @@ from flash.runner import TERMINAL_STATES
29
33
  from flash.schema import (
30
34
  ConfigError,
31
35
  spec_and_train_keys_from_file,
32
- spec_from_file,
33
36
  train_schema_metadata,
34
37
  )
35
38
  from flash.serve.urls import is_freesolo_hosted_url
@@ -320,12 +323,14 @@ def cmd_env_list(args) -> int:
320
323
  def _cmd_train_cost(args) -> int:
321
324
  """`flash train --cost`: print the pre-flight USD cost for the config and exit (no submit).
322
325
 
323
- Catalog-only and deterministic. SFT cost never imports the environment; it requires a positive
324
- [train].max_examples row count instead of guessing or locally counting a dataset."""
325
- from flash.cost import estimate_cost
326
+ grpo and opd quote offline from the catalog. sft has no offline quote at all: its cost is
327
+ derived from the exact tokenized dataset, and only a workload profile knows that, so sft asks
328
+ the server for the same profile-backed quote a real submit would freeze. There is deliberately
329
+ no analytical sft fallback -- a guessed row count is what this whole path exists to remove.
330
+ """
326
331
  from flash.lora_rank import preflight_train_context_within_serving
327
332
 
328
- spec = spec_from_file(
333
+ spec, authored_train_keys = spec_and_train_keys_from_file(
329
334
  args.config,
330
335
  run_id=None,
331
336
  overrides=args.overrides,
@@ -333,6 +338,15 @@ def _cmd_train_cost(args) -> int:
333
338
  project_required=True,
334
339
  )
335
340
  preflight_train_context_within_serving(spec)
341
+ if spec.algorithm == "sft":
342
+ return _cmd_train_cost_sft(args, spec, authored_train_keys)
343
+ return _cmd_train_cost_offline(spec)
344
+
345
+
346
+ def _cmd_train_cost_offline(spec) -> int:
347
+ """Catalog-only quote for the algorithms that do not need workload evidence yet (grpo, opd)."""
348
+ from flash.cost import estimate_cost
349
+
336
350
  if spec.train.init_from_adapter:
337
351
  # --cost is offline/catalog-only and cannot read the source adapter, so the rank stays at the
338
352
  # local default. Warm starts train and are priced at the SOURCE adapter's authoritative rank
@@ -352,6 +366,168 @@ def _cmd_train_cost(args) -> int:
352
366
  return 0
353
367
 
354
368
 
369
+ def _cmd_train_cost_sft(args, spec, authored_train_keys: frozenset[str]) -> int:
370
+ """Exact sft quote, served by the same authenticated path that freezes a real submit's quote."""
371
+ # cli._warn_if_login_shadowed() suppresses this warning for `--cost` because the catalog path
372
+ # never reaches an organization. the sft path does: it authenticates, resolves the project, and
373
+ # can start a billed profile run, so the warning has to fire here after all.
374
+ message = shadowed_login_warning()
375
+ if message:
376
+ print(render.warn(message) if render.styled() else f"warning: {message}", file=sys.stderr)
377
+ client = client_from_config()
378
+ try:
379
+ status = client.create_run(
380
+ spec_payload(spec, authored_train_keys=authored_train_keys),
381
+ runtime_secrets=runtime_secrets_from_local_env(
382
+ args.config, keys=spec.environment.secrets
383
+ )
384
+ or None,
385
+ dry_run=True,
386
+ client_train_schema=_client_train_schema(authored_train_keys),
387
+ )
388
+ except ApiError as exc:
389
+ _raise_if_workload_profile_pending(client, exc)
390
+ detail = _legacy_train_key_rejection_detail(exc, authored_train_keys)
391
+ if detail is None:
392
+ raise
393
+ raise ApiError(exc.status, detail, detail=detail) from exc
394
+ _print_exact_sft_cost(status, spec)
395
+ return 0
396
+
397
+
398
+ def _client_train_schema(authored_train_keys: frozenset[str]) -> dict:
399
+ return {
400
+ "version": __version__,
401
+ "fields": train_schema_metadata(),
402
+ "authored_keys": sorted(authored_train_keys),
403
+ }
404
+
405
+
406
+ def _raise_if_workload_profile_pending(client: ApiClient, exc: ApiError) -> None:
407
+ """Explain a profile-pending rejection and fail, or return so the caller keeps handling `exc`.
408
+
409
+ A miss is not a validation error: the server started a real, separately billed profile run that
410
+ tokenizes the exact dataset. Saying only "409" would leave the user with a charge they cannot
411
+ see the reason for, and re-running blindly would look like the same request failing twice.
412
+ """
413
+ detail = exc.detail
414
+ if exc.code != "workload_profile_pending" or not isinstance(detail, dict):
415
+ return
416
+ profile_run_id = str(detail.get("profile_run_id") or "")
417
+ state = str(detail.get("state") or "unknown")
418
+ # the profile id is deterministic in the workload, so this config's profile may already be
419
+ # running under another key. that run is not readable here and is not billed here either, so
420
+ # both the follow-up command and the charge sentence have to change.
421
+ owned = detail.get("owned") is not False
422
+ charge = _profile_charge(client, profile_run_id) if owned else None
423
+ if owned:
424
+ lines = [
425
+ "no exact workload profile exists for this config yet, so there is no training quote "
426
+ "to print. the server started a separate profile run that loads your environment and "
427
+ "tokenizes the exact dataset this training would consume.",
428
+ "that profile run is real work and is billed on its own"
429
+ + (f" (estimated ${charge:.2f})" if charge is not None else "")
430
+ + "; no training run was created, no training gpu was allocated, and nothing was "
431
+ "charged for training.",
432
+ f"follow it with `{CLI_NAME} runs status {profile_run_id}`, then re-run this command "
433
+ "once it reports done."
434
+ if profile_run_id
435
+ else "re-run this command once the profile reports done.",
436
+ ]
437
+ else:
438
+ lines = [
439
+ "no exact workload profile exists for this config yet, so there is no training quote "
440
+ "to print. one is already being measured for this exact config and will be reused, so "
441
+ "nothing was started or charged here.",
442
+ "re-run this command in a few minutes.",
443
+ ]
444
+ for line in lines:
445
+ print(render.note(line) if render.styled() else line, file=sys.stderr)
446
+ raise ClientError(
447
+ f"workload profile {profile_run_id or '(unknown)'} is {state}; "
448
+ "the exact quote is available once it succeeds"
449
+ )
450
+
451
+
452
+ def _profile_charge(client: ApiClient, profile_run_id: str) -> float | None:
453
+ """The profile run's own quote, or None when it cannot be read (not owned by this key, etc)."""
454
+ if not profile_run_id:
455
+ return None
456
+ try:
457
+ status = client.get_run(profile_run_id)
458
+ except (ApiError, ClientError):
459
+ return None
460
+ quote = status.get("estimated_cost_usd") if isinstance(status, dict) else None
461
+ # bool is an int subclass, so an unchecked isinstance would render a JSON `true` as "$1.00" --
462
+ # a charge the user is told to expect that no profile ever quoted.
463
+ if not isinstance(quote, (int, float)) or isinstance(quote, bool):
464
+ return None
465
+ return float(quote)
466
+
467
+
468
+ def _exact_sft_cost_rows(spec, profile: dict) -> list[tuple[str, str | None]]:
469
+ """Rows describing the exact measured workload behind an sft quote.
470
+
471
+ Only aggregates the server actually returned are shown. A field that is absent is dropped
472
+ rather than defaulted, so the panel never reports a count the profile did not measure.
473
+ """
474
+
475
+ def count(key: str) -> int | None:
476
+ value = profile.get(key)
477
+ return value if isinstance(value, int) and not isinstance(value, bool) else None
478
+
479
+ steps = count("authoritative_steps")
480
+ retained, selected = count("retained_examples"), count("selected_examples")
481
+ dropped = count("dropped_examples")
482
+ compute, supervised = (
483
+ count("authoritative_compute_tokens"),
484
+ count("authoritative_supervised_tokens"),
485
+ )
486
+ packing = str(profile.get("packing_mode") or "")
487
+ architecture = str(profile.get("architecture_mode") or "")
488
+ digest = str(profile.get("content_digest") or "")
489
+ examples = None
490
+ if retained is not None and selected is not None:
491
+ examples = f"{retained:,} trained of {selected:,} selected"
492
+ if dropped:
493
+ examples += f" ({dropped:,} dropped)"
494
+ tokens = None
495
+ if compute is not None:
496
+ tokens = f"{compute:,} compute"
497
+ if supervised is not None:
498
+ tokens += f", {supervised:,} supervised"
499
+ return [
500
+ ("run", f"{spec.model} [SFT{f', {steps} steps' if steps is not None else ''}]"),
501
+ ("workload", f"{packing} ({architecture})" if packing and architecture else None),
502
+ ("examples", examples),
503
+ ("tokens", tokens),
504
+ ("profile", digest[:12] or None),
505
+ ]
506
+
507
+
508
+ def _print_exact_sft_cost(status: dict, spec) -> None:
509
+ total = status.get("estimated_cost_usd") if isinstance(status, dict) else None
510
+ if not isinstance(total, (int, float)) or isinstance(total, bool):
511
+ raise ClientError(
512
+ "the server accepted this SFT config but returned no cost estimate; "
513
+ f"run `{CLI_NAME} train --dry-run` to see the full server response"
514
+ )
515
+ profile = status.get("workload_profile")
516
+ rows = _exact_sft_cost_rows(spec, profile if isinstance(profile, dict) else {})
517
+ if render.styled():
518
+ print(render.exact_cost_panel(rows, float(total)))
519
+ else:
520
+ for key, value in rows:
521
+ if value is not None:
522
+ print(f"{key.ljust(8)}: {value}")
523
+ print(f"{'TOTAL'.ljust(8)}: ${float(total):.2f}")
524
+ print(
525
+ "quoted from the exact tokenized workload, not an assumed row count. no training gpu was "
526
+ "allocated and nothing was charged for training.",
527
+ file=sys.stderr,
528
+ )
529
+
530
+
355
531
  def _legacy_train_key_rejection_detail(
356
532
  exc: ApiError, authored_train_keys: frozenset[str]
357
533
  ) -> str | None:
@@ -443,18 +619,16 @@ def cmd_train(args) -> int:
443
619
  )
444
620
  payload = spec_payload(spec, authored_train_keys=authored_train_keys)
445
621
  client = client_from_config()
446
- client_train_schema = {
447
- "version": __version__,
448
- "fields": train_schema_metadata(),
449
- "authored_keys": sorted(authored_train_keys),
450
- }
622
+ client_train_schema = _client_train_schema(authored_train_keys)
451
623
  runtime_secrets = (
452
624
  runtime_secrets_from_local_env(args.config, keys=spec.environment.secrets) or None
453
625
  )
454
626
  _warn_if_wandb_requested_without_key(spec, runtime_secrets, dry_run=bool(args.dry_run))
455
627
  if args.dry_run:
456
- # dry-run runs submit-time server preflights without importing user code, allocating a gpu,
457
- # or charging anything. a rejection surfaces as the server's error with exit status 1.
628
+ # dry-run runs submit-time server preflights without allocating a training gpu or charging
629
+ # for training. a rejection surfaces as the server's error with exit status 1. for sft it
630
+ # also requires an exact workload profile, so a miss starts a separate billed profile run
631
+ # (see _raise_if_workload_profile_pending) instead of previewing anything.
458
632
  try:
459
633
  status = client.create_run(
460
634
  payload,
@@ -463,10 +637,11 @@ def cmd_train(args) -> int:
463
637
  client_train_schema=client_train_schema,
464
638
  )
465
639
  except ApiError as exc:
640
+ _raise_if_workload_profile_pending(client, exc)
466
641
  detail = _legacy_train_key_rejection_detail(exc, authored_train_keys)
467
642
  if detail is None:
468
643
  raise
469
- raise ApiError(exc.status, detail) from exc
644
+ raise ApiError(exc.status, detail, detail=detail) from exc
470
645
  compatibility = status.pop("train_schema_compatibility", None)
471
646
  _print_train_schema_compatibility(compatibility)
472
647
  # the server fails open on a billing-infra problem, so "cost" is only in the validated list
@@ -474,12 +649,22 @@ def cmd_train(args) -> int:
474
649
  # equally not a verification -- so treat anything but an explicit True as unverified.
475
650
  affordability_verified = status.pop("affordability_verified", None) is True
476
651
  cost = "and cost" if affordability_verified else "but NOT cost"
652
+ # sft additionally required a matching workload profile to get this far, and that profile
653
+ # run already imported environment.py and tokenized the dataset. claiming otherwise here
654
+ # would understate what has been checked -- and what has already been billed.
655
+ environment = (
656
+ "your environment.py and the exact dataset were already loaded and tokenized by the "
657
+ "workload profile this quote is built on; model load and gpu/training are first "
658
+ "exercised on the worker after cold-start."
659
+ if spec.algorithm == "sft"
660
+ else "it did NOT import or run your environment.py; dataset loading, "
661
+ "start_episode/episode shapes, reward/scorer, worker imports, model load, and "
662
+ "gpu/training are first exercised on the worker after cold-start."
663
+ )
477
664
  print(
478
665
  "dry-run validated: config/schema, model+algorithm compatibility, lora rank, "
479
- f"runtime-secret presence, warm-start source, serving context cap, {cost}. it did NOT "
480
- "import or run your environment.py; dataset loading, start_episode/episode shapes, "
481
- "reward/scorer, worker imports, model load, and gpu/training are first exercised on the "
482
- "worker after cold-start.",
666
+ f"runtime-secret presence, warm-start source, serving context cap, {cost}. "
667
+ f"{environment}",
483
668
  file=sys.stderr,
484
669
  )
485
670
  if not affordability_verified:
@@ -497,11 +682,18 @@ def cmd_train(args) -> int:
497
682
  else:
498
683
  print(json.dumps(status, indent=2))
499
684
  return 0
500
- status = client.create_run(
501
- payload,
502
- runtime_secrets=runtime_secrets,
503
- client_train_schema=client_train_schema,
504
- )
685
+ try:
686
+ status = client.create_run(
687
+ payload,
688
+ runtime_secrets=runtime_secrets,
689
+ client_train_schema=client_train_schema,
690
+ )
691
+ except ApiError as exc:
692
+ # a real submit misses the profile cache the same way a preview does, and the miss starts a
693
+ # separately billed profile run. without this the user sees a bare 409 for a charge they
694
+ # were never told about (see _raise_if_workload_profile_pending).
695
+ _raise_if_workload_profile_pending(client, exc)
696
+ raise
505
697
  run_id = status["run_id"]
506
698
  logger.info(
507
699
  "submitted run %s: model=%s algorithm=%s gpu=%s",