freesolo-flash-dev 0.2.31__tar.gz → 0.2.33__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 (273) hide show
  1. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/PKG-INFO +19 -17
  2. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/README.md +18 -16
  3. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/__init__.py +5 -3
  4. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/commands.py +8 -9
  5. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/render.py +23 -14
  6. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/training_doc.py +55 -10
  7. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cost/types.py +12 -3
  8. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/envs/adapter.py +41 -1
  9. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/base.py +32 -36
  10. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/pricing.py +0 -6
  11. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/pricing.py +0 -2
  12. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/train/__init__.py +12 -1
  13. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/train/endpoints.py +12 -5
  14. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/serve/deploy.py +20 -12
  15. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/serve/export.py +9 -13
  16. freesolo_flash_dev-0.2.33/flash/serve/pricing.py +94 -0
  17. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/routes/serving.py +7 -10
  18. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/pyproject.toml +2 -2
  19. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_commands.py +18 -7
  20. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_render_theme.py +13 -15
  21. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_pull.py +27 -3
  22. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_export.py +27 -1
  23. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_flash_worker.py +11 -8
  24. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_gpus.py +9 -39
  25. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_orchestrator_flash.py +77 -9
  26. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_serve.py +0 -19
  27. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_server_api.py +28 -9
  28. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_serving_contract.py +38 -3
  29. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/uv.lock +1 -1
  30. freesolo_flash_dev-0.2.31/flash/serve/pricing.py +0 -58
  31. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.dockerignore +0 -0
  32. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.env.example +0 -0
  33. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/auto-rebake.yml +0 -0
  34. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/bake-kernel-cache.yml +0 -0
  35. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/ci.yml +0 -0
  36. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/main-source-guard.yml +0 -0
  37. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/publish-dev.yml +0 -0
  38. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/publish-image.yml +0 -0
  39. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/publish.yml +0 -0
  40. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/version-parity.yml +0 -0
  41. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.github/workflows/worker-image.yml +0 -0
  42. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/.gitignore +0 -0
  43. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/Dockerfile +0 -0
  44. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/Dockerfile.worker +0 -0
  45. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/LICENSE +0 -0
  46. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/build/kernel_cache/.gitignore +0 -0
  47. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/build/kernel_cache/.keep +0 -0
  48. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/Dockerfile.kernelcache +0 -0
  49. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/Dockerfile.kernelcache.relayer +0 -0
  50. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/bake_kernel_cache.py +0 -0
  51. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/bake_pod_entry.py +0 -0
  52. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/kernel_fingerprint.py +0 -0
  53. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docker/make_rp_handler.py +0 -0
  54. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-style/README.md +0 -0
  55. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-style/generate.py +0 -0
  56. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-style/index.html +0 -0
  57. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-style/preview.png +0 -0
  58. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-style/themed-errors.png +0 -0
  59. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/README.md +0 -0
  60. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/before-after-dark.png +0 -0
  61. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/before-after-light.png +0 -0
  62. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/gallery-dark.png +0 -0
  63. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/gallery-light.png +0 -0
  64. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/help-dark.png +0 -0
  65. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/cli-theme/help-light.png +0 -0
  66. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/docs/kernel-cache.md +0 -0
  67. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/__init__.py +0 -0
  68. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/_channel.py +0 -0
  69. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/_fileio.py +0 -0
  70. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/_logging.py +0 -0
  71. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/_update_check.py +0 -0
  72. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/catalog.py +0 -0
  73. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/__main__.py +0 -0
  74. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cli/envpush.py +0 -0
  75. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/client/__init__.py +0 -0
  76. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/client/config.py +0 -0
  77. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/client/http.py +0 -0
  78. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/client/runtime_secrets.py +0 -0
  79. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/client/specs.py +0 -0
  80. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cost/__init__.py +0 -0
  81. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cost/analytical.py +0 -0
  82. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cost/facts.py +0 -0
  83. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/cost/spec.py +0 -0
  84. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/__init__.py +0 -0
  85. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/accounting.py +0 -0
  86. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/chalk_kernels.py +0 -0
  87. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/multiturn_rollout.py +0 -0
  88. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/recipe.py +0 -0
  89. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/vram.py +0 -0
  90. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/__init__.py +0 -0
  91. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/__main__.py +0 -0
  92. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/_pkg.py +0 -0
  93. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/adapter.py +0 -0
  94. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/decoding.py +0 -0
  95. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/finalize.py +0 -0
  96. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/gpu_setup.py +0 -0
  97. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/grpo.py +0 -0
  98. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/heartbeat.py +0 -0
  99. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/hf.py +0 -0
  100. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/kernel_warmup.py +0 -0
  101. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/lora.py +0 -0
  102. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/packing.py +0 -0
  103. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/__init__.py +0 -0
  104. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/attn.py +0 -0
  105. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/diagnostics.py +0 -0
  106. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/lifecycle.py +0 -0
  107. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/liger.py +0 -0
  108. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/loraplus.py +0 -0
  109. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/perf/memory.py +0 -0
  110. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/rl.py +0 -0
  111. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/sft.py +0 -0
  112. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/engine/worker/wandb_log.py +0 -0
  113. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/envs/__init__.py +0 -0
  114. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/envs/base.py +0 -0
  115. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/envs/pull.py +0 -0
  116. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/envs/registry.py +0 -0
  117. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/__init__.py +0 -0
  118. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/_auth.py +0 -0
  119. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/_http.py +0 -0
  120. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/_instance.py +0 -0
  121. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/_instance_bootstrap.py +0 -0
  122. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/_poll.py +0 -0
  123. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/allocator.py +0 -0
  124. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/__init__.py +0 -0
  125. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/api.py +0 -0
  126. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/auth.py +0 -0
  127. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/gpus.py +0 -0
  128. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  129. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  130. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/preflight.py +0 -0
  131. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/lambdalabs/train.py +0 -0
  132. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/preflight.py +0 -0
  133. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/realized.py +0 -0
  134. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/__init__.py +0 -0
  135. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/api.py +0 -0
  136. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/auth.py +0 -0
  137. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/cost.py +0 -0
  138. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/gpus.py +0 -0
  139. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/jobs.py +0 -0
  140. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/keys.py +0 -0
  141. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/preflight.py +0 -0
  142. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/preload.py +0 -0
  143. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/slots.py +0 -0
  144. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/providers/runpod/train/deps.py +0 -0
  145. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/py.typed +0 -0
  146. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/runner/__init__.py +0 -0
  147. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/runner/checkpoints.py +0 -0
  148. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/runner/deploy.py +0 -0
  149. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/runner/lifecycle.py +0 -0
  150. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/schema/__init__.py +0 -0
  151. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/schema/fields.py +0 -0
  152. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/serve/__init__.py +0 -0
  153. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/__init__.py +0 -0
  154. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/__main__.py +0 -0
  155. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/_deps.py +0 -0
  156. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/_internal_client.py +0 -0
  157. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/_locks.py +0 -0
  158. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/_runtime.py +0 -0
  159. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/app.py +0 -0
  160. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/auth.py +0 -0
  161. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/billing.py +0 -0
  162. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/billing_retry.py +0 -0
  163. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/checkpoints.py +0 -0
  164. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/db.py +0 -0
  165. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/environment_registry.py +0 -0
  166. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/envs.py +0 -0
  167. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/reconcile.py +0 -0
  168. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/routes/__init__.py +0 -0
  169. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/routes/envs.py +0 -0
  170. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/routes/meta.py +0 -0
  171. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/routes/runs.py +0 -0
  172. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/server/run_registry.py +0 -0
  173. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/flash/spec.py +0 -0
  174. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/infisical-entrypoint.sh +0 -0
  175. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/scripts/build_dev_dist.py +0 -0
  176. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/__init__.py +0 -0
  177. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/_helpers/__init__.py +0 -0
  178. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/_helpers/runner.py +0 -0
  179. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/_helpers/specs.py +0 -0
  180. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/conftest.py +0 -0
  181. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/fixtures/math_eval.jsonl +0 -0
  182. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/fixtures/math_train.jsonl +0 -0
  183. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/live/__init__.py +0 -0
  184. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/live/conftest.py +0 -0
  185. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/live/test_lambda_live.py +0 -0
  186. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/live/test_runpod_live.py +0 -0
  187. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_agent_flash_cli_contract.py +0 -0
  188. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_algorithms.py +0 -0
  189. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_allocator.py +0 -0
  190. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_b200_rollout_opt.py +0 -0
  191. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_backend_jobspec_contract.py +0 -0
  192. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_billing_retry.py +0 -0
  193. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cancel_remote.py +0 -0
  194. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_catalog_consistency.py +0 -0
  195. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_chalk_kernels.py +0 -0
  196. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_checkpoints.py +0 -0
  197. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_errors.py +0 -0
  198. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_estimate.py +0 -0
  199. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_help.py +0 -0
  200. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cli_managed.py +0 -0
  201. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_client.py +0 -0
  202. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_client_server_integration.py +0 -0
  203. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_config_overrides.py +0 -0
  204. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_analytical.py +0 -0
  205. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_equation.py +0 -0
  206. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_estimate.py +0 -0
  207. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_hardware.py +0 -0
  208. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_models.py +0 -0
  209. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_cost_rewards.py +0 -0
  210. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_dev_channel.py +0 -0
  211. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_disk_gb.py +0 -0
  212. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_endpoint_name.py +0 -0
  213. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_delete.py +0 -0
  214. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_no_install.py +0 -0
  215. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_publish.py +0 -0
  216. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_push.py +0 -0
  217. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_env_rate_limit_resolve.py +0 -0
  218. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_envs_coverage.py +0 -0
  219. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_flash_mvp.py +0 -0
  220. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_flashinfer_cache_dirs.py +0 -0
  221. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_github_urlopen_retry.py +0 -0
  222. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_grpo_mask_aware.py +0 -0
  223. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_grpo_params.py +0 -0
  224. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_grpo_sleep_gate.py +0 -0
  225. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_idle_endpoint_reaper.py +0 -0
  226. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_internal_client.py +0 -0
  227. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_jobs.py +0 -0
  228. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_kernel_cache.py +0 -0
  229. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_kernel_fingerprint.py +0 -0
  230. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_kv_util.py +0 -0
  231. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_lambda_runner.py +0 -0
  232. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_logging.py +0 -0
  233. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_login_perms.py +0 -0
  234. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_managed_hf_repo.py +0 -0
  235. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_metrics_schema_agent_contract.py +0 -0
  236. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_mig_guard.py +0 -0
  237. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_multiturn_rollout.py +0 -0
  238. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_oom_escalate_gpu.py +0 -0
  239. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_open_model_policy.py +0 -0
  240. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_packing.py +0 -0
  241. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_poll_helpers.py +0 -0
  242. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_preflight.py +0 -0
  243. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_provider_routing.py +0 -0
  244. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_provider_teardown_robustness.py +0 -0
  245. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_providers_symmetry.py +0 -0
  246. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_realized_cost.py +0 -0
  247. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_resolve_params_b.py +0 -0
  248. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_resume_on_retry.py +0 -0
  249. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_runmgmt.py +0 -0
  250. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_runpod_api_delete.py +0 -0
  251. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_runpod_key_fingerprint.py +0 -0
  252. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_runpod_key_waterfall.py +0 -0
  253. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_runpod_slots.py +0 -0
  254. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_server_billing.py +0 -0
  255. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_server_db.py +0 -0
  256. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_sft_gc_off.py +0 -0
  257. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_spec_and_validation.py +0 -0
  258. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_thinking_config.py +0 -0
  259. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_update_check.py +0 -0
  260. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_verifiers.py +0 -0
  261. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_version.py +0 -0
  262. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  263. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_vl_warmstart_recombine.py +0 -0
  264. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_vl_weight_sync.py +0 -0
  265. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_wandb_naming.py +0 -0
  266. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_warmstart_cross_repo.py +0 -0
  267. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_weight_cache.py +0 -0
  268. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_dryrun.py +0 -0
  269. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_hardexit.py +0 -0
  270. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_image.py +0 -0
  271. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_init_heartbeat.py +0 -0
  272. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_stack.py +0 -0
  273. {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.33}/tests/test_worker_thinking.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.31
3
+ Version: 0.2.33
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
@@ -51,15 +51,16 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  # Flash
53
53
 
54
- Managed LoRA post-training service: SFT and GRPO on managed RunPod Flash GPUs.
55
- The allocator picks the cheapest validated RunPod GPU class that fits the run.
54
+ Managed LoRA post-training service: SFT and GRPO on managed Flash GPUs.
55
+ The allocator picks the cheapest validated managed GPU class that fits the run.
56
56
 
57
57
  ## Scope
58
58
 
59
59
  - `flash train <cfg.toml>` / control-plane `POST /runs` — submit a training job;
60
60
  one dedicated GPU per run, supervised server-side (stall watchdog, bounded
61
61
  auto-retry resuming from the last streamed checkpoint, endpoint GC).
62
- - `flash deploy`, `flash chat` — serving for trained adapters.
62
+ - `flash checkpoints`, `flash deploy`, `flash chat` — serving for trained adapters,
63
+ including deployable intermediate RL checkpoints.
63
64
  - **Freesolo SDK environments.** Every run names a Freesolo environment id.
64
65
  Scaffold `environment.py` plus `datasets/train.jsonl`, upload `.` or another
65
66
  folder with `flash env push --name <name> <folder>`, then reference the
@@ -70,21 +71,22 @@ The allocator picks the cheapest validated RunPod GPU class that fits the run.
70
71
  ## Layout
71
72
 
72
73
  - `flash/catalog.py` — curated model catalog (Qwen3 dense supported tier;
73
- Qwen3.5/3.6 experimental tier) + `model_policy = "allow"` VRAM-fit check + each
74
- model's `thinking` capability (opt-in reasoning mode `thinking = true`)
74
+ Qwen3.5/3.6 experimental tier), VRAM-fit sizing, and each model's `thinking`
75
+ capability (opt-in reasoning mode `thinking = true`)
75
76
  - `flash/schema.py`, `flash/spec.py` — TOML → `JobSpec`
76
77
  - `flash/runner.py` — server-side run supervisor (durable job handle,
77
78
  retries, cost guard, endpoint GC)
78
- - `flash/providers/` — RunPod Flash provider code (pricing, gpus, durable
79
+ - `flash/providers/` — managed GPU substrate code (pricing, GPU classes, durable
79
80
  submit/poll, preflight) behind the `base.Provider` protocol, with an
80
- `allocator.py` that picks the cheapest fitting class
81
+ `allocator.py` that picks the cheapest fitting managed GPU class
81
82
  - `flash/engine/` — the on-GPU worker (TRL + colocated vLLM rollouts) and the
82
83
  shared recipe; SFT targets and RL rewards route through the active environment
83
84
  (task-specific grading lives with its example, not in the engine)
84
85
  - `flash/envs/` — environment machinery: registry and the adapter that loads
85
86
  Freesolo SDK environments onto the worker's interface
86
87
  - `flash env setup` — scaffold a starter local Freesolo env, `datasets/train.jsonl`,
87
- and ready-to-run configs to start from
88
+ ready-to-run configs, and a `TRAINING.md` playbook with common failure modes and
89
+ mitigations
88
90
  - `flash/serve/`, `flash/server/` — adapter serving and the FastAPI control
89
91
  plane (run operator-side via the separate `flash-server` command)
90
92
  - `Dockerfile` — the control-plane image (used by the repo docker-compose)
@@ -101,12 +103,12 @@ uv run flash --help
101
103
  uv run flash-server # control plane (operator-side, run once)
102
104
  ```
103
105
 
104
- The control plane owns provider credentials: `RUNPOD_API_KEY` is always required,
105
- plus the shared `HF_TOKEN`.
106
+ The control plane owns infrastructure credentials plus the shared `HF_TOKEN`.
106
107
  The artifact repo is platform-managed and environment-scoped (runs for the same environment share
107
108
  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`).
109
+ Flash uploads code under content-addressed prefixes and only reuses completed snapshots. The repo is
110
+ not a user knob and not an operator-wide env var. Clients authenticate with their freesolo API key
111
+ (`flash login`).
110
112
 
111
113
  ## Release channels
112
114
 
@@ -175,10 +177,10 @@ Use `choices[0].message.content` for the generated text. The run id is the adapt
175
177
  for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `409`
176
178
  with a hint to deploy first.
177
179
 
178
- Operators can also call the Modal serving app directly after the adapter is registered.
179
- The default serving app is `https://clado-ai--freesolo-lora-serving.modal.run`, and
180
- operators can point Flash at another serving app by setting `FREESOLO_SERVING_URL`.
181
- Use that same base URL when calling the app directly; pass the run id as `model`:
180
+ Operators can also call the serving backend directly after the adapter is registered.
181
+ The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
182
+ operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
183
+ Use that same base URL when calling the backend directly; pass the run id as `model`:
182
184
 
183
185
  ```bash
184
186
  export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
@@ -1,14 +1,15 @@
1
1
  # Flash
2
2
 
3
- Managed LoRA post-training service: SFT and GRPO on managed RunPod Flash GPUs.
4
- The allocator picks the cheapest validated RunPod GPU class that fits the run.
3
+ Managed LoRA post-training service: SFT and GRPO on managed Flash GPUs.
4
+ The allocator picks the cheapest validated managed GPU class that fits the run.
5
5
 
6
6
  ## Scope
7
7
 
8
8
  - `flash train <cfg.toml>` / control-plane `POST /runs` — submit a training job;
9
9
  one dedicated GPU per run, supervised server-side (stall watchdog, bounded
10
10
  auto-retry resuming from the last streamed checkpoint, endpoint GC).
11
- - `flash deploy`, `flash chat` — serving for trained adapters.
11
+ - `flash checkpoints`, `flash deploy`, `flash chat` — serving for trained adapters,
12
+ including deployable intermediate RL checkpoints.
12
13
  - **Freesolo SDK environments.** Every run names a Freesolo environment id.
13
14
  Scaffold `environment.py` plus `datasets/train.jsonl`, upload `.` or another
14
15
  folder with `flash env push --name <name> <folder>`, then reference the
@@ -19,21 +20,22 @@ The allocator picks the cheapest validated RunPod GPU class that fits the run.
19
20
  ## Layout
20
21
 
21
22
  - `flash/catalog.py` — curated model catalog (Qwen3 dense supported tier;
22
- Qwen3.5/3.6 experimental tier) + `model_policy = "allow"` VRAM-fit check + each
23
- model's `thinking` capability (opt-in reasoning mode `thinking = true`)
23
+ Qwen3.5/3.6 experimental tier), VRAM-fit sizing, and each model's `thinking`
24
+ capability (opt-in reasoning mode `thinking = true`)
24
25
  - `flash/schema.py`, `flash/spec.py` — TOML → `JobSpec`
25
26
  - `flash/runner.py` — server-side run supervisor (durable job handle,
26
27
  retries, cost guard, endpoint GC)
27
- - `flash/providers/` — RunPod Flash provider code (pricing, gpus, durable
28
+ - `flash/providers/` — managed GPU substrate code (pricing, GPU classes, durable
28
29
  submit/poll, preflight) behind the `base.Provider` protocol, with an
29
- `allocator.py` that picks the cheapest fitting class
30
+ `allocator.py` that picks the cheapest fitting managed GPU class
30
31
  - `flash/engine/` — the on-GPU worker (TRL + colocated vLLM rollouts) and the
31
32
  shared recipe; SFT targets and RL rewards route through the active environment
32
33
  (task-specific grading lives with its example, not in the engine)
33
34
  - `flash/envs/` — environment machinery: registry and the adapter that loads
34
35
  Freesolo SDK environments onto the worker's interface
35
36
  - `flash env setup` — scaffold a starter local Freesolo env, `datasets/train.jsonl`,
36
- and ready-to-run configs to start from
37
+ ready-to-run configs, and a `TRAINING.md` playbook with common failure modes and
38
+ mitigations
37
39
  - `flash/serve/`, `flash/server/` — adapter serving and the FastAPI control
38
40
  plane (run operator-side via the separate `flash-server` command)
39
41
  - `Dockerfile` — the control-plane image (used by the repo docker-compose)
@@ -50,12 +52,12 @@ uv run flash --help
50
52
  uv run flash-server # control plane (operator-side, run once)
51
53
  ```
52
54
 
53
- The control plane owns provider credentials: `RUNPOD_API_KEY` is always required,
54
- plus the shared `HF_TOKEN`.
55
+ The control plane owns infrastructure credentials plus the shared `HF_TOKEN`.
55
56
  The artifact repo is platform-managed and environment-scoped (runs for the same environment share
56
57
  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`).
58
+ Flash uploads code under content-addressed prefixes and only reuses completed snapshots. The repo is
59
+ not a user knob and not an operator-wide env var. Clients authenticate with their freesolo API key
60
+ (`flash login`).
59
61
 
60
62
  ## Release channels
61
63
 
@@ -124,10 +126,10 @@ Use `choices[0].message.content` for the generated text. The run id is the adapt
124
126
  for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `409`
125
127
  with a hint to deploy first.
126
128
 
127
- Operators can also call the Modal serving app directly after the adapter is registered.
128
- The default serving app is `https://clado-ai--freesolo-lora-serving.modal.run`, and
129
- operators can point Flash at another serving app by setting `FREESOLO_SERVING_URL`.
130
- Use that same base URL when calling the app directly; pass the run id as `model`:
129
+ Operators can also call the serving backend directly after the adapter is registered.
130
+ The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
131
+ operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
132
+ Use that same base URL when calling the backend directly; pass the run id as `model`:
131
133
 
132
134
  ```bash
133
135
  export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
@@ -66,7 +66,7 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
66
66
  "catalog",
67
67
  [
68
68
  ("models", "list supported base models"),
69
- ("gpus", "list managed GPU classes with live $/hr"),
69
+ ("gpus", "list managed GPU classes with estimated $/hr"),
70
70
  ],
71
71
  ),
72
72
  (
@@ -226,7 +226,7 @@ def _build_parser() -> argparse.ArgumentParser:
226
226
  models = sub.add_parser("models", help="list supported base models")
227
227
  models.set_defaults(func=cmd_models)
228
228
 
229
- gpus = sub.add_parser("gpus", help="list managed GPU classes with live $/hr")
229
+ gpus = sub.add_parser("gpus", help="list managed GPU classes with estimated $/hr")
230
230
  gpus.set_defaults(func=cmd_gpus)
231
231
 
232
232
  env = sub.add_parser("env", help="manage Freesolo environments")
@@ -268,7 +268,9 @@ def _build_parser() -> argparse.ArgumentParser:
268
268
  env_pull.set_defaults(func=cmd_env_pull)
269
269
 
270
270
  env_delete = env_sub.add_parser("delete", help="delete a published Freesolo environment")
271
- env_delete.add_argument("env_id", help="the Freesolo environment id to delete, e.g. you/your-env")
271
+ env_delete.add_argument(
272
+ "env_id", help="the Freesolo environment id to delete, e.g. you/your-env"
273
+ )
272
274
  env_delete.add_argument(
273
275
  "-y",
274
276
  "--yes",
@@ -236,8 +236,8 @@ def cmd_env_setup(args) -> int:
236
236
  "[train]\n"
237
237
  "steps = 150\n"
238
238
  "lora_rank = 32\n"
239
- "# GPU and the HF artifact repo are managed automatically by the platform: the GPU is\n"
240
- "# the cheapest fitting class across providers, and each run gets its own artifact repo.\n"
239
+ "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
240
+ "# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
241
241
  )
242
242
  sft = Path("configs/sft.toml")
243
243
  if not sft.exists():
@@ -248,8 +248,8 @@ def cmd_env_setup(args) -> int:
248
248
  "[train]\n"
249
249
  "epochs = 1\n"
250
250
  "lora_rank = 32\n"
251
- "# GPU and the HF artifact repo are managed automatically by the platform: the GPU is\n"
252
- "# the cheapest fitting class across providers, and each run gets its own artifact repo.\n"
251
+ "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
252
+ "# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
253
253
  )
254
254
  training = Path("TRAINING.md")
255
255
  if not training.exists():
@@ -280,7 +280,7 @@ def cmd_models(args) -> int:
280
280
 
281
281
 
282
282
  def cmd_gpus(args) -> int:
283
- """List RunPod GPU classes, VRAM, and $/hr."""
283
+ """List validated managed GPU classes, VRAM, and estimated $/hr."""
284
284
  from flash.providers.base import GPU_INFO
285
285
  from flash.providers.runpod.pricing import static_rates as runpod_static_rates
286
286
 
@@ -290,7 +290,7 @@ def cmd_gpus(args) -> int:
290
290
  )
291
291
  tip = (
292
292
  "Tip: GPU class selection is fully automatic — the submit-time allocator always picks the\n"
293
- "cheapest validated RunPod class that fits the model, so you don't pin a GPU type."
293
+ "cheapest validated managed class that fits the model, so you don't pin a GPU type."
294
294
  )
295
295
  if render.styled():
296
296
  rows = [(info.name, info.vram_gb, runpod_rates.get(info.name)) for info in infos]
@@ -300,7 +300,7 @@ def cmd_gpus(args) -> int:
300
300
  def fmt_rate(v: float | None) -> str:
301
301
  return f"{v:>10.2f}" if v else f"{'-':>10}"
302
302
 
303
- print(f"{'gpu':<16}{'vram':>6}{'runpod$/hr':>11}")
303
+ print(f"{'gpu':<16}{'vram':>6}{'$/hr':>11}")
304
304
  for info in infos:
305
305
  runpod_rate = runpod_rates.get(info.name)
306
306
  print(f"{info.name:<16}{info.vram_gb:>5}G{fmt_rate(runpod_rate):>11}")
@@ -483,8 +483,7 @@ def cmd_runs(args) -> int:
483
483
  spec = r.get("spec") or {}
484
484
  model = spec.get("model", "")
485
485
  algorithm = str(spec.get("algorithm") or "-").upper()
486
- gpu, provider = render._run_where(spec, r.get("remote") or {})
487
- where = f"{gpu}@{provider}" if provider else gpu
486
+ where = render._run_gpu(spec, r.get("remote") or {})
488
487
  print(
489
488
  f"{r['run_id']:<32} {r['state']:<11} {algorithm:<5} "
490
489
  f"{r.get('cost_usd', 0.0):>8.4f} {where:<22} {model}"
@@ -357,6 +357,21 @@ def _color_json(obj, depth: int) -> str:
357
357
  return _paint(json.dumps(obj), _GREEN)
358
358
 
359
359
 
360
+ def _hide_provider_metadata(obj):
361
+ """Styled CLI details are for humans; keep backend provider names out of that view."""
362
+ if isinstance(obj, dict):
363
+ return {
364
+ k: _hide_provider_metadata(v)
365
+ for k, v in obj.items()
366
+ if k.lower() not in {"provider", "flash_arm"}
367
+ }
368
+ if isinstance(obj, list):
369
+ return [_hide_provider_metadata(v) for v in obj]
370
+ if isinstance(obj, str) and obj.lower() in {"runpod", "lambda"}:
371
+ return "managed"
372
+ return obj
373
+
374
+
360
375
  def version(value: str) -> str:
361
376
  """The wordmark + version."""
362
377
  mark = _paint(CLI_NAME, _ACCENT, "1")
@@ -388,13 +403,9 @@ def gpus_table(rows: list[tuple[str, int, float | None]], tip: str) -> str:
388
403
  return _safe(f"{header('gpus', 'managed GPU classes')}\n{table}\n\n{_dim(tip)}")
389
404
 
390
405
 
391
- def _run_where(spec: dict, remote: dict) -> tuple[str, str]:
392
- """Return ``(gpu, provider)`` from remote (preferred) or spec fallback."""
393
- provider = remote.get("provider") or (
394
- "runpod" if remote else (spec.get("gpu") or {}).get("provider", "")
395
- )
396
- gpu = remote.get("gpu") or (spec.get("gpu") or {}).get("type", "")
397
- return gpu, provider
406
+ def _run_gpu(spec: dict, remote: dict) -> str:
407
+ """Human-facing GPU label. Provider metadata stays internal."""
408
+ return remote.get("gpu") or (spec.get("gpu") or {}).get("type", "")
398
409
 
399
410
 
400
411
  def runs_table(runs: list[dict]) -> str:
@@ -404,8 +415,7 @@ def runs_table(runs: list[dict]) -> str:
404
415
  spec = r.get("spec") or {}
405
416
  model = spec.get("model", "")
406
417
  algorithm = str(spec.get("algorithm") or "-").upper()
407
- gpu, provider = _run_where(spec, r.get("remote") or {})
408
- where = f"{gpu}@{provider}" if provider else gpu
418
+ where = _run_gpu(spec, r.get("remote") or {})
409
419
  color, uni, ascii_dot = _STATE_STYLE.get(str(r.get("state", "")).lower(), (_GRAY, "•", "-"))
410
420
  body.append(
411
421
  [
@@ -471,8 +481,7 @@ def _humanize_ts(value) -> str | None:
471
481
  def run_status(obj: dict) -> str:
472
482
  """A curated status panel for `flash status`, with the full JSON below for completeness."""
473
483
  spec = obj.get("spec") or {}
474
- gpu, provider = _run_where(spec, obj.get("remote") or {})
475
- where = f"{gpu} @ {provider}" if gpu and provider else (gpu or None)
484
+ where = _run_gpu(spec, obj.get("remote") or {}) or None
476
485
  pairs = [
477
486
  ("run id", _paint(obj.get("run_id", ""), _ACCENT2)),
478
487
  ("model", spec.get("model")),
@@ -492,7 +501,7 @@ def run_status(obj: dict) -> str:
492
501
  if obj.get("error"):
493
502
  pairs.append(("error", _paint(str(obj["error"]), _RED)))
494
503
  head = f"{header('status')}\n {badge(obj.get('state', 'unknown'))}\n\n{_kv(pairs)}"
495
- raw = f"{_dim('details')}\n{_json(obj)}"
504
+ raw = f"{_dim('details')}\n{_json(_hide_provider_metadata(obj))}"
496
505
  return _safe(f"{head}\n\n{raw}")
497
506
 
498
507
 
@@ -509,7 +518,7 @@ def object_panel(cmd: str, obj: dict, desc: str | None = None) -> str:
509
518
  if rid:
510
519
  line += " " + _paint(rid, _ACCENT2)
511
520
  parts.append(line + "\n")
512
- parts.append(_json(obj))
521
+ parts.append(_json(_hide_provider_metadata(obj)))
513
522
  return _safe("\n".join(parts))
514
523
 
515
524
 
@@ -586,7 +595,7 @@ def cost_panel(est) -> str:
586
595
  ),
587
596
  (
588
597
  "gpu",
589
- f"{est.gpu} on {est.provider} "
598
+ f"{est.gpu} "
590
599
  f"{_dim(f'({est.gpu_vram_gb} GB; needs >= {est.required_vram_gb} GB)')} "
591
600
  f"@ {money(est.gpu_hourly_usd, 2)}/hr",
592
601
  ),
@@ -20,10 +20,10 @@ actually produced, and decide — honestly — whether the model got better.
20
20
 
21
21
  Flash is a **managed** training service with a thin CLI/client. You author an
22
22
  environment (the task + its reward), publish it, and submit SFT or GRPO runs from a
23
- TOML config. Flash allocates the cheapest fitting GPU across providers, runs the job,
24
- streams logs back, and serves the result. You never handle provider credentials or a
25
- GPU — you authenticate once with a freesolo API key, and everything below is a `flash`
26
- CLI command.
23
+ TOML config. Flash allocates the cheapest fitting managed GPU class, runs the job,
24
+ streams logs back, and serves the result. You never handle infrastructure credentials
25
+ you authenticate once with a freesolo API key, and everything below is a `flash` CLI
26
+ command.
27
27
 
28
28
  ### Install & authenticate
29
29
 
@@ -32,7 +32,7 @@ pip install freesolo-flash # installs the `flash` CLI (import name is a
32
32
  flash login --api-key fslo_... # or: export FREESOLO_API_KEY=fslo_... (create a key at https://freesolo.co)
33
33
  flash whoami # confirm the identity behind your key
34
34
  flash models # supported base models (and which support `thinking`)
35
- flash gpus # managed GPU classes with live $/hr
35
+ flash gpus # managed GPU classes with estimated $/hr
36
36
  ```
37
37
 
38
38
  ### The project layout (`flash env setup` created this)
@@ -105,12 +105,14 @@ id = "your-org/my-env" # the id printed by `flash env push`
105
105
  steps = 150 # GRPO is step-driven; SFT is epoch-driven (epochs = N)
106
106
  lora_rank = 32
107
107
  lora_alpha = 64
108
+ # All GRPO/SFT knobs live under [train]. Do not add [grpo] or [sft] tables.
108
109
  ```
109
110
 
110
- GPU and the HF artifact repo are **fully managed** — there is no GPU knob; the allocator
111
- picks the cheapest class that fits, and each run gets its own artifact repo. Compose or
112
- tweak configs without editing files: `--config extra.toml` (deep-merge) and
113
- `--set key=value` (e.g. `--set train.steps=300`).
111
+ GPU and HF artifacts are **fully managed** — do not pick `gpu.type` or set
112
+ `train.hf_repo`; the allocator picks the cheapest validated managed GPU class that fits,
113
+ and run artifacts are stored in a private environment-scoped repo with content-addressed
114
+ Flash code snapshots. Compose or tweak configs without editing files: `--config
115
+ extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.steps=300`).
114
116
 
115
117
  ### 4. Submit
116
118
 
@@ -139,6 +141,7 @@ flash deploy <run-id> # serve the trained adapter (--step N for an in
139
141
  flash chat <run-id> -m "hello" # chat with the deployed adapter
140
142
  flash deployments # active serving endpoints
141
143
  flash undeploy <run-id> # tear the endpoint down
144
+ flash export --adapter-id <run-id> --repository <you>/<repo> # copy adapter weights to your HF repo
142
145
  ```
143
146
 
144
147
  The rest of this file is about doing the above *well* — designing a reward that teaches,
@@ -185,6 +188,32 @@ If any box is unchecked, the run is not done improving — keep training, don't
185
188
 
186
189
  ---
187
190
 
191
+ ## Common Flash issues and mitigations
192
+
193
+ Most bad Flash runs fail in a small number of predictable ways. Check these before
194
+ spending another GPU run:
195
+
196
+ | Issue | Symptom | Mitigation |
197
+ | --- | --- | --- |
198
+ | Environment id is blank or stale | `flash train --dry-run` fails, or the worker uses old reward/data | Run `flash env push --name my-env .` after every environment/data edit and paste the returned id into every config you submit. |
199
+ | Local-only env path in config | Config validation says there is no local path mode | Publish first, then use the returned slug in `[environment] id`. `flash train` only runs published env ids, not local paths. |
200
+ | Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `steps`, `epochs`, `group_size`, `max_tokens`, `temperature`, `max_length`, LoRA, and other training knobs under `[train]`. |
201
+ | Trying to pin managed infrastructure | `gpu.type`, `train.hf_repo`, or `model_policy` changes do not do what you expected | Treat GPU choice, model policy, and the run artifact repo as managed. Tune the model, algorithm, environment, and `[train]` knobs instead. |
202
+ | Secrets are not available on the worker | Reward code works locally but remote logs show missing API keys or auth failures | List secret names under `[environment] secrets = [...]`, export those env vars locally before submit, or put them in local `.env` / `.env.local`. Never put secret values in `[worker_env]` or hard-code them in the config. |
203
+ | Wrong model / thinking setting | Config validation fails, or chat behavior does not match the run | Use `flash models`; set `thinking = true` only for supported models. Thinking is a training-time/run-level choice and serving preserves that parity, so `flash chat` does not expose an override flag. |
204
+ | Thinking reward grades the wrong text | Rewards accidentally score hidden reasoning, or ignore reasoning you meant to inspect | By default, score the answer text. In thinking mode the response object is still string-compatible, but also exposes `.completion`, `.thinking`, and `.raw` when a reward intentionally needs those fields. |
205
+ | All-zero or flat GRPO reward | `reward_mean` stays near 0 and outputs do not improve | Make the reward dense: give partial credit for parse/format/execution/correctness tiers, and log a separate clean `success` metric. Do not keep rerunning an all-zero reward. |
206
+ | Reward rises but behavior is worse | Short, templated, malformed, or reward-hacked outputs score well | Deploy the adapter and probe real examples. Add hard validity gates before judge calls, penalize degenerate shortcuts, and judge the outcome rather than the surface string. |
207
+ | Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_tokens` for GRPO rollouts or `max_length` for SFT only after seeing truncation. Oversizing them by default just burns memory/cost. |
208
+ | Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash status <run-id> --logs`, reduce footprint (`max_length`, `max_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
209
+ | Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash status <run-id> --follow` to reattach, `--logs` for the console/error tail, or `flash cancel <run-id>` if you intentionally want to stop it. |
210
+ | Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id> --step N`, and compare with held-out probes before exporting or relying on the final adapter. |
211
+ | Export fails before upload | CLI says no HuggingFace token | Pass `flash export --api-key hf_...`, or set `HF_TOKEN` in your shell, `.env`, or `.env.local`. Exports are private unless you pass `--public`. |
212
+ | SFT loss improves but quality does not | Train loss falls while held-out behavior stalls or degrades | Keep a held-out split outside training. Deploy and score that split; if quality drops, reduce epochs or improve data instead of adding more passes. |
213
+ | Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, cap steps/epochs for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
214
+
215
+ ---
216
+
188
217
  ## Judge the run, don't just finish it
189
218
 
190
219
  - **Judge the trend, not a single number.** The proof of training is the curve:
@@ -259,6 +288,12 @@ attach is logged by name in the per-scorer breakdown — that is how the clean s
259
288
  rate becomes visible. Use the shaped `score` to confirm the model is learning *at all*,
260
289
  and judge the run on the explicit `success` metric.
261
290
 
291
+ When `thinking = true`, score the final answer unless you intentionally need the
292
+ reasoning trace. Flash passes a string-compatible response object to `score_response`;
293
+ `str(response_text)` is the answer text, while `response_text.completion`,
294
+ `response_text.thinking`, and `response_text.raw` are available for rewards that
295
+ explicitly inspect the separated completion, reasoning, or original raw model output.
296
+
262
297
  ### Reward rules that prevent silent failure
263
298
 
264
299
  - **Return `0.0` explicitly — never let scoring raise.** An uncaught exception in
@@ -418,7 +453,7 @@ on a beyond-noise improvement.
418
453
 
419
454
  ## Treat crashes as infra, not model size
420
455
 
421
- > A CUDA / OOM / vLLM / kernel / provider error is an **infrastructure** problem, not a
456
+ > A CUDA / OOM / vLLM / kernel / infrastructure error is an **infrastructure** problem, not a
422
457
  > sign the model is too big. Lower `max_length`, `max_tokens`, or `group_size` to shrink
423
458
  > the run's footprint and let the allocator retry onto the next fitting GPU class — do
424
459
  > **not** switch to a smaller model to make a crash disappear. That silently destroys
@@ -431,6 +466,8 @@ on a beyond-noise improvement.
431
466
  ```bash
432
467
  flash env setup # scaffold environment.py, datasets/, configs/, this file
433
468
  flash env push --name my-env . # publish the environment; paste the returned id into [environment]
469
+ flash env pull your-org/my-env # download a published environment into the current folder
470
+ flash env delete your-org/my-env -y # delete a published environment
434
471
  flash train configs/rl.toml --dry-run # validate the config locally (no GPU, no charge)
435
472
  flash train configs/rl.toml --cost # pre-flight USD estimate, then exit
436
473
  flash train configs/rl.toml # submit and follow logs (Ctrl-C detaches; --background to skip following)
@@ -438,7 +475,15 @@ flash status <run-id> # state + accrued cost
438
475
  flash status <run-id> --logs # reward/loss trend + worker console/error logs
439
476
  flash status <run-id> --follow # stream a live run to completion
440
477
  flash runs # list your runs and their state/cost
478
+ flash cancel <run-id> # stop a live run
479
+ flash checkpoints <run-id> # list deployable RL checkpoints
441
480
  flash deploy <run-id> # serve the trained adapter
481
+ flash deploy <run-id> --step N # serve a specific RL checkpoint
482
+ flash chat <run-id> -m "probe" # stream a reply from the deployed adapter
483
+ flash deployments # list active serving deployments
484
+ flash undeploy <run-id> # tear down an active deployment
485
+ flash export --adapter-id <run-id> --repository <you>/<repo> # export final adapter
486
+ flash export --adapter-id <run-id> --repository <you>/<repo> --step N # export a checkpoint
442
487
  ```
443
488
 
444
489
  See the full reference at https://freesolo.co/docs.
@@ -41,7 +41,9 @@ class RunConfig:
41
41
  # substrate up front (else it filters out every candidate -> confusing "no GPU fits").
42
42
  prov = (self.provider or "auto").strip().lower() or "auto"
43
43
  if prov not in ("auto", *PROVIDER_NAMES):
44
- raise ValueError(f"unknown provider {self.provider!r} (auto, {', '.join(PROVIDER_NAMES)})")
44
+ raise ValueError(
45
+ f"unknown provider {self.provider!r} (auto, {', '.join(PROVIDER_NAMES)})"
46
+ )
45
47
  object.__setattr__(self, "provider", prov)
46
48
  if self.steps < 1:
47
49
  raise ValueError(f"steps must be >= 1, got {self.steps}")
@@ -86,7 +88,14 @@ class RunConfig:
86
88
  comp = None
87
89
  batch = self.batch_size if self.batch_size is not None else RECIPE.sft.effective_batch
88
90
  group = None
89
- return replace(self, seq_len=seq, completion_len=comp, batch_size=batch, group_size=group, lora_rank=lora)
91
+ return replace(
92
+ self,
93
+ seq_len=seq,
94
+ completion_len=comp,
95
+ batch_size=batch,
96
+ group_size=group,
97
+ lora_rank=lora,
98
+ )
90
99
 
91
100
  def train_knobs(self) -> dict[str, int]:
92
101
  """The knob dict ``model_required_vram_gb`` consumes. Only an EXPLICIT batch_size is
@@ -141,7 +150,7 @@ class CostEstimate:
141
150
  """Multi-line itemized breakdown for CLI output."""
142
151
  lines = [
143
152
  f"Run : {self.model_id} [{self.method.upper()}, {self.steps} steps]",
144
- f"GPU : {self.gpu} on {self.provider} "
153
+ f"GPU : {self.gpu} "
145
154
  f"({self.gpu_vram_gb} GB; run needs >= {self.required_vram_gb} GB) "
146
155
  f"@ ${self.gpu_hourly_usd:.2f}/hr",
147
156
  f"Setup : {self.setup_seconds / 60:.1f} min (cold start: boot + deps + model load"
@@ -410,6 +410,45 @@ def _github_response_message(payload: object) -> str:
410
410
  return ""
411
411
 
412
412
 
413
+ def _github_tree_entries(ref: GitHubEnvironmentRef, treeish: str, context: str) -> list[dict]:
414
+ payload = _download_github_json(ref, _github_tree_url(ref, treeish), context)
415
+ if not isinstance(payload, dict) or not isinstance(payload.get("tree"), list):
416
+ raise RuntimeError(
417
+ f"GitHub path {context!r} is not an environment directory"
418
+ f"{_github_response_message(payload)}"
419
+ )
420
+ if payload.get("truncated"):
421
+ raise RuntimeError(
422
+ f"GitHub tree response for environment directory {context!r} was truncated"
423
+ )
424
+ entries = payload["tree"]
425
+ if not all(isinstance(entry, dict) for entry in entries):
426
+ raise RuntimeError("GitHub tree response included an invalid entry")
427
+ return entries
428
+
429
+
430
+ def _resolve_github_directory_tree_sha(ref: GitHubEnvironmentRef, repo_dir: str) -> str:
431
+ treeish = ref.ref
432
+ current = ""
433
+ for part in [part for part in repo_dir.split("/") if part]:
434
+ entries = _github_tree_entries(ref, treeish, current or ref.ref)
435
+ match = next(
436
+ (
437
+ entry
438
+ for entry in entries
439
+ if entry.get("path") == part
440
+ and entry.get("type") == "tree"
441
+ and isinstance(entry.get("sha"), str)
442
+ ),
443
+ None,
444
+ )
445
+ current = f"{current}/{part}" if current else part
446
+ if match is None:
447
+ raise RuntimeError(f"GitHub path {repo_dir!r} is not an environment directory")
448
+ treeish = match["sha"]
449
+ return treeish
450
+
451
+
413
452
  def _download_github_directory(ref: GitHubEnvironmentRef, repo_dir: str, dest: Path) -> Path:
414
453
  """Download one GitHub directory into a repo-shaped tree under ``dest``."""
415
454
  repo_root = dest / "repo"
@@ -464,9 +503,10 @@ def _download_github_directory(ref: GitHubEnvironmentRef, repo_dir: str, dest: P
464
503
  (repo_root / path).mkdir(parents=True, exist_ok=True)
465
504
 
466
505
  create_dir(repo_dir)
506
+ package_tree_sha = _resolve_github_directory_tree_sha(ref, repo_dir)
467
507
  payload = _download_github_json(
468
508
  ref,
469
- _github_tree_url(ref, f"{ref.ref}:{repo_dir}", recursive=True),
509
+ _github_tree_url(ref, package_tree_sha, recursive=True),
470
510
  repo_dir,
471
511
  )
472
512
  if not isinstance(payload, dict) or not isinstance(payload.get("tree"), list):