freesolo-flash-dev 0.2.30__tar.gz → 0.2.31__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 (276) hide show
  1. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/bake-kernel-cache.yml +9 -2
  2. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/Dockerfile.worker +1 -1
  3. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/PKG-INFO +10 -8
  4. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/README.md +4 -4
  5. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/bake_kernel_cache.py +60 -3
  6. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/bake_pod_entry.py +27 -0
  7. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/catalog.py +79 -1
  8. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/commands.py +5 -1
  9. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/render.py +3 -1
  10. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/training_doc.py +8 -5
  11. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cost/__init__.py +2 -1
  12. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cost/analytical.py +30 -12
  13. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cost/facts.py +6 -2
  14. freesolo_flash_dev-0.2.31/flash/cost/spec.py +248 -0
  15. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cost/types.py +16 -2
  16. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/accounting.py +6 -2
  17. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/__init__.py +9 -2
  18. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/adapter.py +22 -1
  19. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/decoding.py +35 -1
  20. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/lora.py +115 -9
  21. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/rl.py +13 -2
  22. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/envs/adapter.py +215 -7
  23. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/envs/registry.py +3 -1
  24. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/_instance.py +3 -0
  25. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/_instance_bootstrap.py +119 -13
  26. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/base.py +24 -8
  27. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/__init__.py +2 -0
  28. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/jobs/__init__.py +12 -3
  29. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/jobs/builders.py +2 -2
  30. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/pricing.py +9 -4
  31. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/__init__.py +2 -0
  32. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/jobs.py +3 -0
  33. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/pricing.py +5 -2
  34. freesolo_flash_dev-0.2.31/flash/providers/runpod/train/__init__.py +154 -0
  35. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/train/deps.py +3 -2
  36. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/train/endpoints.py +111 -9
  37. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/runner/__init__.py +53 -6
  38. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/runner/deploy.py +37 -14
  39. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/runner/lifecycle.py +26 -40
  40. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/schema/__init__.py +10 -2
  41. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/serve/deploy.py +77 -71
  42. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/serve/export.py +54 -0
  43. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/_locks.py +1 -13
  44. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/_runtime.py +26 -54
  45. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/app.py +1 -15
  46. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/billing.py +13 -0
  47. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/environment_registry.py +9 -4
  48. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/routes/envs.py +11 -3
  49. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/routes/runs.py +37 -0
  50. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/routes/serving.py +30 -26
  51. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/pyproject.toml +8 -6
  52. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/conftest.py +0 -11
  53. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_allocator.py +5 -5
  54. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cancel_remote.py +2 -32
  55. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_catalog_consistency.py +73 -0
  56. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_commands.py +8 -1
  57. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_estimate.py +130 -13
  58. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_client.py +11 -0
  59. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_config_overrides.py +5 -6
  60. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_analytical.py +27 -11
  61. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_equation.py +6 -4
  62. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_estimate.py +6 -1
  63. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_hardware.py +16 -6
  64. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_disk_gb.py +1 -2
  65. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_publish.py +91 -5
  66. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_pull.py +229 -0
  67. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_export.py +75 -0
  68. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_flash_mvp.py +1 -1
  69. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_flash_worker.py +77 -1
  70. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_gpus.py +51 -17
  71. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_grpo_params.py +9 -7
  72. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_grpo_sleep_gate.py +3 -3
  73. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_jobs.py +108 -54
  74. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_lambda_runner.py +89 -4
  75. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_managed_hf_repo.py +31 -10
  76. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_mig_guard.py +3 -3
  77. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_orchestrator_flash.py +267 -17
  78. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_packing.py +1 -0
  79. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_provider_routing.py +34 -34
  80. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_provider_teardown_robustness.py +4 -4
  81. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_providers_symmetry.py +6 -6
  82. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_resume_on_retry.py +4 -4
  83. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_runmgmt.py +29 -0
  84. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_serve.py +146 -77
  85. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_server_api.py +246 -21
  86. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_server_billing.py +49 -0
  87. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_serving_contract.py +3 -0
  88. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_spec_and_validation.py +14 -0
  89. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_verifiers.py +47 -1
  90. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_vl_warmstart_recombine.py +196 -16
  91. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_weight_cache.py +1 -1
  92. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_init_heartbeat.py +7 -0
  93. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_stack.py +1 -1
  94. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_thinking.py +20 -5
  95. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/uv.lock +12 -8
  96. freesolo_flash_dev-0.2.30/flash/cost/spec.py +0 -110
  97. freesolo_flash_dev-0.2.30/flash/providers/runpod/train/__init__.py +0 -62
  98. freesolo_flash_dev-0.2.30/flash/server/repo_cleanup.py +0 -296
  99. freesolo_flash_dev-0.2.30/tests/test_repo_cleanup.py +0 -513
  100. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.dockerignore +0 -0
  101. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.env.example +0 -0
  102. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/auto-rebake.yml +0 -0
  103. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/ci.yml +0 -0
  104. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/main-source-guard.yml +0 -0
  105. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/publish-dev.yml +0 -0
  106. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/publish-image.yml +0 -0
  107. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/publish.yml +0 -0
  108. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/version-parity.yml +0 -0
  109. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.github/workflows/worker-image.yml +0 -0
  110. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/.gitignore +0 -0
  111. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/Dockerfile +0 -0
  112. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/LICENSE +0 -0
  113. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/build/kernel_cache/.gitignore +0 -0
  114. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/build/kernel_cache/.keep +0 -0
  115. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/Dockerfile.kernelcache +0 -0
  116. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/Dockerfile.kernelcache.relayer +0 -0
  117. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/kernel_fingerprint.py +0 -0
  118. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docker/make_rp_handler.py +0 -0
  119. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-style/README.md +0 -0
  120. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-style/generate.py +0 -0
  121. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-style/index.html +0 -0
  122. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-style/preview.png +0 -0
  123. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-style/themed-errors.png +0 -0
  124. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/README.md +0 -0
  125. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/before-after-dark.png +0 -0
  126. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/before-after-light.png +0 -0
  127. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/gallery-dark.png +0 -0
  128. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/gallery-light.png +0 -0
  129. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/help-dark.png +0 -0
  130. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/cli-theme/help-light.png +0 -0
  131. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/docs/kernel-cache.md +0 -0
  132. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/__init__.py +0 -0
  133. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/_channel.py +0 -0
  134. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/_fileio.py +0 -0
  135. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/_logging.py +0 -0
  136. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/_update_check.py +0 -0
  137. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/__init__.py +0 -0
  138. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/__main__.py +0 -0
  139. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/cli/envpush.py +0 -0
  140. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/client/__init__.py +0 -0
  141. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/client/config.py +0 -0
  142. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/client/http.py +0 -0
  143. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/client/runtime_secrets.py +0 -0
  144. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/client/specs.py +0 -0
  145. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/__init__.py +0 -0
  146. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/chalk_kernels.py +0 -0
  147. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/multiturn_rollout.py +0 -0
  148. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/recipe.py +0 -0
  149. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/vram.py +0 -0
  150. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/__main__.py +0 -0
  151. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/_pkg.py +0 -0
  152. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/finalize.py +0 -0
  153. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/gpu_setup.py +0 -0
  154. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/grpo.py +0 -0
  155. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/heartbeat.py +0 -0
  156. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/hf.py +0 -0
  157. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/kernel_warmup.py +0 -0
  158. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/packing.py +0 -0
  159. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/__init__.py +0 -0
  160. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/attn.py +0 -0
  161. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/diagnostics.py +0 -0
  162. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/lifecycle.py +0 -0
  163. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/liger.py +0 -0
  164. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/loraplus.py +0 -0
  165. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/perf/memory.py +0 -0
  166. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/sft.py +0 -0
  167. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/engine/worker/wandb_log.py +0 -0
  168. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/envs/__init__.py +0 -0
  169. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/envs/base.py +0 -0
  170. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/envs/pull.py +0 -0
  171. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/__init__.py +0 -0
  172. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/_auth.py +0 -0
  173. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/_http.py +0 -0
  174. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/_poll.py +0 -0
  175. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/allocator.py +0 -0
  176. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/api.py +0 -0
  177. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/auth.py +0 -0
  178. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/gpus.py +0 -0
  179. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/preflight.py +0 -0
  180. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/lambdalabs/train.py +0 -0
  181. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/preflight.py +0 -0
  182. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/realized.py +0 -0
  183. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/api.py +0 -0
  184. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/auth.py +0 -0
  185. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/cost.py +0 -0
  186. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/gpus.py +0 -0
  187. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/keys.py +0 -0
  188. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/preflight.py +0 -0
  189. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/preload.py +0 -0
  190. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/providers/runpod/slots.py +0 -0
  191. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/py.typed +0 -0
  192. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/runner/checkpoints.py +0 -0
  193. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/schema/fields.py +0 -0
  194. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/serve/__init__.py +0 -0
  195. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/serve/pricing.py +0 -0
  196. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/__init__.py +0 -0
  197. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/__main__.py +0 -0
  198. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/_deps.py +0 -0
  199. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/_internal_client.py +0 -0
  200. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/auth.py +0 -0
  201. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/billing_retry.py +0 -0
  202. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/checkpoints.py +0 -0
  203. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/db.py +0 -0
  204. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/envs.py +0 -0
  205. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/reconcile.py +0 -0
  206. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/routes/__init__.py +0 -0
  207. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/routes/meta.py +0 -0
  208. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/server/run_registry.py +0 -0
  209. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/flash/spec.py +0 -0
  210. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/infisical-entrypoint.sh +0 -0
  211. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/scripts/build_dev_dist.py +0 -0
  212. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/__init__.py +0 -0
  213. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/_helpers/__init__.py +0 -0
  214. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/_helpers/runner.py +0 -0
  215. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/_helpers/specs.py +0 -0
  216. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/fixtures/math_eval.jsonl +0 -0
  217. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/fixtures/math_train.jsonl +0 -0
  218. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/live/__init__.py +0 -0
  219. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/live/conftest.py +0 -0
  220. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/live/test_lambda_live.py +0 -0
  221. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/live/test_runpod_live.py +0 -0
  222. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_agent_flash_cli_contract.py +0 -0
  223. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_algorithms.py +0 -0
  224. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_b200_rollout_opt.py +0 -0
  225. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_backend_jobspec_contract.py +0 -0
  226. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_billing_retry.py +0 -0
  227. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_chalk_kernels.py +0 -0
  228. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_checkpoints.py +0 -0
  229. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_errors.py +0 -0
  230. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_help.py +0 -0
  231. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_managed.py +0 -0
  232. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cli_render_theme.py +0 -0
  233. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_client_server_integration.py +0 -0
  234. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_models.py +0 -0
  235. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_cost_rewards.py +0 -0
  236. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_dev_channel.py +0 -0
  237. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_endpoint_name.py +0 -0
  238. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_delete.py +0 -0
  239. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_no_install.py +0 -0
  240. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_push.py +0 -0
  241. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_env_rate_limit_resolve.py +0 -0
  242. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_envs_coverage.py +0 -0
  243. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_flashinfer_cache_dirs.py +0 -0
  244. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_github_urlopen_retry.py +0 -0
  245. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_grpo_mask_aware.py +0 -0
  246. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_idle_endpoint_reaper.py +0 -0
  247. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_internal_client.py +0 -0
  248. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_kernel_cache.py +0 -0
  249. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_kernel_fingerprint.py +0 -0
  250. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_kv_util.py +0 -0
  251. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_logging.py +0 -0
  252. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_login_perms.py +0 -0
  253. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_metrics_schema_agent_contract.py +0 -0
  254. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_multiturn_rollout.py +0 -0
  255. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_oom_escalate_gpu.py +0 -0
  256. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_open_model_policy.py +0 -0
  257. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_poll_helpers.py +0 -0
  258. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_preflight.py +0 -0
  259. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_realized_cost.py +0 -0
  260. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_resolve_params_b.py +0 -0
  261. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_runpod_api_delete.py +0 -0
  262. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_runpod_key_fingerprint.py +0 -0
  263. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_runpod_key_waterfall.py +0 -0
  264. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_runpod_slots.py +0 -0
  265. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_server_db.py +0 -0
  266. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_sft_gc_off.py +0 -0
  267. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_thinking_config.py +0 -0
  268. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_update_check.py +0 -0
  269. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_version.py +0 -0
  270. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  271. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_vl_weight_sync.py +0 -0
  272. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_wandb_naming.py +0 -0
  273. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_warmstart_cross_repo.py +0 -0
  274. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_dryrun.py +0 -0
  275. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_hardexit.py +0 -0
  276. {freesolo_flash_dev-0.2.30 → freesolo_flash_dev-0.2.31}/tests/test_worker_image.py +0 -0
@@ -76,16 +76,23 @@ jobs:
76
76
  gpu_type_id: "NVIDIA RTX A6000",
77
77
  allowed_cuda: "",
78
78
  }
79
+ # sm89 (Ada): warm on the L40S -- a DATACENTER Ada card with reliable secure-cloud capacity --
80
+ # instead of the consumer RTX 4090 (scarce/cold on secure cloud, where the bake pod often never
81
+ # finished pulling the ~20GB image before the deadline). The cache is sm-keyed, so an L40S-warmed
82
+ # sm89 cache is valid on every sm89 card (4090/L40/L4). NB: verify the exact RunPod gpuTypeId.
79
83
  - {
80
84
  sm: sm89,
81
85
  arch: "8.9",
82
- gpu_type_id: "NVIDIA GeForce RTX 4090",
86
+ gpu_type_id: "NVIDIA L40S",
83
87
  allowed_cuda: "",
84
88
  }
89
+ # sm90 (Hopper): warm on the H200 (141GB, sm90) -- better secure-cloud capacity than the
90
+ # heavily-contended H100 80GB HBM3 (which cold-pull-stalled the bake). Same sm90, so the cache
91
+ # is valid on every H100/H200. H200 is in flash's validated GPU catalog (providers/base.py).
85
92
  - {
86
93
  sm: sm90,
87
94
  arch: "9.0",
88
- gpu_type_id: "NVIDIA H100 80GB HBM3",
95
+ gpu_type_id: "NVIDIA H200",
89
96
  allowed_cuda: "",
90
97
  }
91
98
  # Blackwell needs CUDA-13 hosts to JIT its PTX (matches min_cuda_for in the provider). Bake
@@ -63,7 +63,7 @@ RUN pip install --no-cache-dir \
63
63
  "tilelang==0.1.11" \
64
64
  "apache-tvm-ffi==0.1.11" \
65
65
  "hf_transfer" \
66
- "freesolo>=0.2.49" \
66
+ "freesolo>=0.2.52" \
67
67
  "runpod"
68
68
  # Gated-DeltaNet (Qwen3.5/3.6) runs on flash-linear-attention's Triton kernels. On consumer cards
69
69
  # (Ampere/Ada/Blackwell) the Triton path is correct. On Hopper (sm90) fla's GDN chunk_bwd is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.30
3
+ Version: 0.2.31
4
4
  Summary: Flash — managed LoRA post-training (SFT/GRPO) 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
@@ -18,19 +18,20 @@ Requires-Python: <3.13,>=3.11
18
18
  Provides-Extra: dev
19
19
  Requires-Dist: datasets>=2.19; extra == 'dev'
20
20
  Requires-Dist: fastapi; extra == 'dev'
21
- Requires-Dist: freesolo>=0.2.49; extra == 'dev'
21
+ Requires-Dist: freesolo>=0.2.52; extra == 'dev'
22
22
  Requires-Dist: httpx>=0.27; extra == 'dev'
23
23
  Requires-Dist: huggingface-hub>=0.34; extra == 'dev'
24
24
  Requires-Dist: mypy>=1.13.0; extra == 'dev'
25
25
  Requires-Dist: pytest>=9.0.3; extra == 'dev'
26
26
  Requires-Dist: ruff>=0.6; extra == 'dev'
27
27
  Requires-Dist: runpod-flash; extra == 'dev'
28
+ Requires-Dist: transformers<5.11,>=5.6; extra == 'dev'
28
29
  Requires-Dist: uvicorn; extra == 'dev'
29
30
  Provides-Extra: gpu
30
31
  Requires-Dist: accelerate>=1.4; extra == 'gpu'
31
32
  Requires-Dist: bitsandbytes>=0.49; extra == 'gpu'
32
33
  Requires-Dist: datasets>=2.19; extra == 'gpu'
33
- Requires-Dist: freesolo>=0.2.49; extra == 'gpu'
34
+ Requires-Dist: freesolo>=0.2.52; extra == 'gpu'
34
35
  Requires-Dist: huggingface-hub>=0.34; extra == 'gpu'
35
36
  Requires-Dist: peft>=0.19; extra == 'gpu'
36
37
  Requires-Dist: torch==2.10.0; extra == 'gpu'
@@ -40,10 +41,11 @@ Requires-Dist: vllm==0.19.1; extra == 'gpu'
40
41
  Provides-Extra: server
41
42
  Requires-Dist: datasets>=2.19; extra == 'server'
42
43
  Requires-Dist: fastapi; extra == 'server'
43
- Requires-Dist: freesolo>=0.2.49; extra == 'server'
44
+ Requires-Dist: freesolo>=0.2.52; extra == 'server'
44
45
  Requires-Dist: httpx>=0.27; extra == 'server'
45
46
  Requires-Dist: huggingface-hub>=0.34; extra == 'server'
46
47
  Requires-Dist: runpod-flash; extra == 'server'
48
+ Requires-Dist: transformers<5.11,>=5.6; extra == 'server'
47
49
  Requires-Dist: uvicorn; extra == 'server'
48
50
  Description-Content-Type: text/markdown
49
51
 
@@ -101,10 +103,10 @@ uv run flash-server # control plane (operator-side, run onc
101
103
 
102
104
  The control plane owns provider credentials: `RUNPOD_API_KEY` is always required,
103
105
  plus the shared `HF_TOKEN`.
104
- The artifact repo is platform-managed and per-run (each run gets its own
105
- `Freesolo-Co/flashrun-<run_id>`, written by the operator `HF_TOKEN`); it is not a user
106
- knob and not an operator-wide env var. Clients authenticate with their freesolo API key
107
- (`flash login`).
106
+ The artifact repo is platform-managed and environment-scoped (runs for the same environment share
107
+ one private `Freesolo-Co/flashrun-<environment>-<hash>` repo, written by the operator `HF_TOKEN`);
108
+ it is not a user knob and not an operator-wide env var. Clients authenticate with their freesolo
109
+ API key (`flash login`).
108
110
 
109
111
  ## Release channels
110
112
 
@@ -52,10 +52,10 @@ uv run flash-server # control plane (operator-side, run onc
52
52
 
53
53
  The control plane owns provider credentials: `RUNPOD_API_KEY` is always required,
54
54
  plus the shared `HF_TOKEN`.
55
- The artifact repo is platform-managed and per-run (each run gets its own
56
- `Freesolo-Co/flashrun-<run_id>`, written by the operator `HF_TOKEN`); it is not a user
57
- knob and not an operator-wide env var. Clients authenticate with their freesolo API key
58
- (`flash login`).
55
+ The artifact repo is platform-managed and environment-scoped (runs for the same environment share
56
+ one private `Freesolo-Co/flashrun-<environment>-<hash>` repo, written by the operator `HF_TOKEN`);
57
+ it is not a user knob and not an operator-wide env var. Clients authenticate with their freesolo
58
+ API key (`flash login`).
59
59
 
60
60
  ## Release channels
61
61
 
@@ -66,15 +66,19 @@ def main() -> int:
66
66
  ap = argparse.ArgumentParser(description="offload the kernel-cache warmup to a RunPod GPU")
67
67
  ap.add_argument("--arch", required=True, help="TORCH_CUDA_ARCH_LIST target, e.g. 9.0")
68
68
  ap.add_argument("--sm", required=True, help="sm tag, e.g. sm90 (must match the produced cache)")
69
- ap.add_argument("--gpu-type-id", required=True, help="RunPod gpuTypeId, e.g. 'NVIDIA H100 80GB HBM3'")
69
+ ap.add_argument(
70
+ "--gpu-type-id", required=True, help="RunPod gpuTypeId, e.g. 'NVIDIA H100 80GB HBM3'"
71
+ )
70
72
  ap.add_argument("--image", default="ghcr.io/freesolo-co/flash-worker:cu128")
71
73
  ap.add_argument("--out", default="build/kernel_cache")
72
74
  # the warm pod only pulls the ~20GB image + writes the cache (no model download), so keep this
73
75
  # modest -- an over-large ask shrinks the eligible host pool and trips "machine does not have the
74
76
  # resources" on scarce classes (e.g. Blackwell sm120 on secure cloud).
75
77
  ap.add_argument("--container-disk-gb", type=int, default=60)
76
- ap.add_argument("--deadline-min", type=int, default=45)
77
- ap.add_argument("--run-id", default="", help="unique suffix for the temp repo (default: time+uuid)")
78
+ ap.add_argument("--deadline-min", type=int, default=90)
79
+ ap.add_argument(
80
+ "--run-id", default="", help="unique suffix for the temp repo (default: time+uuid)"
81
+ )
78
82
  ap.add_argument(
79
83
  "--allowed-cuda",
80
84
  default="",
@@ -189,6 +193,59 @@ def main() -> int:
189
193
  shutil.move(s, d)
190
194
  shutil.rmtree(tmp, ignore_errors=True)
191
195
  rc = _verify(args.out, args.sm)
196
+ else:
197
+ # Non-success (timeout / pod_died): the pod-side out/ (STARTED marker, warmup.log) lives in
198
+ # the temp dataset the `finally` below deletes, so pull + print it FIRST. out/STARTED present
199
+ # = the warmup entrypoint ran (a timeout then means it hung / was slow); STARTED absent is
200
+ # AMBIGUOUS -- either the pod ran the wrong entrypoint, or the best-effort STARTED upload
201
+ # failed while the warmup is still running (the pod log disambiguates). warmup.log is only
202
+ # uploaded AFTER the warmup process returns, so it's present only if the warmup
203
+ # finished/was killed, not on a pure mid-run hang.
204
+ log(f"outcome={outcome}: pulling pod-side out/ for diagnostics before cleanup")
205
+ try:
206
+ dbg = os.path.join(args.out, ".dbg")
207
+ snapshot_download(
208
+ repo_id=repo,
209
+ repo_type="dataset",
210
+ allow_patterns=["out/**"],
211
+ local_dir=dbg,
212
+ token=token,
213
+ )
214
+ src = os.path.join(dbg, "out")
215
+ if os.path.isdir(src) and os.listdir(src):
216
+ started = os.path.isfile(os.path.join(src, "STARTED"))
217
+ log(f"warmup entrypoint ran (out/STARTED present): {started}")
218
+ for root, _, files in os.walk(src):
219
+ for f in sorted(files):
220
+ p = os.path.join(root, f)
221
+ log(f" out/{os.path.relpath(p, src)} ({os.path.getsize(p)} b)")
222
+ wl = os.path.join(src, "warmup.log")
223
+ if os.path.isfile(wl):
224
+ log("--- warmup.log tail (last 60 lines) ---")
225
+ with open(wl, errors="replace") as wlf:
226
+ for line in wlf.read().splitlines()[-60:]:
227
+ log(f" | {line}")
228
+ else:
229
+ log(
230
+ "no warmup.log (warmup never returned -> mid-run hang or still running at deadline)"
231
+ )
232
+ else:
233
+ # Ambiguous: out/STARTED is uploaded best-effort BEFORE warmup, and the cache tree
234
+ # only lands AFTER warmup returns. So an empty out/ means EITHER the entrypoint
235
+ # never ran (wrong CMD / docker_args) OR the STARTED upload failed while warmup is
236
+ # still mid-run. The pod retries that upload 3x, so a dropped marker is unlikely and
237
+ # wrong-entrypoint is the probable cause -- but don't assert it. The only proof is
238
+ # the pod's OWN console (its "[bake] uploaded out/STARTED" / WARNING lines), which
239
+ # this helper can't fetch (pod terminated below) -> read it in the RunPod dashboard.
240
+ log(
241
+ "pod produced NO out/ -> warmup entrypoint probably never ran "
242
+ "(wrong CMD / docker_args); the only other cause is a failed out/STARTED "
243
+ "upload mid-run (unlikely, it retries 3x) -- confirm via the pod console "
244
+ "in the RunPod dashboard (this CI log does not capture pod stdout)"
245
+ )
246
+ shutil.rmtree(dbg, ignore_errors=True)
247
+ except Exception as e:
248
+ log(f"diagnostic fetch failed (ignore): {str(e)[:160]}")
192
249
  finally:
193
250
  if pod_id:
194
251
  try:
@@ -28,6 +28,33 @@ def main() -> int:
28
28
  arch = os.environ.get("BAKE_ARCH", "")
29
29
  api = HfApi(token=token)
30
30
 
31
+ # Upload a STARTED marker FIRST -- before the code download / chalk install / warmup -- so a CI-side
32
+ # timeout can tell whether this entrypoint even ran. Present on a timeout = the warmup started (so it
33
+ # hung or was slow); absent = the pod ran the wrong entrypoint, OR this upload itself failed while
34
+ # the warmup is still running. Retry a few times so a transient HF blip doesn't drop the marker and
35
+ # make the helper misread a slow run as a wrong entrypoint.
36
+ started_uploaded = False
37
+ for attempt in range(3):
38
+ try:
39
+ api.upload_file(
40
+ path_or_fileobj=io.BytesIO(f"started arch={arch}\n".encode()),
41
+ path_in_repo="out/STARTED",
42
+ repo_id=repo,
43
+ repo_type="dataset",
44
+ )
45
+ started_uploaded = True
46
+ print("[bake] uploaded out/STARTED", flush=True)
47
+ break
48
+ except Exception as e:
49
+ # keep the "out/STARTED" token in the line so it greps the same as the success/WARNING lines
50
+ print(f"[bake] out/STARTED upload attempt {attempt + 1}/3 failed: {e}", flush=True)
51
+ if attempt < 2: # no point sleeping after the last attempt
52
+ time.sleep(3)
53
+ if not started_uploaded:
54
+ # The marker never landed; warn loudly so a later "no out/STARTED" is read as upload-failed,
55
+ # not as a wrong-entrypoint run.
56
+ print("[bake] WARNING: out/STARTED never uploaded; warmup still proceeds", flush=True)
57
+
31
58
  # the flash code the helper uploaded (upload_code -> code/flash); run the warmup from it.
32
59
  snapshot_download(
33
60
  repo_id=repo,
@@ -29,6 +29,18 @@ DEFAULT_GPU = "RTX 5090"
29
29
  _DEFAULT_VOCAB_SIZE = 248_320
30
30
 
31
31
 
32
+ @dataclass(frozen=True)
33
+ class ServingCapacity:
34
+ gpu: str
35
+ max_loras: int
36
+ max_lora_rank: int
37
+ max_model_len: int
38
+ serve_model_id: str = ""
39
+ max_num_seqs: int = 0
40
+ max_num_batched_tokens: int = 0
41
+ gpu_memory_utilization: float = 0.0
42
+
43
+
32
44
  @dataclass(frozen=True)
33
45
  class ModelInfo:
34
46
  id: str
@@ -61,6 +73,10 @@ class ModelInfo:
61
73
  notes: str = ""
62
74
  # 0 = platform default (64 GB) suffices. Runner raises gpu.disk_gb to at least this.
63
75
  min_disk_gb: int = 0
76
+ # Deployment capacity of the external freesolo multi-LoRA serving app. This is separate from
77
+ # Flash's training GPU recommendation above; serving uses Modal/vLLM and sizes hot LoRA buffers
78
+ # by max_loras x max_lora_rank at engine init.
79
+ serving: ServingCapacity | None = None
64
80
  # "none" / "hybrid" (Qwen3-style) / "always" (can't disable) / "unknown" (open-model policy)
65
81
  thinking: str = "none"
66
82
  vocab_size: int = _DEFAULT_VOCAB_SIZE
@@ -79,7 +95,20 @@ class ModelInfo:
79
95
  hidden_size: int = 0
80
96
 
81
97
  def to_dict(self) -> dict[str, Any]:
82
- return asdict(self)
98
+ data = asdict(self)
99
+ serving = data.get("serving")
100
+ if serving is None:
101
+ data.pop("serving", None)
102
+ else:
103
+ for key in (
104
+ "serve_model_id",
105
+ "max_num_seqs",
106
+ "max_num_batched_tokens",
107
+ "gpu_memory_utilization",
108
+ ):
109
+ if serving.get(key) in ("", 0, 0.0, None):
110
+ serving.pop(key, None)
111
+ return data
83
112
 
84
113
 
85
114
  DEFAULT_MODEL = "Qwen/Qwen3.5-4B"
@@ -94,6 +123,7 @@ MODELS: dict[str, ModelInfo] = {
94
123
  algos=("sft", "grpo"),
95
124
  min_vram_gb=12,
96
125
  recommended_gpu="RTX 4090",
126
+ serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
97
127
  thinking="hybrid",
98
128
  notes="On-device class SLM (131k ctx); standard Llama architecture.",
99
129
  ),
@@ -106,6 +136,7 @@ MODELS: dict[str, ModelInfo] = {
106
136
  algos=("sft", "grpo"),
107
137
  min_vram_gb=12,
108
138
  recommended_gpu="RTX 4090",
139
+ serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
109
140
  thinking="hybrid",
110
141
  notes="Smallest Qwen3.5; cheap smoke/dev runs with the modern arch.",
111
142
  ),
@@ -118,6 +149,7 @@ MODELS: dict[str, ModelInfo] = {
118
149
  algos=("sft", "grpo"),
119
150
  min_vram_gb=16,
120
151
  recommended_gpu="RTX 4090",
152
+ serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
121
153
  thinking="hybrid",
122
154
  ),
123
155
  "Qwen/Qwen3.5-4B": ModelInfo(
@@ -129,6 +161,15 @@ MODELS: dict[str, ModelInfo] = {
129
161
  algos=("sft", "grpo"),
130
162
  min_vram_gb=32,
131
163
  recommended_gpu="RTX 5090",
164
+ serving=ServingCapacity(
165
+ gpu="L4",
166
+ serve_model_id="lovedheart/Qwen3.5-4B-FP8",
167
+ max_loras=64,
168
+ max_lora_rank=32,
169
+ max_model_len=8192,
170
+ max_num_seqs=8,
171
+ gpu_memory_utilization=0.98,
172
+ ),
132
173
  thinking="hybrid",
133
174
  notes="Current-gen 4B. GRPO uses the sleep-mode memory recipe (hybrid arch needs "
134
175
  "extra engine state-cache); fused DeltaNet kernels ship in the default stack.",
@@ -145,6 +186,15 @@ MODELS: dict[str, ModelInfo] = {
145
186
  grpo_min_vram_gb=80,
146
187
  quant="bf16",
147
188
  recommended_gpu="A100 PCIe",
189
+ serving=ServingCapacity(
190
+ gpu="L4",
191
+ serve_model_id="lovedheart/Qwen3.5-9B-FP8",
192
+ max_loras=44,
193
+ max_lora_rank=32,
194
+ max_model_len=8192,
195
+ max_num_seqs=8,
196
+ gpu_memory_utilization=0.98,
197
+ ),
148
198
  thinking="hybrid",
149
199
  notes="bf16 LoRA. ~19 GB of weights; SFT fits a 48 GB card, while colocated GRPO "
150
200
  "(two bf16 copies + KV + the 248k-vocab fp32 logits) needs an 80 GB-class card "
@@ -174,6 +224,16 @@ MODELS: dict[str, ModelInfo] = {
174
224
  sleep_unsupported=True,
175
225
  quant="bf16",
176
226
  recommended_gpu="H200",
227
+ serving=ServingCapacity(
228
+ gpu="A100-80GB",
229
+ serve_model_id="Qwen/Qwen3.6-35B-A3B-FP8",
230
+ max_loras=12,
231
+ max_lora_rank=32,
232
+ max_model_len=8192,
233
+ max_num_seqs=8,
234
+ max_num_batched_tokens=4096,
235
+ gpu_memory_utilization=0.98,
236
+ ),
177
237
  thinking="hybrid",
178
238
  min_disk_gb=200,
179
239
  notes="MoE (35B total / ~3B active), bf16 LoRA. SFT runs on the 141 GB H200 (the ~70 GB "
@@ -199,6 +259,24 @@ def get_model(model_id: str) -> ModelInfo:
199
259
  ) from exc
200
260
 
201
261
 
262
+ def serving_lora_rank_cap(model: str | ModelInfo | None) -> int | None:
263
+ """Return the model's serving LoRA rank cap, or None when Flash has no local cap.
264
+
265
+ Serving capacity is model-specific: small serving models currently allow rank 64, while larger
266
+ serving paths can cap at rank 32. Unknown/open-policy models intentionally return None instead
267
+ of inheriting a global fallback.
268
+ """
269
+ if isinstance(model, ModelInfo):
270
+ info = model
271
+ elif isinstance(model, str) and model.strip():
272
+ info = MODELS.get(model.strip())
273
+ else:
274
+ info = None
275
+ if info is None or info.serving is None:
276
+ return None
277
+ return int(info.serving.max_lora_rank)
278
+
279
+
202
280
  def vocab_size_for(model_id: str) -> int:
203
281
  """Curated vocab_size for a model, or the safe default for open-model-policy entries."""
204
282
  info = MODELS.get(model_id)
@@ -338,7 +338,11 @@ def cmd_env_list(args) -> int:
338
338
 
339
339
 
340
340
  def _cmd_train_cost(args) -> int:
341
- """Print the pre-flight USD cost for the config and exit without submitting."""
341
+ """`flash train --cost`: print the pre-flight USD cost for the config and exit (no submit).
342
+
343
+ Catalog-only and deterministic; SFT uses the actual local training-token count when the env
344
+ and tokenizer are importable. An uncapped SFT run must be able to count the env's train split,
345
+ otherwise it errors instead of guessing a dataset size."""
342
346
  from flash.cost import estimate_cost
343
347
 
344
348
  spec = spec_from_file(
@@ -592,7 +592,8 @@ def cost_panel(est) -> str:
592
592
  ),
593
593
  (
594
594
  "setup",
595
- f"{est.setup_seconds / 60:.1f} min {_dim(f'(cold start: boot + deps + model load{setup_extra})')}",
595
+ f"{est.setup_seconds / 60:.1f} min "
596
+ f"{_dim(f'(cold start: boot + deps + model load{setup_extra}; not billed)')}",
596
597
  ),
597
598
  ("per step", f"{est.seconds_per_step:.2f} s"),
598
599
  (
@@ -601,6 +602,7 @@ def cost_panel(est) -> str:
601
602
  + (_paint(" [capped at wall-clock limit]", _RED) if est.wall_capped else ""),
602
603
  ),
603
604
  ("wall clock", f"{est.wall_clock_hours:.2f} h"),
605
+ ("billable", f"{est.billable_hours:.2f} h {_dim('(training only)')}"),
604
606
  ]
605
607
  panel = _kv(pairs)
606
608
  total = f" {_paint('TOTAL'.ljust(10), _GRAY, '1')} {_paint(_glyph('·', '-'), _FAINT)} {_paint(f'${est.total_usd:.2f}', _TEAL, '1')}"
@@ -302,9 +302,12 @@ Pick SFT when you already have good answers and want the model to imitate them.
302
302
  more.
303
303
  - **SFT is a great warm start for GRPO.** SFT first to teach the format and a competent
304
304
  baseline, then GRPO to optimize past it. Across that lineage keep the **same base
305
- model and the same `lora_rank` / `lora_alpha`** `init_from_adapter` loads a LoRA
306
- adapter specific to one base model and one adapter shape, so mixing sizes is an
307
- invalid shape mismatch.
305
+ model**. For text-only continued adapters, keep the same adapter shape. For VL
306
+ warm-starts, Flash trains a fresh GRPO LoRA and rank-stacks it with the SFT LoRA
307
+ for deployment, so `SFT rank + GRPO rank` must stay within the selected model's
308
+ effective serving `max_lora_rank`. That cap comes from the serving/model policy:
309
+ some small serving models allow rank 64, while larger serving paths can cap at rank
310
+ 32. Flash preflights the rank-stacked deploy rank against that model-specific cap.
308
311
 
309
312
  ```toml
310
313
  # configs/rl.toml — warm-start GRPO from the SFT run's adapter
@@ -314,8 +317,8 @@ algorithm = "grpo"
314
317
  # paste the full adapter_ref `flash status <sft-run-id>` prints, verbatim
315
318
  # (shape: <owner>/<repo>:sft/<run-id> — the owner/repo prefix is required)
316
319
  init_from_adapter = "your-org/your-repo:sft/<sft-run-id>"
317
- lora_rank = 32 # must match the SFT run
318
- lora_alpha = 64 # must match the SFT run
320
+ lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effective serving cap
321
+ lora_alpha = 32
319
322
  ```
320
323
 
321
324
  SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
@@ -1,4 +1,5 @@
1
- """Flash training-cost estimator: deterministic pre-flight estimate of wall-clock hours x $/hr."""
1
+ """Flash training-cost estimator: a deterministic, equation-based pre-flight estimate
2
+ (``estimate_cost``) of cost = training-only GPU hours x market $/hr. No output multiplier."""
2
3
 
3
4
  from __future__ import annotations
4
5
 
@@ -1,6 +1,9 @@
1
- """The analytical cost model: total = wall-clock hours x GPU $/hr, where wall = cold-start
2
- setup + steps x per-step time (a FLOPs/MFU estimate). GRPO splits each step into a vLLM
3
- rollout + reward grading + policy/reference update."""
1
+ """The analytical cost model: total = training-only GPU hours x GPU $/hr.
2
+
3
+ Elapsed wall clock still includes cold-start setup + steps x per-step time, but setup/cold-start
4
+ is reported as non-billable. GRPO splits each step into a vLLM rollout + reward grading +
5
+ policy/reference update.
6
+ """
4
7
 
5
8
  from __future__ import annotations
6
9
 
@@ -38,11 +41,11 @@ REWARD_CONCURRENCY = 16.0
38
41
 
39
42
  # Cold-start overhead (seconds): container boot + deps + model load (+ vLLM init for GRPO).
40
43
  #
41
- # Calibrated against a real fresh-worker run (0.8B SFT, RTX 3090 @ $0.239/hr) whose billed wall
44
+ # Calibrated against a real fresh-worker run (0.8B SFT, RTX 3090 @ $0.239/hr) whose elapsed wall
42
45
  # was ~708s for only ~26 priced steps -- i.e. cold start, not training, dominated. A fresh worker
43
46
  # spent ~12.5 min in `sft_model_load` alone (download + checkpoint deserialize + GPU placement +
44
- # framework/CUDA init), so the MODEL-LOAD term -- not boot/deps -- is the dominant cost of a short
45
- # job. MODEL_LOAD_BASE_S is the fixed (size-independent) load/init overhead; the download term on
47
+ # framework/CUDA init), so the MODEL-LOAD term -- not boot/deps -- dominates a short job's elapsed
48
+ # time. MODEL_LOAD_BASE_S is the fixed (size-independent) load/init overhead; the download term on
46
49
  # top of it scales with checkpoint size, so bigger models pay a longer cold start.
47
50
  WORKER_BOOT_S = 120.0 # container pull + start
48
51
  DEPS_INSTALL_S = 90.0 # pip/uv resolve + install
@@ -64,9 +67,9 @@ def _fmt_duration(seconds: float) -> str:
64
67
 
65
68
 
66
69
  def setup_seconds(config: RunConfig) -> float:
67
- """Cold-start wall time billed before the first optimizer step: container boot + deps + model
68
- load (a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for
69
- GRPO. The model-load term dominates a short job's bill (see the constants above)."""
70
+ """Cold-start wall time before the first optimizer step: container boot + deps + model load
71
+ (a fixed deserialize/placement/init base + a size-scaled download), plus vLLM init for GRPO.
72
+ This elapsed setup time is reported but not included in customer-facing cost."""
70
73
  model_load = MODEL_LOAD_BASE_S + download_weight_gb(config.model_id) / DOWNLOAD_RATE_GBPS
71
74
  s = WORKER_BOOT_S + DEPS_INSTALL_S + model_load
72
75
  if config.is_grpo:
@@ -96,6 +99,15 @@ def seconds_per_step(config: RunConfig, gpu: str) -> float:
96
99
  return gen_s + reward_s + update_s
97
100
 
98
101
 
102
+ def sft_seconds_for_tokens(config: RunConfig, gpu: str, train_tokens: float) -> float:
103
+ """SFT steady-state wall time for an actual token count on ``gpu``."""
104
+ n = config.normalized()
105
+ params = active_params_b(n.model_id) * 1e9
106
+ peak = gpu_tflops(gpu) * 1e12
107
+ flops = SFT_FLOPS_PER_TOKEN_PER_PARAM * params * train_tokens
108
+ return flops / (peak * MFU_SFT_TRAIN)
109
+
110
+
99
111
  def select_gpu(config: RunConfig) -> tuple[str, int]:
100
112
  """(chosen GPU class, required VRAM GB): the cheapest fitting class for the cost.
101
113
 
@@ -128,6 +140,8 @@ def _notes(config: RunConfig, raw_train_s: float, wall_capped: bool, cap_s: floa
128
140
  + (f", env {n.environment}" if n.environment else "")
129
141
  + ") + policy+reference update"
130
142
  )
143
+ elif n.train_tokens is not None:
144
+ notes.append(f"SFT priced on {n.train_tokens:,} actual train tokens")
131
145
  notes.append(f"GPU sized with {vram_headroom() - 1:.0%} VRAM headroom; static GPU $/hr")
132
146
  if wall_capped:
133
147
  notes.append(
@@ -141,14 +155,18 @@ def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S)
141
155
  """Deterministic pre-flight cost calculation."""
142
156
  gpu, need = select_gpu(config)
143
157
  hourly = gpu_hourly_usd(gpu, provider=config.provider)
144
- # Mirror the runner's max(60, max_wall_seconds) floor so a sub-60s cap isn't underpriced.
158
+ # Mirror the runner's max(60, max_wall_seconds) floor so elapsed wall time is not undercounted.
145
159
  cap_s = max(60.0, float(config.max_wall_seconds)) if config.max_wall_seconds is not None else wall_cap_s
146
160
 
147
161
  setup = setup_seconds(config)
148
162
  sps = seconds_per_step(config, gpu)
149
163
  raw_train = config.steps * sps
164
+ if not config.is_grpo and config.train_tokens is not None:
165
+ raw_train = sft_seconds_for_tokens(config, gpu, config.train_tokens)
166
+ sps = raw_train / config.steps
150
167
 
151
- # The cap is on total wall; setup is billed too, so clamp training to fit it.
168
+ # The cap is on total elapsed wall; setup is reported but not billed, so only training
169
+ # contributes to total_usd.
152
170
  wall_capped = (setup + raw_train) > cap_s
153
171
  setup = min(setup, cap_s)
154
172
  train = max(0.0, cap_s - setup) if wall_capped else raw_train
@@ -168,6 +186,6 @@ def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S)
168
186
  train_seconds=train,
169
187
  wall_clock_seconds=wall,
170
188
  wall_capped=wall_capped,
171
- total_usd=wall / 3600.0 * hourly,
189
+ total_usd=train / 3600.0 * hourly,
172
190
  notes=_notes(config, raw_train, wall_capped, cap_s),
173
191
  )
@@ -9,9 +9,11 @@ from flash.providers.base import GPU_INFO, GpuClass, providers_for
9
9
  GPU_COMPUTE_TFLOPS: dict[str, float] = {
10
10
  "RTX 4090": 165.0,
11
11
  "RTX 5090": 210.0,
12
- "RTX A6000": 155.0,
13
12
  "A100 PCIe": 312.0,
14
13
  "A100 SXM": 312.0,
14
+ # A100 SXM 40GB: same SMs/tensor cores as the 80GB A100 SXM, less HBM only (Lambda-only class).
15
+ # Without this, removing A6000 leaves the 33-40 GB Lambda pick falling back to _DEFAULT_TFLOPS.
16
+ "A100 SXM 40GB": 312.0,
15
17
  "H100": 990.0,
16
18
  # H200: same SMs/tensor cores as H100, more HBM only.
17
19
  "H200": 990.0,
@@ -56,7 +58,9 @@ def pick_gpu(required_vram_gb: int, *, provider: str | None = None) -> str:
56
58
  candidates = [g for g in GPU_INFO.values() if g.vram_gb >= required_vram_gb and _selectable(g)]
57
59
  if not candidates:
58
60
  raise ValueError(f"no GPU class fits >= {required_vram_gb} GB")
59
- best = min(candidates, key=lambda g: (gpu_hourly_usd(g.name, provider=provider), g.vram_gb, g.name))
61
+ best = min(
62
+ candidates, key=lambda g: (gpu_hourly_usd(g.name, provider=provider), g.vram_gb, g.name)
63
+ )
60
64
  return best.name
61
65
 
62
66