freesolo-flash-dev 0.2.35__tar.gz → 0.2.36__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 (271) hide show
  1. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/Dockerfile.worker +1 -1
  2. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/PKG-INFO +4 -4
  3. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/catalog.py +13 -20
  4. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/commands.py +25 -7
  5. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/client/http.py +18 -2
  6. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/__init__.py +0 -4
  7. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/registry.py +1 -1
  8. freesolo_flash_dev-0.2.36/flash/lora_rank.py +193 -0
  9. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/train/deps.py +1 -1
  10. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/runner/__init__.py +24 -4
  11. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/runner/checkpoints.py +21 -0
  12. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/runner/deploy.py +37 -20
  13. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/schema/__init__.py +7 -2
  14. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/serve/deploy.py +2 -19
  15. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/_runtime.py +23 -0
  16. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/app.py +1 -0
  17. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/routes/serving.py +117 -4
  18. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/pyproject.toml +6 -6
  19. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_catalog_consistency.py +18 -24
  20. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_checkpoints.py +52 -1
  21. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_commands.py +25 -1
  22. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_grpo_params.py +1 -0
  23. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_jobs.py +104 -0
  24. freesolo_flash_dev-0.2.36/tests/test_lora_rank_preflight.py +97 -0
  25. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_serve.py +7 -7
  26. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_server_api.py +193 -0
  27. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_spec_and_validation.py +6 -6
  28. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_verifiers.py +1 -1
  29. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_vl_warmstart_recombine.py +11 -10
  30. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/uv.lock +8 -8
  31. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.dockerignore +0 -0
  32. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.env.example +0 -0
  33. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/auto-rebake.yml +0 -0
  34. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/bake-kernel-cache.yml +0 -0
  35. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/ci.yml +0 -0
  36. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/main-source-guard.yml +0 -0
  37. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/notify-tests-repo.yml +0 -0
  38. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/publish-dev.yml +0 -0
  39. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/publish-image.yml +0 -0
  40. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/publish.yml +0 -0
  41. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/version-parity.yml +0 -0
  42. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.github/workflows/worker-image.yml +0 -0
  43. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/.gitignore +0 -0
  44. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/Dockerfile +0 -0
  45. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/LICENSE +0 -0
  46. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/README.md +0 -0
  47. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/build/kernel_cache/.gitignore +0 -0
  48. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/build/kernel_cache/.keep +0 -0
  49. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/Dockerfile.kernelcache +0 -0
  50. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/Dockerfile.kernelcache.relayer +0 -0
  51. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/bake_kernel_cache.py +0 -0
  52. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/bake_pod_entry.py +0 -0
  53. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/kernel_fingerprint.py +0 -0
  54. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docker/make_rp_handler.py +0 -0
  55. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/cli-style/README.md +0 -0
  56. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/cli-style/generate.py +0 -0
  57. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/cli-style/index.html +0 -0
  58. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/cli-style/preview.png +0 -0
  59. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/cli-style/themed-errors.png +0 -0
  60. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/docs/kernel-cache.md +0 -0
  61. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/__init__.py +0 -0
  62. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/_channel.py +0 -0
  63. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/_fileio.py +0 -0
  64. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/_logging.py +0 -0
  65. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/_update_check.py +0 -0
  66. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/__init__.py +0 -0
  67. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/__main__.py +0 -0
  68. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/envpush.py +0 -0
  69. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/render.py +0 -0
  70. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cli/training_doc.py +0 -0
  71. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/client/__init__.py +0 -0
  72. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/client/config.py +0 -0
  73. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/client/runtime_secrets.py +0 -0
  74. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/client/specs.py +0 -0
  75. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cost/__init__.py +0 -0
  76. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cost/analytical.py +0 -0
  77. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cost/facts.py +0 -0
  78. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cost/spec.py +0 -0
  79. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/cost/types.py +0 -0
  80. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/__init__.py +0 -0
  81. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/accounting.py +0 -0
  82. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/chalk_kernels.py +0 -0
  83. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/multiturn_rollout.py +0 -0
  84. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/recipe.py +0 -0
  85. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/vram.py +0 -0
  86. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/__init__.py +0 -0
  87. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/__main__.py +0 -0
  88. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/_pkg.py +0 -0
  89. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/adapter.py +0 -0
  90. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/decoding.py +0 -0
  91. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/finalize.py +0 -0
  92. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/gpu_setup.py +0 -0
  93. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/grpo.py +0 -0
  94. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/heartbeat.py +0 -0
  95. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/hf.py +0 -0
  96. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/kernel_warmup.py +0 -0
  97. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/lora.py +0 -0
  98. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/packing.py +0 -0
  99. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/attn.py +0 -0
  100. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/diagnostics.py +0 -0
  101. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/lifecycle.py +0 -0
  102. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/liger.py +0 -0
  103. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/loraplus.py +0 -0
  104. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/perf/memory.py +0 -0
  105. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/rl.py +0 -0
  106. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/sft.py +0 -0
  107. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/engine/worker/wandb_log.py +0 -0
  108. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/__init__.py +0 -0
  109. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/adapter.py +0 -0
  110. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/base.py +0 -0
  111. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/loader.py +0 -0
  112. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/envs/pull.py +0 -0
  113. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/__init__.py +0 -0
  114. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/_auth.py +0 -0
  115. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/_http.py +0 -0
  116. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/_instance.py +0 -0
  117. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/_instance_bootstrap.py +0 -0
  118. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/_poll.py +0 -0
  119. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/allocator.py +0 -0
  120. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/base.py +0 -0
  121. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/__init__.py +0 -0
  122. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/api.py +0 -0
  123. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/auth.py +0 -0
  124. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/gpus.py +0 -0
  125. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  126. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  127. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/preflight.py +0 -0
  128. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/pricing.py +0 -0
  129. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/lambdalabs/train.py +0 -0
  130. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/preflight.py +0 -0
  131. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/realized.py +0 -0
  132. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/__init__.py +0 -0
  133. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/api.py +0 -0
  134. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/auth.py +0 -0
  135. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/cost.py +0 -0
  136. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/gpus.py +0 -0
  137. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/jobs.py +0 -0
  138. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/keys.py +0 -0
  139. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/preflight.py +0 -0
  140. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/preload.py +0 -0
  141. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/pricing.py +0 -0
  142. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/slots.py +0 -0
  143. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/train/__init__.py +0 -0
  144. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/providers/runpod/train/endpoints.py +0 -0
  145. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/py.typed +0 -0
  146. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/runner/lifecycle.py +0 -0
  147. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/schema/fields.py +0 -0
  148. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/serve/__init__.py +0 -0
  149. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/serve/export.py +0 -0
  150. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/serve/pricing.py +0 -0
  151. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/__init__.py +0 -0
  152. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/__main__.py +0 -0
  153. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/_deps.py +0 -0
  154. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/_internal_client.py +0 -0
  155. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/_locks.py +0 -0
  156. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/auth.py +0 -0
  157. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/billing.py +0 -0
  158. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/billing_retry.py +0 -0
  159. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/checkpoints.py +0 -0
  160. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/db.py +0 -0
  161. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/environment_registry.py +0 -0
  162. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/envs.py +0 -0
  163. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/reconcile.py +0 -0
  164. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/routes/__init__.py +0 -0
  165. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/routes/envs.py +0 -0
  166. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/routes/meta.py +0 -0
  167. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/routes/runs.py +0 -0
  168. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/server/run_registry.py +0 -0
  169. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/flash/spec.py +0 -0
  170. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/infisical-entrypoint.sh +0 -0
  171. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/scripts/build_dev_dist.py +0 -0
  172. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/__init__.py +0 -0
  173. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/_helpers/__init__.py +0 -0
  174. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/_helpers/runner.py +0 -0
  175. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/_helpers/specs.py +0 -0
  176. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/conftest.py +0 -0
  177. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/fixtures/math_eval.jsonl +0 -0
  178. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/fixtures/math_train.jsonl +0 -0
  179. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/live/__init__.py +0 -0
  180. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/live/conftest.py +0 -0
  181. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/live/test_lambda_live.py +0 -0
  182. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/live/test_runpod_live.py +0 -0
  183. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_agent_flash_cli_contract.py +0 -0
  184. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_algorithms.py +0 -0
  185. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_allocator.py +0 -0
  186. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_b200_rollout_opt.py +0 -0
  187. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_backend_jobspec_contract.py +0 -0
  188. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_billing_retry.py +0 -0
  189. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cancel_remote.py +0 -0
  190. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_chalk_kernels.py +0 -0
  191. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_charge_pricing.py +0 -0
  192. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_errors.py +0 -0
  193. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_estimate.py +0 -0
  194. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_help.py +0 -0
  195. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_managed.py +0 -0
  196. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cli_render_theme.py +0 -0
  197. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_client.py +0 -0
  198. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_client_server_integration.py +0 -0
  199. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_config_overrides.py +0 -0
  200. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_analytical.py +0 -0
  201. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_equation.py +0 -0
  202. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_estimate.py +0 -0
  203. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_hardware.py +0 -0
  204. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_models.py +0 -0
  205. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_cost_rewards.py +0 -0
  206. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_dev_channel.py +0 -0
  207. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_disk_gb.py +0 -0
  208. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_endpoint_name.py +0 -0
  209. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_cache_evict.py +0 -0
  210. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_delete.py +0 -0
  211. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_no_install.py +0 -0
  212. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_publish.py +0 -0
  213. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_pull.py +0 -0
  214. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_push.py +0 -0
  215. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_env_rate_limit_resolve.py +0 -0
  216. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_envs_coverage.py +0 -0
  217. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_export.py +0 -0
  218. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_flash_mvp.py +0 -0
  219. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_flash_worker.py +0 -0
  220. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_flashinfer_cache_dirs.py +0 -0
  221. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_github_urlopen_retry.py +0 -0
  222. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_gpus.py +0 -0
  223. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_grpo_mask_aware.py +0 -0
  224. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_grpo_sleep_gate.py +0 -0
  225. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_idle_endpoint_reaper.py +0 -0
  226. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_internal_client.py +0 -0
  227. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_kernel_cache.py +0 -0
  228. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_kernel_fingerprint.py +0 -0
  229. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_kv_util.py +0 -0
  230. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_lambda_runner.py +0 -0
  231. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_logging.py +0 -0
  232. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_login_perms.py +0 -0
  233. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_managed_hf_repo.py +0 -0
  234. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_metrics_schema_agent_contract.py +0 -0
  235. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_mig_guard.py +0 -0
  236. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_multiturn_rollout.py +0 -0
  237. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_oom_escalate_gpu.py +0 -0
  238. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_open_model_policy.py +0 -0
  239. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_orchestrator_flash.py +0 -0
  240. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_packing.py +0 -0
  241. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_poll_helpers.py +0 -0
  242. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_preflight.py +0 -0
  243. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_provider_routing.py +0 -0
  244. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_provider_teardown_robustness.py +0 -0
  245. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_providers_symmetry.py +0 -0
  246. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_realized_cost.py +0 -0
  247. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_resolve_params_b.py +0 -0
  248. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_resume_on_retry.py +0 -0
  249. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_runmgmt.py +0 -0
  250. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_runpod_api_delete.py +0 -0
  251. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_runpod_key_fingerprint.py +0 -0
  252. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_runpod_key_waterfall.py +0 -0
  253. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_runpod_slots.py +0 -0
  254. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_server_billing.py +0 -0
  255. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_server_db.py +0 -0
  256. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_serving_contract.py +0 -0
  257. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_sft_gc_off.py +0 -0
  258. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_thinking_config.py +0 -0
  259. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_update_check.py +0 -0
  260. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_version.py +0 -0
  261. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  262. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_vl_weight_sync.py +0 -0
  263. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_wandb_naming.py +0 -0
  264. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_warmstart_cross_repo.py +0 -0
  265. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_weight_cache.py +0 -0
  266. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_dryrun.py +0 -0
  267. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_hardexit.py +0 -0
  268. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_image.py +0 -0
  269. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_init_heartbeat.py +0 -0
  270. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_stack.py +0 -0
  271. {freesolo_flash_dev-0.2.35 → freesolo_flash_dev-0.2.36}/tests/test_worker_thinking.py +0 -0
@@ -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.52" \
66
+ "freesolo>=0.2.54" \
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.35
3
+ Version: 0.2.36
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,7 +18,7 @@ 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.52; extra == 'dev'
21
+ Requires-Dist: freesolo>=0.2.54; 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'
@@ -31,7 +31,7 @@ Provides-Extra: gpu
31
31
  Requires-Dist: accelerate>=1.4; extra == 'gpu'
32
32
  Requires-Dist: bitsandbytes>=0.49; extra == 'gpu'
33
33
  Requires-Dist: datasets>=2.19; extra == 'gpu'
34
- Requires-Dist: freesolo>=0.2.52; extra == 'gpu'
34
+ Requires-Dist: freesolo>=0.2.54; extra == 'gpu'
35
35
  Requires-Dist: huggingface-hub>=0.34; extra == 'gpu'
36
36
  Requires-Dist: peft>=0.19; extra == 'gpu'
37
37
  Requires-Dist: torch==2.10.0; extra == 'gpu'
@@ -41,7 +41,7 @@ Requires-Dist: vllm==0.19.1; extra == 'gpu'
41
41
  Provides-Extra: server
42
42
  Requires-Dist: datasets>=2.19; extra == 'server'
43
43
  Requires-Dist: fastapi; extra == 'server'
44
- Requires-Dist: freesolo>=0.2.52; extra == 'server'
44
+ Requires-Dist: freesolo>=0.2.54; extra == 'server'
45
45
  Requires-Dist: httpx>=0.27; extra == 'server'
46
46
  Requires-Dist: huggingface-hub>=0.34; extra == 'server'
47
47
  Requires-Dist: runpod-flash; extra == 'server'
@@ -116,11 +116,8 @@ class ModelInfo:
116
116
  DEFAULT_MODEL = "Qwen/Qwen3.5-4B"
117
117
 
118
118
  SERVING_FP8_MODEL_REPOS: dict[str, str] = {
119
- "openbmb/MiniCPM5-1B": "Freesolo-Co/MiniCPM5-1B-FP8",
120
- "Qwen/Qwen3.5-0.8B": "Freesolo-Co/Qwen3.5-0.8B-FP8",
121
- "Qwen/Qwen3.5-2B": "Freesolo-Co/Qwen3.5-2B-FP8",
122
- "Qwen/Qwen3.5-4B": "Freesolo-Co/Qwen3.5-4B-FP8",
123
- "Qwen/Qwen3.5-9B": "Freesolo-Co/Qwen3.5-9B-FP8",
119
+ "Qwen/Qwen3.5-4B": "lovedheart/Qwen3.5-4B-FP8",
120
+ "Qwen/Qwen3.5-9B": "lovedheart/Qwen3.5-9B-FP8",
124
121
  "Qwen/Qwen3.6-35B-A3B": "Qwen/Qwen3.6-35B-A3B-FP8",
125
122
  }
126
123
 
@@ -136,9 +133,8 @@ MODELS: dict[str, ModelInfo] = {
136
133
  recommended_gpu="RTX 4090",
137
134
  serving=ServingCapacity(
138
135
  gpu="L4",
139
- serve_model_id=SERVING_FP8_MODEL_REPOS["openbmb/MiniCPM5-1B"],
140
136
  max_loras=16,
141
- max_lora_rank=128,
137
+ max_lora_rank=64,
142
138
  max_model_len=32768,
143
139
  ),
144
140
  thinking="hybrid",
@@ -155,9 +151,8 @@ MODELS: dict[str, ModelInfo] = {
155
151
  recommended_gpu="RTX 4090",
156
152
  serving=ServingCapacity(
157
153
  gpu="L4",
158
- serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-0.8B"],
159
154
  max_loras=16,
160
- max_lora_rank=128,
155
+ max_lora_rank=64,
161
156
  max_model_len=32768,
162
157
  ),
163
158
  thinking="hybrid",
@@ -174,9 +169,8 @@ MODELS: dict[str, ModelInfo] = {
174
169
  recommended_gpu="RTX 4090",
175
170
  serving=ServingCapacity(
176
171
  gpu="L4",
177
- serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-2B"],
178
172
  max_loras=16,
179
- max_lora_rank=128,
173
+ max_lora_rank=64,
180
174
  max_model_len=32768,
181
175
  ),
182
176
  thinking="hybrid",
@@ -191,10 +185,10 @@ MODELS: dict[str, ModelInfo] = {
191
185
  min_vram_gb=32,
192
186
  recommended_gpu="RTX 5090",
193
187
  serving=ServingCapacity(
194
- gpu="L40S",
188
+ gpu="L4",
195
189
  serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-4B"],
196
190
  max_loras=64,
197
- max_lora_rank=64,
191
+ max_lora_rank=32,
198
192
  max_model_len=8192,
199
193
  max_num_seqs=8,
200
194
  gpu_memory_utilization=0.98,
@@ -216,10 +210,10 @@ MODELS: dict[str, ModelInfo] = {
216
210
  quant="bf16",
217
211
  recommended_gpu="A100 PCIe",
218
212
  serving=ServingCapacity(
219
- gpu="L40S",
213
+ gpu="L4",
220
214
  serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-9B"],
221
215
  max_loras=44,
222
- max_lora_rank=64,
216
+ max_lora_rank=32,
223
217
  max_model_len=8192,
224
218
  max_num_seqs=8,
225
219
  gpu_memory_utilization=0.98,
@@ -254,14 +248,13 @@ MODELS: dict[str, ModelInfo] = {
254
248
  quant="bf16",
255
249
  recommended_gpu="H200",
256
250
  serving=ServingCapacity(
257
- gpu="A100-80GB:2",
251
+ gpu="A100-80GB",
258
252
  serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.6-35B-A3B"],
259
253
  max_loras=12,
260
- max_lora_rank=64,
254
+ max_lora_rank=32,
261
255
  max_model_len=8192,
262
256
  max_num_seqs=8,
263
257
  max_num_batched_tokens=4096,
264
- tensor_parallel_size=2,
265
258
  gpu_memory_utilization=0.98,
266
259
  ),
267
260
  thinking="hybrid",
@@ -292,8 +285,8 @@ def get_model(model_id: str) -> ModelInfo:
292
285
  def serving_lora_rank_cap(model: str | ModelInfo | None) -> int | None:
293
286
  """Return the model's serving LoRA rank cap, or None when Flash has no local cap.
294
287
 
295
- Serving capacity is model-specific: small serving models currently allow rank 128, while larger
296
- serving paths currently allow rank 64. Unknown/open-policy models intentionally return None instead
288
+ Serving capacity is model-specific: small serving models currently allow rank 64, while larger
289
+ serving paths currently allow rank 32. Unknown/open-policy models intentionally return None instead
297
290
  of inheriting a global fallback.
298
291
  """
299
292
  if isinstance(model, ModelInfo):
@@ -594,10 +594,17 @@ def cmd_deploy(args) -> int:
594
594
  print(render.arrow(url_note) if render.styled() else f"note: {url_note}", file=sys.stderr)
595
595
  if dep.get("state") != "dry_run":
596
596
  state = dep.get("state", "deploying")
597
- status_note = (
598
- f"deployment state is {state!r}; run `flash deployments` to check progress "
599
- "and use `flash chat` once it is ready."
600
- )
597
+ if state == "failed":
598
+ detail = str(dep.get("error") or dep.get("detail") or "unknown error")
599
+ status_note = (
600
+ f"deployment failed: {detail}; run `flash deployments` for details and "
601
+ f"retry `flash deploy {args.run_id}` after fixing the error."
602
+ )
603
+ else:
604
+ status_note = (
605
+ f"deployment state is {state!r}; run `flash deployments` to check progress "
606
+ "and use `flash chat` once it is ready."
607
+ )
601
608
  print(
602
609
  render.arrow(status_note) if render.styled() else f"note: {status_note}",
603
610
  file=sys.stderr,
@@ -608,7 +615,7 @@ def cmd_deploy(args) -> int:
608
615
  render.arrow(skip_note) if render.styled() else f"note: {skip_note}",
609
616
  file=sys.stderr,
610
617
  )
611
- return 0
618
+ return 1 if dep.get("state") == "failed" else 0
612
619
 
613
620
 
614
621
  def cmd_export(args) -> int:
@@ -678,6 +685,17 @@ def cmd_deployments(args) -> int:
678
685
 
679
686
 
680
687
  def cmd_chat(args) -> int:
688
+ from flash.schema import parse_checkpoint_ref
689
+
690
+ parsed = parse_checkpoint_ref(args.run_id)
691
+ if parsed is None:
692
+ print(
693
+ f"invalid run/checkpoint reference {args.run_id!r} "
694
+ "(expected <run_id> or <run_id>/step-N)",
695
+ file=sys.stderr,
696
+ )
697
+ return 1
698
+ base_run_id, _step = parsed
681
699
  client = client_from_config()
682
700
  messages = [{"role": "user", "content": args.message}]
683
701
  system = getattr(args, "system", None)
@@ -689,7 +707,7 @@ def cmd_chat(args) -> int:
689
707
  if stream is not None:
690
708
  wrote = False
691
709
  for chunk in stream(
692
- args.run_id,
710
+ base_run_id,
693
711
  messages=messages,
694
712
  temperature=args.temperature,
695
713
  max_tokens=args.max_tokens,
@@ -701,7 +719,7 @@ def cmd_chat(args) -> int:
701
719
  return 0
702
720
 
703
721
  resp = client.chat(
704
- args.run_id,
722
+ base_run_id,
705
723
  messages=messages,
706
724
  temperature=args.temperature,
707
725
  max_tokens=args.max_tokens,
@@ -301,10 +301,18 @@ class ApiClient:
301
301
  max_tokens: int = 512,
302
302
  timeout: float | None = None,
303
303
  ) -> dict:
304
+ from flash.schema import parse_checkpoint_ref
305
+
306
+ parsed = parse_checkpoint_ref(run_id)
307
+ if parsed is None:
308
+ raise ClientError(
309
+ "invalid run id: expected RUN_ID or RUN_ID/step-N for a deployed checkpoint"
310
+ )
311
+ base_run_id, _step = parsed
304
312
  _validate_chat_messages(messages)
305
313
  return self._request(
306
314
  "POST",
307
- f"/v1/runs/{run_id}/chat",
315
+ f"/v1/runs/{base_run_id}/chat",
308
316
  body={"messages": messages, "temperature": temperature, "max_tokens": max_tokens},
309
317
  timeout=timeout if timeout is not None else 30 * 60,
310
318
  )
@@ -316,12 +324,20 @@ class ApiClient:
316
324
  temperature: float = 0.0,
317
325
  max_tokens: int = 512,
318
326
  ) -> Iterator[str]:
327
+ from flash.schema import parse_checkpoint_ref
328
+
329
+ parsed = parse_checkpoint_ref(run_id)
330
+ if parsed is None:
331
+ raise ClientError(
332
+ "invalid run id: expected RUN_ID or RUN_ID/step-N for a deployed checkpoint"
333
+ )
334
+ base_run_id, _step = parsed
319
335
  _validate_chat_messages(messages)
320
336
  headers = {"Content-Type": "application/json"}
321
337
  if self.api_key:
322
338
  headers["Authorization"] = f"Bearer {self.api_key}"
323
339
  req = urllib.request.Request(
324
- f"{self.api_url}/v1/runs/{run_id}/chat",
340
+ f"{self.api_url}/v1/runs/{base_run_id}/chat",
325
341
  method="POST",
326
342
  data=json.dumps(
327
343
  {
@@ -14,8 +14,6 @@ import contextlib
14
14
  import os
15
15
  import sys
16
16
 
17
- # back-compat re-export from flash.engine.vram
18
- from flash.engine.vram import _LIGER_LONG_CTX_TOKENS, _LIGER_MIN_PARAMS_B
19
17
  from flash.engine.worker.perf.attn import (
20
18
  _attn_impl_for_capability,
21
19
  _flash_attn_3_available,
@@ -323,9 +321,7 @@ def _ensure_fla_fastpath_on_hopper() -> None:
323
321
 
324
322
  __all__ = [
325
323
  "RETRIABLE_INFRA_MARKER",
326
- "_LIGER_LONG_CTX_TOKENS",
327
324
  "_LIGER_MIN_PARAMS",
328
- "_LIGER_MIN_PARAMS_B",
329
325
  "_LONG_CONTEXT_TOKENS",
330
326
  "RetriableInfraError",
331
327
  "_GpuPeakSampler",
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from .base import Environment
6
6
 
7
- FREESOLO_WORKER_SPEC = "freesolo>=0.2.52"
7
+ FREESOLO_WORKER_SPEC = "freesolo>=0.2.54"
8
8
 
9
9
 
10
10
  def worker_pip_for_env(env_id: str) -> list[str]:
@@ -0,0 +1,193 @@
1
+ """LoRA adapter rank parsing and control-plane preflight helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Callable, Mapping
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from flash.catalog import serving_lora_rank_cap
10
+
11
+ if TYPE_CHECKING:
12
+ from flash.spec import JobSpec
13
+
14
+ _VL_WARMSTART_RECOMBINE_PREFIXES = ("Qwen/Qwen3.5-", "Qwen/Qwen3.6-")
15
+
16
+ AdapterConfigLoader = Callable[[str, str | None], Mapping[str, Any]]
17
+
18
+
19
+ def resolve_adapter_ref(adapter_ref: str) -> tuple[str, str] | None:
20
+ """Resolve an internal storage ref into ``(repo, artifact_prefix)``."""
21
+ from flash.schema import parse_adapter_storage_ref
22
+
23
+ return parse_adapter_storage_ref(adapter_ref)
24
+
25
+
26
+ def adapter_config_path_from_ref(adapter_ref: str) -> tuple[str, str]:
27
+ resolved = resolve_adapter_ref(adapter_ref)
28
+ if resolved is None:
29
+ raise ValueError(
30
+ "train.init_from_adapter could not be resolved to an internal adapter storage ref"
31
+ )
32
+ repo, prefix = resolved
33
+ return repo, f"{prefix}/adapter/adapter_config.json"
34
+
35
+
36
+ def _positive_int(value: Any, *, source: str, field: str) -> int:
37
+ if isinstance(value, bool):
38
+ raise ValueError(
39
+ f"could not verify adapter rank: {source} has invalid rank metadata ({field})"
40
+ )
41
+ try:
42
+ rank = int(value)
43
+ except (TypeError, ValueError) as exc:
44
+ raise ValueError(
45
+ f"could not verify adapter rank: {source} has invalid rank metadata ({field})"
46
+ ) from exc
47
+ if rank <= 0:
48
+ raise ValueError(f"could not verify adapter rank: {source} has non-positive rank {rank}")
49
+ return rank
50
+
51
+
52
+ def rank_from_adapter_config(config: Mapping[str, Any], *, source: str) -> int:
53
+ """Return the max LoRA rank advertised by PEFT adapter metadata."""
54
+ if not isinstance(config, Mapping):
55
+ raise ValueError(f"could not verify adapter rank: {source} is not a JSON object")
56
+ ranks: list[int] = []
57
+ if config.get("r") is not None:
58
+ ranks.append(_positive_int(config["r"], source=source, field="r"))
59
+ if "rank_pattern" in config and config["rank_pattern"] is not None:
60
+ rank_pattern = config["rank_pattern"]
61
+ if not isinstance(rank_pattern, Mapping):
62
+ raise ValueError(
63
+ f"could not verify adapter rank: {source} has invalid rank metadata "
64
+ "(rank_pattern)"
65
+ )
66
+ ranks.extend(
67
+ _positive_int(v, source=source, field="rank_pattern") for v in rank_pattern.values()
68
+ )
69
+ if not ranks:
70
+ raise ValueError(f"could not verify adapter rank: {source} has no LoRA rank metadata")
71
+ return max(ranks)
72
+
73
+
74
+ def uniform_rank_from_adapter_config(config: Mapping[str, Any], *, source: str) -> int:
75
+ """Return a uniform adapter rank for the VL SFT+GRPO recombine path."""
76
+ if not isinstance(config, Mapping):
77
+ raise ValueError(f"adapter rank preflight: {source} is not a JSON object")
78
+ if config.get("r") is None:
79
+ raise ValueError(
80
+ f"adapter rank preflight: {source} must contain a positive integer `r`"
81
+ )
82
+ rank = _positive_int(config["r"], source=source, field="r")
83
+ for key in ("rank_pattern", "alpha_pattern"):
84
+ pattern = config.get(key)
85
+ if pattern is None:
86
+ continue
87
+ if not isinstance(pattern, Mapping):
88
+ raise ValueError(
89
+ f"adapter rank preflight: {source} has invalid {key}={pattern!r}; "
90
+ "VL warm-start recombine requires a uniform LoRA rank/alpha"
91
+ )
92
+ if pattern:
93
+ raise ValueError(
94
+ f"adapter rank preflight: {source} has non-empty {key}={pattern!r}; "
95
+ "VL warm-start recombine requires a uniform LoRA rank/alpha"
96
+ )
97
+ return rank
98
+
99
+
100
+ def load_hf_adapter_config(adapter_ref: str, token: str | None = None) -> Mapping[str, Any]:
101
+ """Read ``adapter_config.json`` for a Flash adapter ref from Hugging Face datasets."""
102
+ repo, filename = adapter_config_path_from_ref(adapter_ref)
103
+ try:
104
+ from huggingface_hub import hf_hub_download
105
+ except ImportError as exc: # pragma: no cover - package extra is present in supported installs
106
+ raise ValueError(
107
+ "could not verify train.init_from_adapter rank: huggingface_hub is not installed"
108
+ ) from exc
109
+ try:
110
+ local = hf_hub_download(
111
+ repo_id=repo,
112
+ filename=filename,
113
+ repo_type="dataset",
114
+ token=token,
115
+ )
116
+ except Exception as exc:
117
+ raise ValueError(
118
+ f"could not verify train.init_from_adapter rank: failed to read {repo}:{filename}"
119
+ ) from exc
120
+ try:
121
+ with open(local, encoding="utf-8") as f:
122
+ config = json.load(f)
123
+ except Exception as exc:
124
+ raise ValueError(
125
+ f"could not verify train.init_from_adapter rank: invalid JSON in {repo}:{filename}"
126
+ ) from exc
127
+ if not isinstance(config, Mapping):
128
+ raise ValueError(
129
+ f"could not verify train.init_from_adapter rank: {repo}:{filename} "
130
+ "is not a JSON object"
131
+ )
132
+ return config
133
+
134
+
135
+ def _uses_vl_warmstart_recombine(model: str) -> bool:
136
+ return model.startswith(_VL_WARMSTART_RECOMBINE_PREFIXES)
137
+
138
+
139
+ def preflight_init_adapter_lora_rank(
140
+ spec: JobSpec,
141
+ *,
142
+ token: str | None = None,
143
+ config_loader: AdapterConfigLoader | None = None,
144
+ ) -> None:
145
+ """Reject warm-start adapters that cannot fit the model's serving LoRA rank cap.
146
+
147
+ The control plane calls this before creating/submitting a run. It intentionally uses only
148
+ ``adapter_config.json`` so the preflight stays CPU-only and catches undeployable adapters before
149
+ any training GPU is allocated.
150
+ """
151
+ adapter_storage_ref = (spec.train.init_from_adapter or "").strip()
152
+ if not adapter_storage_ref:
153
+ return
154
+ max_lora_rank = serving_lora_rank_cap(spec.model)
155
+ if max_lora_rank is None:
156
+ return
157
+
158
+ repo, filename = adapter_config_path_from_ref(adapter_storage_ref)
159
+ source = f"{repo}:{filename}"
160
+ loader = config_loader or load_hf_adapter_config
161
+ config = loader(adapter_storage_ref, token)
162
+
163
+ adapter_rank = rank_from_adapter_config(config, source=source)
164
+ if adapter_rank > max_lora_rank:
165
+ raise ValueError(
166
+ f"train.init_from_adapter={adapter_storage_ref!r} has rank {adapter_rank}, exceeding "
167
+ f"{spec.model}'s serving max_lora_rank={max_lora_rank}; use a lower-rank adapter "
168
+ "or raise the serving cap after real-GPU validation"
169
+ )
170
+
171
+ if spec.algorithm != "grpo" or not _uses_vl_warmstart_recombine(spec.model):
172
+ return
173
+
174
+ sft_rank = uniform_rank_from_adapter_config(config, source=source)
175
+ grpo_rank = int(spec.train.lora_rank)
176
+ recombined_rank = sft_rank + grpo_rank
177
+ if recombined_rank <= max_lora_rank:
178
+ return
179
+
180
+ allowed_grpo_rank = max_lora_rank - sft_rank
181
+ if allowed_grpo_rank >= 1:
182
+ guidance = f"set GRPO train.lora_rank <= {allowed_grpo_rank}"
183
+ else:
184
+ allowed_sft_rank = max_lora_rank - grpo_rank
185
+ if allowed_sft_rank >= 1:
186
+ guidance = f"retrain the SFT adapter at rank <= {allowed_sft_rank}"
187
+ else:
188
+ guidance = f"lower both SFT and GRPO ranks so their sum is <= {max_lora_rank}"
189
+ raise ValueError(
190
+ "train.init_from_adapter rank preflight failed: recombined SFT+GRPO adapter would be "
191
+ f"rank {recombined_rank} (SFT rank {sft_rank} + GRPO rank {grpo_rank}), exceeding "
192
+ f"{spec.model}'s serving max_lora_rank={max_lora_rank}; {guidance}"
193
+ )
@@ -30,7 +30,7 @@ WORKER_DEPS = [
30
30
  "flashinfer-python==0.6.6",
31
31
  "bitsandbytes>=0.49",
32
32
  "datasets>=4.7,<6",
33
- # >=0.2.52: includes robust JSONL loading and corrected package metadata.
33
+ # >=0.2.54: includes robust JSONL loading and corrected package metadata.
34
34
  FREESOLO_WORKER_SPEC,
35
35
  "huggingface_hub>=0.25",
36
36
  "accelerate>=1.4",
@@ -387,10 +387,7 @@ def _resolve_init_from_adapter(
387
387
  ref = spec.train.init_from_adapter
388
388
  if not ref:
389
389
  return spec
390
- from flash.schema import checkpoint_storage_ref, parse_adapter_storage_ref, parse_checkpoint_ref
391
-
392
- if parse_adapter_storage_ref(ref) is not None:
393
- return spec
390
+ from flash.schema import checkpoint_storage_ref, parse_checkpoint_ref
394
391
 
395
392
  parsed = parse_checkpoint_ref(ref)
396
393
  if parsed is None:
@@ -431,6 +428,25 @@ def _resolve_init_from_adapter(
431
428
  f"train.init_from_adapter references {src_run_id}/step-{step}, but that "
432
429
  "deployable checkpoint was not found"
433
430
  )
431
+ else:
432
+ if src_status.state not in {"done", "deployed"}:
433
+ raise ValueError(
434
+ f"train.init_from_adapter references run {src_run_id!r}, but that run is "
435
+ f"{src_status.state!r}; use a completed source run or a concrete "
436
+ f"{src_run_id}/step-N checkpoint"
437
+ )
438
+ from flash.runner.checkpoints import CheckpointListingError, final_adapter_exists
439
+
440
+ try:
441
+ exists = final_adapter_exists(src_spec)
442
+ except CheckpointListingError as exc:
443
+ raise ValueError(str(exc)) from exc
444
+ if not exists:
445
+ raise ValueError(
446
+ f"train.init_from_adapter references run {src_run_id!r}, but its final "
447
+ "adapter was not found; use a concrete checkpoint ref like "
448
+ f"{src_run_id}/step-N if one exists"
449
+ )
434
450
  storage = checkpoint_storage_ref(src_spec.train.hf_repo, src_spec.phase, src_run_id, step)
435
451
  return replace(spec, train=replace(spec.train, init_from_adapter=storage))
436
452
 
@@ -459,6 +475,10 @@ def submit_job(
459
475
  owner_org_id=owner_org_id,
460
476
  owner_key_id=owner_key_id,
461
477
  )
478
+ if not dry_run:
479
+ from flash.lora_rank import preflight_init_adapter_lora_rank
480
+
481
+ preflight_init_adapter_lora_rank(worker_spec, token=os.environ.get("HF_TOKEN"))
462
482
  # env ref->sha pin is deferred (background) or after status save (sync) — never on creation path.
463
483
  status = RunStatus(
464
484
  run_id=public_spec.run_id,
@@ -39,6 +39,15 @@ def _adapter_file_re(base: str) -> re.Pattern[str]:
39
39
  return re.compile(re.escape(base) + r"/checkpoints/step-(\d+)/adapter/([^/]+)$")
40
40
 
41
41
 
42
+ def _adapter_folder_has_required_files(files: list[str], prefix: str) -> bool:
43
+ names = {
44
+ path.removeprefix(f"{prefix}/adapter/")
45
+ for path in files
46
+ if path.startswith(f"{prefix}/adapter/") and "/" not in path.removeprefix(f"{prefix}/adapter/")
47
+ }
48
+ return "adapter_config.json" in names and not names.isdisjoint(_ADAPTER_WEIGHT_FILES)
49
+
50
+
42
51
  def _repo_files(spec: JobSpec, *, strict: bool) -> list[str]:
43
52
  repo = spec.train.hf_repo
44
53
  if not repo:
@@ -110,3 +119,15 @@ def checkpoint_step_exists(spec: JobSpec, step: int) -> bool:
110
119
  int(item.get("step", -1)) == int(step)
111
120
  for item in _checkpoints_from_files(spec, _repo_files(spec, strict=True))
112
121
  )
122
+
123
+
124
+ def final_adapter_exists(spec: JobSpec) -> bool:
125
+ """Authoritatively verify that the run-level final adapter exists in the artifact repo."""
126
+ try:
127
+ files = _repo_files(spec, strict=True)
128
+ except CheckpointListingError as exc:
129
+ cause = exc.__cause__ or exc
130
+ raise CheckpointListingError(
131
+ f"could not verify final adapter for {spec.run_id}: {cause}"
132
+ ) from cause
133
+ return _adapter_folder_has_required_files(files, adapter_prefix(spec))