freesolo-flash-dev 0.2.33__tar.gz → 0.2.35__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. freesolo_flash_dev-0.2.35/.github/workflows/notify-tests-repo.yml +35 -0
  2. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/PKG-INFO +1 -1
  3. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/catalog.py +44 -14
  4. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/__init__.py +34 -22
  5. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/commands.py +115 -44
  6. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/envpush.py +16 -3
  7. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/render.py +24 -8
  8. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/training_doc.py +18 -16
  9. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/client/http.py +40 -26
  10. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cost/facts.py +2 -0
  11. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/vram.py +52 -3
  12. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/adapter.py +14 -17
  13. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/hf.py +123 -37
  14. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/rl.py +3 -3
  15. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/sft.py +1 -1
  16. freesolo_flash_dev-0.2.35/flash/envs/adapter.py +447 -0
  17. freesolo_flash_dev-0.2.33/flash/envs/adapter.py → freesolo_flash_dev-0.2.35/flash/envs/loader.py +98 -398
  18. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/envs/pull.py +1 -1
  19. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/endpoints.py +64 -29
  20. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/runner/__init__.py +173 -16
  21. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/runner/checkpoints.py +44 -14
  22. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/runner/deploy.py +92 -15
  23. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/runner/lifecycle.py +37 -10
  24. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/schema/__init__.py +55 -5
  25. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/serve/deploy.py +173 -13
  26. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/_runtime.py +2 -2
  27. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/app.py +18 -1
  28. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/billing.py +26 -6
  29. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/billing_retry.py +33 -34
  30. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/envs.py +27 -7
  31. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/reconcile.py +10 -6
  32. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/routes/runs.py +1 -1
  33. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/routes/serving.py +242 -45
  34. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/pyproject.toml +2 -2
  35. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/conftest.py +29 -0
  36. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_agent_flash_cli_contract.py +2 -4
  37. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_billing_retry.py +61 -41
  38. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cancel_remote.py +123 -0
  39. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_catalog_consistency.py +28 -15
  40. freesolo_flash_dev-0.2.35/tests/test_charge_pricing.py +139 -0
  41. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_checkpoints.py +115 -1
  42. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_commands.py +108 -28
  43. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_render_theme.py +3 -5
  44. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_client.py +62 -64
  45. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_client_server_integration.py +6 -0
  46. freesolo_flash_dev-0.2.35/tests/test_env_cache_evict.py +70 -0
  47. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_publish.py +50 -0
  48. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_pull.py +1 -1
  49. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_push.py +10 -0
  50. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_rate_limit_resolve.py +8 -8
  51. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_flash_worker.py +1 -1
  52. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_github_urlopen_retry.py +1 -1
  53. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_gpus.py +18 -0
  54. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_grpo_params.py +54 -109
  55. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_jobs.py +361 -4
  56. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_kv_util.py +8 -0
  57. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_orchestrator_flash.py +6 -2
  58. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_serve.py +52 -11
  59. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_server_api.py +180 -13
  60. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_server_billing.py +50 -5
  61. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_serving_contract.py +191 -3
  62. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_spec_and_validation.py +6 -6
  63. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_verifiers.py +202 -12
  64. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_recombine.py +10 -10
  65. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_warmstart_cross_repo.py +21 -4
  66. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/uv.lock +1 -1
  67. freesolo_flash_dev-0.2.33/docs/cli-theme/README.md +0 -32
  68. freesolo_flash_dev-0.2.33/docs/cli-theme/before-after-dark.png +0 -0
  69. freesolo_flash_dev-0.2.33/docs/cli-theme/before-after-light.png +0 -0
  70. freesolo_flash_dev-0.2.33/docs/cli-theme/gallery-dark.png +0 -0
  71. freesolo_flash_dev-0.2.33/docs/cli-theme/gallery-light.png +0 -0
  72. freesolo_flash_dev-0.2.33/docs/cli-theme/help-dark.png +0 -0
  73. freesolo_flash_dev-0.2.33/docs/cli-theme/help-light.png +0 -0
  74. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.dockerignore +0 -0
  75. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.env.example +0 -0
  76. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/auto-rebake.yml +0 -0
  77. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/bake-kernel-cache.yml +0 -0
  78. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/ci.yml +0 -0
  79. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/main-source-guard.yml +0 -0
  80. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-dev.yml +0 -0
  81. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-image.yml +0 -0
  82. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/publish.yml +0 -0
  83. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/version-parity.yml +0 -0
  84. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.github/workflows/worker-image.yml +0 -0
  85. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/.gitignore +0 -0
  86. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/Dockerfile +0 -0
  87. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/Dockerfile.worker +0 -0
  88. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/LICENSE +0 -0
  89. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/README.md +0 -0
  90. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.gitignore +0 -0
  91. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.keep +0 -0
  92. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache +0 -0
  93. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache.relayer +0 -0
  94. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/bake_kernel_cache.py +0 -0
  95. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/bake_pod_entry.py +0 -0
  96. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/kernel_fingerprint.py +0 -0
  97. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docker/make_rp_handler.py +0 -0
  98. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/cli-style/README.md +0 -0
  99. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/cli-style/generate.py +0 -0
  100. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/cli-style/index.html +0 -0
  101. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/cli-style/preview.png +0 -0
  102. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/cli-style/themed-errors.png +0 -0
  103. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/docs/kernel-cache.md +0 -0
  104. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/__init__.py +0 -0
  105. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/_channel.py +0 -0
  106. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/_fileio.py +0 -0
  107. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/_logging.py +0 -0
  108. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/_update_check.py +0 -0
  109. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cli/__main__.py +0 -0
  110. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/client/__init__.py +0 -0
  111. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/client/config.py +0 -0
  112. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/client/runtime_secrets.py +0 -0
  113. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/client/specs.py +0 -0
  114. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cost/__init__.py +0 -0
  115. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cost/analytical.py +0 -0
  116. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cost/spec.py +0 -0
  117. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/cost/types.py +0 -0
  118. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/__init__.py +0 -0
  119. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/accounting.py +0 -0
  120. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/chalk_kernels.py +0 -0
  121. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/multiturn_rollout.py +0 -0
  122. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/recipe.py +0 -0
  123. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__init__.py +0 -0
  124. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__main__.py +0 -0
  125. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/_pkg.py +0 -0
  126. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/decoding.py +0 -0
  127. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/finalize.py +0 -0
  128. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/gpu_setup.py +0 -0
  129. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/grpo.py +0 -0
  130. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/heartbeat.py +0 -0
  131. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/kernel_warmup.py +0 -0
  132. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/lora.py +0 -0
  133. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/packing.py +0 -0
  134. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/__init__.py +0 -0
  135. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/attn.py +0 -0
  136. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/diagnostics.py +0 -0
  137. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/lifecycle.py +0 -0
  138. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/liger.py +0 -0
  139. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/loraplus.py +0 -0
  140. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/memory.py +0 -0
  141. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/engine/worker/wandb_log.py +0 -0
  142. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/envs/__init__.py +0 -0
  143. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/envs/base.py +0 -0
  144. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/envs/registry.py +0 -0
  145. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/__init__.py +0 -0
  146. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/_auth.py +0 -0
  147. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/_http.py +0 -0
  148. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/_instance.py +0 -0
  149. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/_instance_bootstrap.py +0 -0
  150. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/_poll.py +0 -0
  151. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/allocator.py +0 -0
  152. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/base.py +0 -0
  153. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/__init__.py +0 -0
  154. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/api.py +0 -0
  155. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/auth.py +0 -0
  156. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/gpus.py +0 -0
  157. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  158. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  159. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/preflight.py +0 -0
  160. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/pricing.py +0 -0
  161. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/train.py +0 -0
  162. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/preflight.py +0 -0
  163. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/realized.py +0 -0
  164. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/__init__.py +0 -0
  165. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/api.py +0 -0
  166. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/auth.py +0 -0
  167. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/cost.py +0 -0
  168. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/gpus.py +0 -0
  169. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/jobs.py +0 -0
  170. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/keys.py +0 -0
  171. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preflight.py +0 -0
  172. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preload.py +0 -0
  173. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/pricing.py +0 -0
  174. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/slots.py +0 -0
  175. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/__init__.py +0 -0
  176. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/deps.py +0 -0
  177. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/py.typed +0 -0
  178. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/schema/fields.py +0 -0
  179. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/serve/__init__.py +0 -0
  180. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/serve/export.py +0 -0
  181. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/serve/pricing.py +0 -0
  182. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/__init__.py +0 -0
  183. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/__main__.py +0 -0
  184. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/_deps.py +0 -0
  185. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/_internal_client.py +0 -0
  186. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/_locks.py +0 -0
  187. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/auth.py +0 -0
  188. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/checkpoints.py +0 -0
  189. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/db.py +0 -0
  190. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/environment_registry.py +0 -0
  191. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/routes/__init__.py +0 -0
  192. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/routes/envs.py +0 -0
  193. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/routes/meta.py +0 -0
  194. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/server/run_registry.py +0 -0
  195. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/flash/spec.py +0 -0
  196. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/infisical-entrypoint.sh +0 -0
  197. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/scripts/build_dev_dist.py +0 -0
  198. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/__init__.py +0 -0
  199. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/_helpers/__init__.py +0 -0
  200. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/_helpers/runner.py +0 -0
  201. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/_helpers/specs.py +0 -0
  202. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_eval.jsonl +0 -0
  203. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_train.jsonl +0 -0
  204. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/live/__init__.py +0 -0
  205. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/live/conftest.py +0 -0
  206. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/live/test_lambda_live.py +0 -0
  207. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/live/test_runpod_live.py +0 -0
  208. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_algorithms.py +0 -0
  209. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_allocator.py +0 -0
  210. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_b200_rollout_opt.py +0 -0
  211. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_backend_jobspec_contract.py +0 -0
  212. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_chalk_kernels.py +0 -0
  213. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_errors.py +0 -0
  214. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_estimate.py +0 -0
  215. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_help.py +0 -0
  216. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cli_managed.py +0 -0
  217. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_config_overrides.py +0 -0
  218. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_analytical.py +0 -0
  219. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_equation.py +0 -0
  220. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_estimate.py +0 -0
  221. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_hardware.py +0 -0
  222. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_models.py +0 -0
  223. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_cost_rewards.py +0 -0
  224. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_dev_channel.py +0 -0
  225. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_disk_gb.py +0 -0
  226. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_endpoint_name.py +0 -0
  227. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_delete.py +0 -0
  228. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_env_no_install.py +0 -0
  229. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_envs_coverage.py +0 -0
  230. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_export.py +0 -0
  231. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_flash_mvp.py +0 -0
  232. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_flashinfer_cache_dirs.py +0 -0
  233. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_grpo_mask_aware.py +0 -0
  234. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_grpo_sleep_gate.py +0 -0
  235. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_idle_endpoint_reaper.py +0 -0
  236. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_internal_client.py +0 -0
  237. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_kernel_cache.py +0 -0
  238. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_kernel_fingerprint.py +0 -0
  239. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_lambda_runner.py +0 -0
  240. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_logging.py +0 -0
  241. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_login_perms.py +0 -0
  242. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_managed_hf_repo.py +0 -0
  243. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_metrics_schema_agent_contract.py +0 -0
  244. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_mig_guard.py +0 -0
  245. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_multiturn_rollout.py +0 -0
  246. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_oom_escalate_gpu.py +0 -0
  247. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_open_model_policy.py +0 -0
  248. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_packing.py +0 -0
  249. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_poll_helpers.py +0 -0
  250. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_preflight.py +0 -0
  251. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_provider_routing.py +0 -0
  252. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_provider_teardown_robustness.py +0 -0
  253. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_providers_symmetry.py +0 -0
  254. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_realized_cost.py +0 -0
  255. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_resolve_params_b.py +0 -0
  256. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_resume_on_retry.py +0 -0
  257. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_runmgmt.py +0 -0
  258. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_runpod_api_delete.py +0 -0
  259. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_fingerprint.py +0 -0
  260. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_waterfall.py +0 -0
  261. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_runpod_slots.py +0 -0
  262. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_server_db.py +0 -0
  263. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_sft_gc_off.py +0 -0
  264. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_thinking_config.py +0 -0
  265. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_update_check.py +0 -0
  266. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_version.py +0 -0
  267. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  268. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_vl_weight_sync.py +0 -0
  269. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_wandb_naming.py +0 -0
  270. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_weight_cache.py +0 -0
  271. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_dryrun.py +0 -0
  272. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_hardexit.py +0 -0
  273. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_image.py +0 -0
  274. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_init_heartbeat.py +0 -0
  275. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_stack.py +0 -0
  276. {freesolo_flash_dev-0.2.33 → freesolo_flash_dev-0.2.35}/tests/test_worker_thinking.py +0 -0
@@ -0,0 +1,35 @@
1
+ name: Notify tests repo
2
+
3
+ # Flash's cross-repo integration seams (flash<->backend auth, allocator, CLI flow)
4
+ # live in the freesolo-co/tests repo, which carries flash as a pinned submodule.
5
+ # On every push to dev/main, dispatch to that repo so its CI bumps the flash
6
+ # submodule pin and runs the affected suites against the new sha. Mirrors the
7
+ # freesolo-side notify-tests-repo.yml; the tests repo listens for `flash-merged`.
8
+
9
+ on:
10
+ push:
11
+ branches: [dev, main]
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ notify:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Dispatch to freesolo-co/tests
22
+ env:
23
+ PAT: ${{ secrets.FREESOLO_GITHUB_PAT }}
24
+ SHA: ${{ github.sha }}
25
+ REF: ${{ github.ref_name }}
26
+ run: |
27
+ if [ -z "$PAT" ]; then
28
+ echo "No FREESOLO_GITHUB_PAT (likely a fork) — skipping dispatch."
29
+ exit 0
30
+ fi
31
+ curl -fsS -X POST \
32
+ -H "Authorization: Bearer ${PAT}" \
33
+ -H "Accept: application/vnd.github+json" \
34
+ https://api.github.com/repos/freesolo-co/tests/dispatches \
35
+ -d "{\"event_type\":\"flash-merged\",\"client_payload\":{\"sha\":\"${SHA}\",\"ref\":\"${REF}\"}}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.33
3
+ Version: 0.2.35
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
@@ -38,6 +38,7 @@ class ServingCapacity:
38
38
  serve_model_id: str = ""
39
39
  max_num_seqs: int = 0
40
40
  max_num_batched_tokens: int = 0
41
+ tensor_parallel_size: int = 0
41
42
  gpu_memory_utilization: float = 0.0
42
43
 
43
44
 
@@ -104,6 +105,7 @@ class ModelInfo:
104
105
  "serve_model_id",
105
106
  "max_num_seqs",
106
107
  "max_num_batched_tokens",
108
+ "tensor_parallel_size",
107
109
  "gpu_memory_utilization",
108
110
  ):
109
111
  if serving.get(key) in ("", 0, 0.0, None):
@@ -113,6 +115,15 @@ class ModelInfo:
113
115
 
114
116
  DEFAULT_MODEL = "Qwen/Qwen3.5-4B"
115
117
 
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",
124
+ "Qwen/Qwen3.6-35B-A3B": "Qwen/Qwen3.6-35B-A3B-FP8",
125
+ }
126
+
116
127
  MODELS: dict[str, ModelInfo] = {
117
128
  "openbmb/MiniCPM5-1B": ModelInfo(
118
129
  id="openbmb/MiniCPM5-1B",
@@ -123,7 +134,13 @@ MODELS: dict[str, ModelInfo] = {
123
134
  algos=("sft", "grpo"),
124
135
  min_vram_gb=12,
125
136
  recommended_gpu="RTX 4090",
126
- serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
137
+ serving=ServingCapacity(
138
+ gpu="L4",
139
+ serve_model_id=SERVING_FP8_MODEL_REPOS["openbmb/MiniCPM5-1B"],
140
+ max_loras=16,
141
+ max_lora_rank=128,
142
+ max_model_len=32768,
143
+ ),
127
144
  thinking="hybrid",
128
145
  notes="On-device class SLM (131k ctx); standard Llama architecture.",
129
146
  ),
@@ -136,7 +153,13 @@ MODELS: dict[str, ModelInfo] = {
136
153
  algos=("sft", "grpo"),
137
154
  min_vram_gb=12,
138
155
  recommended_gpu="RTX 4090",
139
- serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
156
+ serving=ServingCapacity(
157
+ gpu="L4",
158
+ serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-0.8B"],
159
+ max_loras=16,
160
+ max_lora_rank=128,
161
+ max_model_len=32768,
162
+ ),
140
163
  thinking="hybrid",
141
164
  notes="Smallest Qwen3.5; cheap smoke/dev runs with the modern arch.",
142
165
  ),
@@ -149,7 +172,13 @@ MODELS: dict[str, ModelInfo] = {
149
172
  algos=("sft", "grpo"),
150
173
  min_vram_gb=16,
151
174
  recommended_gpu="RTX 4090",
152
- serving=ServingCapacity(gpu="L4", max_loras=16, max_lora_rank=64, max_model_len=32768),
175
+ serving=ServingCapacity(
176
+ gpu="L4",
177
+ serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-2B"],
178
+ max_loras=16,
179
+ max_lora_rank=128,
180
+ max_model_len=32768,
181
+ ),
153
182
  thinking="hybrid",
154
183
  ),
155
184
  "Qwen/Qwen3.5-4B": ModelInfo(
@@ -162,10 +191,10 @@ MODELS: dict[str, ModelInfo] = {
162
191
  min_vram_gb=32,
163
192
  recommended_gpu="RTX 5090",
164
193
  serving=ServingCapacity(
165
- gpu="L4",
166
- serve_model_id="lovedheart/Qwen3.5-4B-FP8",
194
+ gpu="L40S",
195
+ serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-4B"],
167
196
  max_loras=64,
168
- max_lora_rank=32,
197
+ max_lora_rank=64,
169
198
  max_model_len=8192,
170
199
  max_num_seqs=8,
171
200
  gpu_memory_utilization=0.98,
@@ -187,10 +216,10 @@ MODELS: dict[str, ModelInfo] = {
187
216
  quant="bf16",
188
217
  recommended_gpu="A100 PCIe",
189
218
  serving=ServingCapacity(
190
- gpu="L4",
191
- serve_model_id="lovedheart/Qwen3.5-9B-FP8",
219
+ gpu="L40S",
220
+ serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-9B"],
192
221
  max_loras=44,
193
- max_lora_rank=32,
222
+ max_lora_rank=64,
194
223
  max_model_len=8192,
195
224
  max_num_seqs=8,
196
225
  gpu_memory_utilization=0.98,
@@ -225,13 +254,14 @@ MODELS: dict[str, ModelInfo] = {
225
254
  quant="bf16",
226
255
  recommended_gpu="H200",
227
256
  serving=ServingCapacity(
228
- gpu="A100-80GB",
229
- serve_model_id="Qwen/Qwen3.6-35B-A3B-FP8",
257
+ gpu="A100-80GB:2",
258
+ serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.6-35B-A3B"],
230
259
  max_loras=12,
231
- max_lora_rank=32,
260
+ max_lora_rank=64,
232
261
  max_model_len=8192,
233
262
  max_num_seqs=8,
234
263
  max_num_batched_tokens=4096,
264
+ tensor_parallel_size=2,
235
265
  gpu_memory_utilization=0.98,
236
266
  ),
237
267
  thinking="hybrid",
@@ -262,8 +292,8 @@ def get_model(model_id: str) -> ModelInfo:
262
292
  def serving_lora_rank_cap(model: str | ModelInfo | None) -> int | None:
263
293
  """Return the model's serving LoRA rank cap, or None when Flash has no local cap.
264
294
 
265
- Serving capacity is model-specific: small serving models currently allow rank 64, while larger
266
- serving paths can cap at rank 32. Unknown/open-policy models intentionally return None instead
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
267
297
  of inheriting a global fallback.
268
298
  """
269
299
  if isinstance(model, ModelInfo):
@@ -35,6 +35,7 @@ from flash.cli.commands import ( # noqa: F401
35
35
  cmd_env_setup,
36
36
  cmd_export,
37
37
  cmd_gpus,
38
+ cmd_log,
38
39
  cmd_login,
39
40
  cmd_models,
40
41
  cmd_runs,
@@ -83,7 +84,8 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
83
84
  "training",
84
85
  [
85
86
  ("train", "submit a managed run from a TOML config"),
86
- ("status", "show a run's status, logs, or live follow"),
87
+ ("status", "show a run's current status"),
88
+ ("log", "print or follow a run's logs"),
87
89
  ("runs", "list runs with their state and cost"),
88
90
  ("checkpoints", "list a run's deployable RL checkpoints"),
89
91
  ("cancel", "cancel a running job"),
@@ -309,21 +311,28 @@ def _build_parser() -> argparse.ArgumentParser:
309
311
  )
310
312
  train.set_defaults(func=cmd_train)
311
313
 
312
- status = sub.add_parser("status", help="show a run's status, logs, or follow logs")
314
+ status = sub.add_parser("status", help="show a run's current status")
313
315
  status.add_argument("run_id")
314
316
  status.add_argument(
315
- "--logs",
317
+ "-f",
318
+ "--follow",
316
319
  action="store_true",
317
- help="print current logs before status the orchestrator log plus the train-subprocess "
318
- "stdout + traceback (console_/error_<phase>.txt) fetched from the run's HF artifact repo",
320
+ help="poll status until the run ends without replaying logs",
319
321
  )
320
- status.add_argument(
322
+ status.set_defaults(func=cmd_status)
323
+
324
+ log = sub.add_parser(
325
+ "log",
326
+ help="print a run's full logs, including worker console/error artifacts",
327
+ )
328
+ log.add_argument("run_id")
329
+ log.add_argument(
321
330
  "-f",
322
331
  "--follow",
323
332
  action="store_true",
324
- help="stream logs until the run ends, then print final status",
333
+ help="stream logs until the run reaches a terminal state",
325
334
  )
326
- status.set_defaults(func=cmd_status)
335
+ log.set_defaults(func=cmd_log)
327
336
 
328
337
  runs = sub.add_parser("runs", help="list runs and their state/cost")
329
338
  runs.set_defaults(func=cmd_runs)
@@ -339,14 +348,17 @@ def _build_parser() -> argparse.ArgumentParser:
339
348
  checkpoints.set_defaults(func=cmd_checkpoints)
340
349
 
341
350
  deploy = sub.add_parser("deploy", help="deploy a run's adapter to a serving endpoint")
342
- deploy.add_argument("run_id")
351
+ deploy.add_argument(
352
+ "run_id",
353
+ metavar="RUN_ID[/step-N]",
354
+ help="run id for the final adapter, or RUN_ID/step-N for a saved checkpoint",
355
+ )
343
356
  deploy.add_argument("--dry-run", action="store_true")
344
357
  deploy.add_argument(
345
- "--step",
346
- type=int,
347
- default=None,
348
- help="deploy a specific intermediate checkpoint (see `flash checkpoints <run_id>`) "
349
- "instead of the run's final adapter; works even for a run cancelled mid-RL",
358
+ "--no-verify",
359
+ action="store_true",
360
+ help="skip the server-side post-deploy smoke generation (registration alone does NOT "
361
+ "guarantee the adapter serves; only ready deployments should be scored by evals)",
350
362
  )
351
363
  deploy.set_defaults(func=cmd_deploy)
352
364
 
@@ -359,7 +371,8 @@ def _build_parser() -> argparse.ArgumentParser:
359
371
  "--adapter-id",
360
372
  dest="adapter_id",
361
373
  required=True,
362
- help="the Freesolo adapter id (the run id) to export",
374
+ metavar="RUN_ID[/step-N]",
375
+ help="run id for the final adapter, or RUN_ID/step-N for a saved checkpoint",
363
376
  )
364
377
  export.add_argument(
365
378
  "--repository",
@@ -371,13 +384,6 @@ def _build_parser() -> argparse.ArgumentParser:
371
384
  help="HuggingFace token with write access to --repository "
372
385
  "(default: HF_TOKEN from your shell or a local .env / .env.local)",
373
386
  )
374
- export.add_argument(
375
- "--step",
376
- type=int,
377
- default=None,
378
- help="export a specific intermediate checkpoint (see `flash checkpoints <adapter-id>`) "
379
- "instead of the run's final adapter; works even for a run cancelled mid-RL",
380
- )
381
387
  export.add_argument(
382
388
  "--public",
383
389
  action="store_true",
@@ -391,6 +397,12 @@ def _build_parser() -> argparse.ArgumentParser:
391
397
  chat = sub.add_parser("chat", help="chat with a deployed adapter")
392
398
  chat.add_argument("run_id")
393
399
  chat.add_argument("-m", "--message", required=True)
400
+ chat.add_argument(
401
+ "--system",
402
+ default=None,
403
+ help="optional system prompt sent ahead of the user message "
404
+ "(training-prompt parity for evals)",
405
+ )
394
406
  chat.add_argument("--max-tokens", type=int, default=512)
395
407
  chat.add_argument("--temperature", type=float, default=0.0)
396
408
  chat.set_defaults(func=cmd_chat)
@@ -401,15 +401,18 @@ def cmd_train(args) -> int:
401
401
  else:
402
402
  print(
403
403
  f"run {run_id} submitted; following logs "
404
- f"(Ctrl-C detaches, `flash status {run_id} --follow` resumes)",
404
+ f"(Ctrl-C detaches, `flash log {run_id} --follow` resumes)",
405
405
  file=sys.stderr,
406
406
  )
407
407
  return _follow_run(client, run_id)
408
408
 
409
409
 
410
- def _poll_logs(client: ApiClient, run_id: str, interval: float) -> str:
411
- """Stream offset-paged logs until the run reaches a terminal state; return that state."""
410
+ def _poll_logs(client: ApiClient, run_id: str, interval: float) -> tuple[str, bool]:
411
+ """Stream offset-paged logs until the run reaches a terminal state.
412
+
413
+ Returns (terminal state, whether any log bytes were printed)."""
412
414
  offset = 0
415
+ printed_any = False
413
416
  spinner = _LogFollowSpinner(run_id)
414
417
  try:
415
418
  while True:
@@ -417,53 +420,75 @@ def _poll_logs(client: ApiClient, run_id: str, interval: float) -> str:
417
420
  if page["logs"]:
418
421
  spinner.clear()
419
422
  print(page["logs"], end="", flush=True)
423
+ printed_any = True
420
424
  offset = page["offset"]
421
425
  if page["state"] in _CLI_DONE_STATES:
422
426
  spinner.clear()
423
- return page["state"]
427
+ return page["state"], printed_any
424
428
  _sleep_with_spinner(interval, spinner, page["state"])
425
429
  finally:
426
430
  spinner.clear()
427
431
 
428
432
 
433
+ def _render_status(status: dict) -> str:
434
+ """One rendering of a run status: themed panel on a TTY, indented JSON on the machine path."""
435
+ return render.run_status(status) if render.styled() else json.dumps(status, indent=2)
436
+
437
+
429
438
  def _follow_run(client: ApiClient, run_id: str) -> int:
430
439
  """Poll logs until the run reaches a terminal state, then print the final status."""
431
- state = _poll_logs(client, run_id, interval=2.0)
432
- status = client.get_run(run_id)
433
- if render.styled():
434
- print(render.run_status(status))
435
- else:
436
- print(json.dumps(status, indent=2))
440
+ state, _ = _poll_logs(client, run_id, interval=2.0)
441
+ print(_render_status(client.get_run(run_id)))
437
442
  return 0 if state in _OK_STATES else 1
438
443
 
439
444
 
445
+ def _follow_status(client: ApiClient, run_id: str, interval: float = 2.0) -> int:
446
+ """Poll run status until terminal, without replaying worker logs."""
447
+ last_rendered: str | None = None
448
+ while True:
449
+ status = client.get_run(run_id)
450
+ rendered = _render_status(status)
451
+ if rendered != last_rendered:
452
+ print(rendered)
453
+ last_rendered = rendered
454
+ state = str(status.get("state") or "")
455
+ if state in _CLI_DONE_STATES:
456
+ return 0 if state in _OK_STATES else 1
457
+ time.sleep(interval)
458
+
459
+
460
+ def _print_worker_output(client: ApiClient, run_id: str, *, printed_any: bool = False) -> bool:
461
+ for name, text in (client.get_worker_output(run_id) or {}).items():
462
+ if not text:
463
+ continue
464
+ sep = "\n" if printed_any else ""
465
+ if render.styled():
466
+ print(f"{sep}{render.log_section(name)}")
467
+ else:
468
+ print(f"{sep}----- {name} -----")
469
+ print(text, end="" if text.endswith("\n") else "\n")
470
+ printed_any = True
471
+ return printed_any
472
+
473
+
474
+ def cmd_log(args) -> int:
475
+ client = client_from_config()
476
+ if getattr(args, "follow", False):
477
+ state, printed_any = _poll_logs(client, args.run_id, interval=2.0)
478
+ _print_worker_output(client, args.run_id, printed_any=printed_any)
479
+ return 0 if state in _OK_STATES else 1
480
+ text = str(client.get_logs(args.run_id, offset=0).get("logs") or "")
481
+ if text:
482
+ print(text, end="" if text.endswith("\n") else "\n")
483
+ _print_worker_output(client, args.run_id, printed_any=bool(text))
484
+ return 0
485
+
486
+
440
487
  def cmd_status(args) -> int:
441
488
  client = client_from_config()
442
489
  if getattr(args, "follow", False):
443
- return _follow_run(client, args.run_id)
444
- if getattr(args, "logs", False):
445
- logs = client.get_logs(args.run_id).get("logs", "")
446
- printed_any = False
447
- if logs:
448
- print(logs, end="")
449
- if not logs.endswith("\n"):
450
- print()
451
- printed_any = True
452
- for name, text in (client.get_worker_output(args.run_id) or {}).items():
453
- if not text:
454
- continue
455
- sep = "\n" if printed_any else ""
456
- if render.styled():
457
- print(f"{sep}{render.log_section(name)}")
458
- else:
459
- print(f"{sep}----- {name} -----")
460
- print(text, end="" if text.endswith("\n") else "\n")
461
- printed_any = True
462
- status = client.get_run(args.run_id)
463
- if render.styled():
464
- print(render.run_status(status))
465
- else:
466
- print(json.dumps(status, indent=2))
490
+ return _follow_status(client, args.run_id)
491
+ print(_render_status(client.get_run(args.run_id)))
467
492
  return 0
468
493
 
469
494
 
@@ -516,20 +541,37 @@ def cmd_checkpoints(args) -> int:
516
541
  if render.styled():
517
542
  print(render.checkpoints_table(args.run_id, checkpoints))
518
543
  return 0
544
+ from flash.schema import format_checkpoint_ref
545
+
519
546
  for c in checkpoints:
520
- print(f"step {c['step']:>6} {c['repo_id']}:{c['subfolder']}")
547
+ # single-space, unpadded columns so a plain `grep "step N"` / awk split works; the ref is
548
+ # the canonical short form, paste-able into train.init_from_adapter.
549
+ print(f"step {c['step']} {format_checkpoint_ref(args.run_id, c['step'])}")
521
550
  print(
522
- f"\ndeploy one with `flash deploy {args.run_id} --step <STEP>`.",
551
+ f"\ndeploy one with `flash deploy {args.run_id}/step-<STEP>`.",
523
552
  file=sys.stderr,
524
553
  )
525
554
  return 0
526
555
 
527
556
 
528
557
  def cmd_deploy(args) -> int:
529
- dep = client_from_config().deploy(
558
+ from flash.schema import parse_checkpoint_ref
559
+
560
+ # `flash deploy <run_id>/step-N` is the same checkpoint ref `flash checkpoints` prints.
561
+ parsed = parse_checkpoint_ref(args.run_id)
562
+ if parsed is None:
563
+ print(
564
+ f"invalid run/checkpoint reference {args.run_id!r} "
565
+ "(expected <run_id> or <run_id>/step-N)",
566
+ file=sys.stderr,
567
+ )
568
+ return 1
569
+ base_run_id, _step = parsed
570
+ client = client_from_config()
571
+ dep = client.deploy(
530
572
  args.run_id,
531
573
  dry_run=args.dry_run,
532
- step=getattr(args, "step", None),
574
+ verify=not getattr(args, "no_verify", False),
533
575
  )
534
576
  if render.styled():
535
577
  print(render.deployed(dep))
@@ -537,11 +579,35 @@ def cmd_deploy(args) -> int:
537
579
  print(json.dumps(dep, indent=2))
538
580
  # a dry run creates no deployment, so the billing / undeploy hint would be misleading.
539
581
  if dep.get("state") != "dry_run":
582
+ endpoint = str(dep.get("endpoint_name") or "").rstrip("/")
583
+ openai_base = dep.get("url") or (f"{endpoint}/v1" if endpoint else "")
540
584
  note = (
541
- f"serving is billed per token only; use `flash undeploy {args.run_id}` "
585
+ f"serving is billed per token only; use `flash undeploy {base_run_id}` "
542
586
  "to deregister the adapter."
543
587
  )
544
588
  print(render.arrow(note) if render.styled() else f"note: {note}", file=sys.stderr)
589
+ if openai_base:
590
+ url_note = (
591
+ f"OpenAI-compatible base URL: {openai_base} — point clients at this /v1 base, "
592
+ "not the bare endpoint (which 404s on /chat/completions)."
593
+ )
594
+ print(render.arrow(url_note) if render.styled() else f"note: {url_note}", file=sys.stderr)
595
+ if dep.get("state") != "dry_run":
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
+ )
601
+ print(
602
+ render.arrow(status_note) if render.styled() else f"note: {status_note}",
603
+ file=sys.stderr,
604
+ )
605
+ if getattr(args, "no_verify", False):
606
+ skip_note = "server-side smoke verification was skipped for this deployment."
607
+ print(
608
+ render.arrow(skip_note) if render.styled() else f"note: {skip_note}",
609
+ file=sys.stderr,
610
+ )
545
611
  return 0
546
612
 
547
613
 
@@ -555,9 +621,8 @@ def cmd_export(args) -> int:
555
621
  "(export it in your shell or put it in a local .env / .env.local)"
556
622
  )
557
623
  client = client_from_config()
558
- where = f" (step {args.step})" if args.step is not None else ""
559
624
  progress = (
560
- f"exporting adapter {args.adapter_id}{where} to {args.repository} — "
625
+ f"exporting adapter {args.adapter_id} to {args.repository} — "
561
626
  "downloading then re-uploading; this can take a minute..."
562
627
  )
563
628
  print(render.note(progress) if render.styled() else progress, file=sys.stderr)
@@ -565,7 +630,6 @@ def cmd_export(args) -> int:
565
630
  args.adapter_id,
566
631
  repository=args.repository,
567
632
  hf_token=hf_token,
568
- step=args.step,
569
633
  private=not args.public,
570
634
  )
571
635
  if render.styled():
@@ -602,16 +666,23 @@ def cmd_deployments(args) -> int:
602
666
  if render.styled():
603
667
  print(render.deployments_table(rows))
604
668
  return 0
605
- print(f"{'RUN_ID':<32} {'GPU':<9} ENDPOINT")
669
+ print(f"{'RUN_ID':<32} {'STATE':<10} {'GPU':<9} {'ENDPOINT':<32} DETAIL")
606
670
  for r in rows:
607
671
  d = r.get("deployment") or {}
608
- print(f"{r['run_id']:<32} {d.get('gpu', '?'):<9} {d.get('endpoint_name', '')}")
672
+ detail = str(d.get("error") or d.get("detail") or "")
673
+ print(
674
+ f"{r['run_id']:<32} {d.get('state', '?'):<10} "
675
+ f"{d.get('gpu', '?'):<9} {d.get('endpoint_name', ''):<32} {detail}"
676
+ )
609
677
  return 0
610
678
 
611
679
 
612
680
  def cmd_chat(args) -> int:
613
681
  client = client_from_config()
614
682
  messages = [{"role": "user", "content": args.message}]
683
+ system = getattr(args, "system", None)
684
+ if system:
685
+ messages.insert(0, {"role": "system", "content": system})
615
686
  if render.styled():
616
687
  print(render.chat_label())
617
688
  stream = getattr(client, "chat_stream", None)
@@ -188,10 +188,23 @@ _ENV_PUSH_SIDECAR_SUFFIXES = frozenset(
188
188
 
189
189
 
190
190
  def _normalize_env_name(raw: str) -> str | None:
191
- import re
191
+ """Normalize only the NAME segment; a namespace prefix is passed through verbatim.
192
192
 
193
- name = re.sub(r"[^a-z0-9]+", "-", raw.lower()).strip("-")
194
- return name or None
193
+ The server is the sole authority on namespace grammar and ownership — rewriting the
194
+ namespace client-side would silently target a different (possibly forbidden) slug.
195
+ """
196
+ from flash.schema import normalize_env_name_segment
197
+
198
+ text = str(raw or "").strip()
199
+ if "/" not in text:
200
+ return normalize_env_name_segment(text)
201
+ parts = [part.strip() for part in text.split("/")]
202
+ if len(parts) != 2 or not all(parts):
203
+ return None
204
+ name = normalize_env_name_segment(parts[1])
205
+ if name is None:
206
+ return None
207
+ return f"{parts[0]}/{name}"
195
208
 
196
209
 
197
210
  def _with_syspath_bootstrap(env_source: str) -> str:
@@ -381,7 +381,7 @@ def version(value: str) -> str:
381
381
  def submitted(run_id: str) -> str:
382
382
  """The `flash train` hand-off note (printed to stderr before logs start streaming)."""
383
383
  head = ok(f"run {_paint(run_id, _ACCENT2)} submitted")
384
- hint = _dim(f"following logs — Ctrl-C detaches; resume with `flash status {run_id} --follow`")
384
+ hint = _dim(f"following logs — Ctrl-C detaches; resume with `flash log {run_id} --follow`")
385
385
  return _safe(f"{head}\n{hint}")
386
386
 
387
387
 
@@ -439,28 +439,37 @@ def deployments_table(rows: list[dict]) -> str:
439
439
  body = []
440
440
  for r in rows:
441
441
  d = r.get("deployment") or {}
442
+ state = str(d.get("state") or "?")
443
+ color = _GREEN if state in {"ready", "deployed"} else _RED if state == "failed" else _AMBER
444
+ detail = str(d.get("error") or d.get("detail") or "")
445
+ if len(detail) > 64:
446
+ detail = detail[:61] + "..."
442
447
  body.append(
443
448
  [
444
449
  (r["run_id"], _ACCENT2),
450
+ (state, color),
445
451
  (d.get("gpu", "?"), _GRAY),
446
452
  (d.get("endpoint_name", ""), _GREEN),
453
+ (detail, _GRAY),
447
454
  ]
448
455
  )
449
- table = _table(["RUN ID", "GPU", "ENDPOINT"], body)
456
+ table = _table(["RUN ID", "STATE", "GPU", "ENDPOINT", "DETAIL"], body)
450
457
  return _safe(f"{header('deployments', f'{len(rows)} active')}\n{table}")
451
458
 
452
459
 
453
460
  def checkpoints_table(run_id: str, rows: list[dict]) -> str:
454
- """Deployable per-step RL checkpoints: the step number + the stable repo path it lives at."""
461
+ """Deployable per-step RL checkpoints: step number + the canonical `<run_id>/step-N` ref."""
462
+ from flash.schema import format_checkpoint_ref
463
+
455
464
  body = [
456
465
  [
457
466
  (str(c.get("step", "")), _TEAL),
458
- (f"{c.get('repo_id', '')}:{c.get('subfolder', '')}", _ACCENT2),
467
+ (format_checkpoint_ref(run_id, c.get("step", 0)), _ACCENT2),
459
468
  ]
460
469
  for c in sorted(rows, key=lambda c: c.get("step", 0))
461
470
  ]
462
471
  table = _table(["STEP", "CHECKPOINT"], body, aligns=["r", "l"])
463
- foot = arrow(f"deploy one with: flash deploy {run_id} --step <STEP>")
472
+ foot = arrow(f"deploy one with: flash deploy {run_id}/step-<STEP>")
464
473
  return _safe(f"{header('checkpoints', f'{len(rows)} deployable')}\n{table}\n\n{foot}")
465
474
 
466
475
 
@@ -541,11 +550,14 @@ def cancelled(payload: dict) -> str:
541
550
 
542
551
  def deployed(dep: dict) -> str:
543
552
  """`flash deploy`: the endpoint, gpu, and serving url as an aligned card (not a JSON dump)."""
553
+ endpoint = str(dep.get("endpoint_name") or "")
554
+ # OpenAI clients need the /v1 base; older servers omit `url`, so derive it from the endpoint.
555
+ url = dep.get("url") or (f"{endpoint.rstrip('/')}/v1" if endpoint else None)
544
556
  pairs = [
545
557
  ("run", _paint(dep.get("run_id", ""), _ACCENT2)),
546
- ("endpoint", _paint(dep["endpoint_name"], _GREEN) if dep.get("endpoint_name") else None),
558
+ ("endpoint", _paint(endpoint, _GREEN) if endpoint else None),
547
559
  ("gpu", dep.get("gpu")),
548
- ("url", _paint(dep["url"], _ACCENT2) if dep.get("url") else None),
560
+ ("url", _paint(url, _ACCENT2) if url else None),
549
561
  ]
550
562
  state = dep.get("state", "deployed")
551
563
  if state == "dry_run":
@@ -571,6 +583,10 @@ def undeployed(result: dict) -> str:
571
583
  line = ok(f"torn down {rid}")
572
584
  if deleted:
573
585
  line += "\n" + _dim(f" deregistered {', '.join(deleted)}")
586
+ else:
587
+ line += "\n" + _dim(
588
+ " serving had no registered adapter to deregister (already gone or never registered)"
589
+ )
574
590
  return _safe(line)
575
591
 
576
592
 
@@ -659,7 +675,7 @@ def chat_label() -> str:
659
675
 
660
676
 
661
677
  def log_section(name: str) -> str:
662
- """A themed divider above a passthrough worker-log section in `flash status --logs` — the same
678
+ """A themed divider above a passthrough worker-log section in `flash log` — the same
663
679
  idiom as chat_label sitting above a raw chat reply (the log body stays raw). The machine path
664
680
  keeps the plain ``----- name -----`` divider that scripts and tests match on."""
665
681
  rule = _paint(_glyph("─", "-") * 3, _FAINT)