freesolo-flash-dev 0.2.34__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 (269) hide show
  1. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/__init__.py +17 -14
  3. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/commands.py +78 -44
  4. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/envpush.py +9 -11
  5. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/render.py +22 -6
  6. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/training_doc.py +8 -7
  7. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/http.py +37 -26
  8. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/vram.py +52 -3
  9. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/adapter.py +14 -17
  10. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/hf.py +123 -37
  11. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/rl.py +3 -3
  12. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/sft.py +1 -1
  13. freesolo_flash_dev-0.2.35/flash/envs/adapter.py +447 -0
  14. freesolo_flash_dev-0.2.34/flash/envs/adapter.py → freesolo_flash_dev-0.2.35/flash/envs/loader.py +98 -399
  15. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/pull.py +1 -1
  16. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/endpoints.py +64 -29
  17. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/__init__.py +107 -12
  18. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/checkpoints.py +44 -14
  19. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/deploy.py +63 -11
  20. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/schema/__init__.py +55 -5
  21. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/deploy.py +173 -13
  22. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/app.py +18 -1
  23. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/envs.py +3 -4
  24. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/runs.py +1 -1
  25. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/serving.py +225 -43
  26. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/pyproject.toml +2 -2
  27. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/conftest.py +29 -0
  28. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_agent_flash_cli_contract.py +2 -4
  29. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cancel_remote.py +123 -0
  30. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_checkpoints.py +115 -1
  31. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_commands.py +73 -14
  32. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_render_theme.py +3 -5
  33. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_client.py +31 -42
  34. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_client_server_integration.py +6 -0
  35. freesolo_flash_dev-0.2.35/tests/test_env_cache_evict.py +70 -0
  36. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_pull.py +1 -1
  37. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_rate_limit_resolve.py +8 -8
  38. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_github_urlopen_retry.py +1 -1
  39. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_gpus.py +18 -0
  40. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_params.py +54 -109
  41. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_jobs.py +344 -0
  42. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kv_util.py +8 -0
  43. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_orchestrator_flash.py +3 -0
  44. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_serve.py +45 -4
  45. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_api.py +139 -13
  46. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_serving_contract.py +191 -3
  47. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_verifiers.py +137 -14
  48. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_warmstart_cross_repo.py +21 -4
  49. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/uv.lock +1 -1
  50. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.dockerignore +0 -0
  51. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.env.example +0 -0
  52. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/auto-rebake.yml +0 -0
  53. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/bake-kernel-cache.yml +0 -0
  54. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/ci.yml +0 -0
  55. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/main-source-guard.yml +0 -0
  56. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/notify-tests-repo.yml +0 -0
  57. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-dev.yml +0 -0
  58. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-image.yml +0 -0
  59. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish.yml +0 -0
  60. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/version-parity.yml +0 -0
  61. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/worker-image.yml +0 -0
  62. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.gitignore +0 -0
  63. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/Dockerfile +0 -0
  64. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/Dockerfile.worker +0 -0
  65. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/LICENSE +0 -0
  66. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/README.md +0 -0
  67. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.gitignore +0 -0
  68. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.keep +0 -0
  69. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache +0 -0
  70. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache.relayer +0 -0
  71. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/bake_kernel_cache.py +0 -0
  72. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/bake_pod_entry.py +0 -0
  73. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/kernel_fingerprint.py +0 -0
  74. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/make_rp_handler.py +0 -0
  75. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/README.md +0 -0
  76. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/generate.py +0 -0
  77. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/index.html +0 -0
  78. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/preview.png +0 -0
  79. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/themed-errors.png +0 -0
  80. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/kernel-cache.md +0 -0
  81. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/__init__.py +0 -0
  82. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_channel.py +0 -0
  83. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_fileio.py +0 -0
  84. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_logging.py +0 -0
  85. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_update_check.py +0 -0
  86. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/catalog.py +0 -0
  87. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/__main__.py +0 -0
  88. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/__init__.py +0 -0
  89. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/config.py +0 -0
  90. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/runtime_secrets.py +0 -0
  91. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/specs.py +0 -0
  92. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/__init__.py +0 -0
  93. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/analytical.py +0 -0
  94. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/facts.py +0 -0
  95. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/spec.py +0 -0
  96. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/types.py +0 -0
  97. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/__init__.py +0 -0
  98. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/accounting.py +0 -0
  99. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/chalk_kernels.py +0 -0
  100. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/multiturn_rollout.py +0 -0
  101. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/recipe.py +0 -0
  102. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__init__.py +0 -0
  103. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__main__.py +0 -0
  104. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/_pkg.py +0 -0
  105. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/decoding.py +0 -0
  106. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/finalize.py +0 -0
  107. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/gpu_setup.py +0 -0
  108. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/grpo.py +0 -0
  109. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/heartbeat.py +0 -0
  110. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/kernel_warmup.py +0 -0
  111. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/lora.py +0 -0
  112. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/packing.py +0 -0
  113. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/__init__.py +0 -0
  114. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/attn.py +0 -0
  115. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/diagnostics.py +0 -0
  116. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/lifecycle.py +0 -0
  117. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/liger.py +0 -0
  118. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/loraplus.py +0 -0
  119. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/memory.py +0 -0
  120. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/wandb_log.py +0 -0
  121. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/__init__.py +0 -0
  122. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/base.py +0 -0
  123. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/registry.py +0 -0
  124. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/__init__.py +0 -0
  125. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_auth.py +0 -0
  126. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_http.py +0 -0
  127. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_instance.py +0 -0
  128. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_instance_bootstrap.py +0 -0
  129. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_poll.py +0 -0
  130. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/allocator.py +0 -0
  131. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/base.py +0 -0
  132. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/__init__.py +0 -0
  133. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/api.py +0 -0
  134. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/auth.py +0 -0
  135. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/gpus.py +0 -0
  136. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  137. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  138. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/preflight.py +0 -0
  139. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/pricing.py +0 -0
  140. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/train.py +0 -0
  141. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/preflight.py +0 -0
  142. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/realized.py +0 -0
  143. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/__init__.py +0 -0
  144. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/api.py +0 -0
  145. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/auth.py +0 -0
  146. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/cost.py +0 -0
  147. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/gpus.py +0 -0
  148. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/jobs.py +0 -0
  149. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/keys.py +0 -0
  150. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preflight.py +0 -0
  151. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preload.py +0 -0
  152. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/pricing.py +0 -0
  153. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/slots.py +0 -0
  154. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/__init__.py +0 -0
  155. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/deps.py +0 -0
  156. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/py.typed +0 -0
  157. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/lifecycle.py +0 -0
  158. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/schema/fields.py +0 -0
  159. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/__init__.py +0 -0
  160. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/export.py +0 -0
  161. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/pricing.py +0 -0
  162. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/__init__.py +0 -0
  163. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/__main__.py +0 -0
  164. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_deps.py +0 -0
  165. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_internal_client.py +0 -0
  166. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_locks.py +0 -0
  167. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_runtime.py +0 -0
  168. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/auth.py +0 -0
  169. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/billing.py +0 -0
  170. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/billing_retry.py +0 -0
  171. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/checkpoints.py +0 -0
  172. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/db.py +0 -0
  173. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/environment_registry.py +0 -0
  174. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/reconcile.py +0 -0
  175. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/__init__.py +0 -0
  176. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/envs.py +0 -0
  177. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/meta.py +0 -0
  178. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/run_registry.py +0 -0
  179. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/spec.py +0 -0
  180. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/infisical-entrypoint.sh +0 -0
  181. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/scripts/build_dev_dist.py +0 -0
  182. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/__init__.py +0 -0
  183. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/__init__.py +0 -0
  184. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/runner.py +0 -0
  185. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/specs.py +0 -0
  186. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_eval.jsonl +0 -0
  187. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_train.jsonl +0 -0
  188. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/__init__.py +0 -0
  189. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/conftest.py +0 -0
  190. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/test_lambda_live.py +0 -0
  191. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/test_runpod_live.py +0 -0
  192. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_algorithms.py +0 -0
  193. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_allocator.py +0 -0
  194. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_b200_rollout_opt.py +0 -0
  195. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_backend_jobspec_contract.py +0 -0
  196. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_billing_retry.py +0 -0
  197. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_catalog_consistency.py +0 -0
  198. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_chalk_kernels.py +0 -0
  199. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_charge_pricing.py +0 -0
  200. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_errors.py +0 -0
  201. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_estimate.py +0 -0
  202. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_help.py +0 -0
  203. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_managed.py +0 -0
  204. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_config_overrides.py +0 -0
  205. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_analytical.py +0 -0
  206. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_equation.py +0 -0
  207. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_estimate.py +0 -0
  208. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_hardware.py +0 -0
  209. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_models.py +0 -0
  210. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_rewards.py +0 -0
  211. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_dev_channel.py +0 -0
  212. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_disk_gb.py +0 -0
  213. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_endpoint_name.py +0 -0
  214. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_delete.py +0 -0
  215. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_no_install.py +0 -0
  216. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_publish.py +0 -0
  217. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_push.py +0 -0
  218. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_envs_coverage.py +0 -0
  219. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_export.py +0 -0
  220. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flash_mvp.py +0 -0
  221. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flash_worker.py +0 -0
  222. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flashinfer_cache_dirs.py +0 -0
  223. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_mask_aware.py +0 -0
  224. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_sleep_gate.py +0 -0
  225. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_idle_endpoint_reaper.py +0 -0
  226. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_internal_client.py +0 -0
  227. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kernel_cache.py +0 -0
  228. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kernel_fingerprint.py +0 -0
  229. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_lambda_runner.py +0 -0
  230. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_logging.py +0 -0
  231. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_login_perms.py +0 -0
  232. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_managed_hf_repo.py +0 -0
  233. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_metrics_schema_agent_contract.py +0 -0
  234. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_mig_guard.py +0 -0
  235. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_multiturn_rollout.py +0 -0
  236. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_oom_escalate_gpu.py +0 -0
  237. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_open_model_policy.py +0 -0
  238. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_packing.py +0 -0
  239. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_poll_helpers.py +0 -0
  240. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_preflight.py +0 -0
  241. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_provider_routing.py +0 -0
  242. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_provider_teardown_robustness.py +0 -0
  243. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_providers_symmetry.py +0 -0
  244. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_realized_cost.py +0 -0
  245. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_resolve_params_b.py +0 -0
  246. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_resume_on_retry.py +0 -0
  247. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runmgmt.py +0 -0
  248. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_api_delete.py +0 -0
  249. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_fingerprint.py +0 -0
  250. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_waterfall.py +0 -0
  251. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_slots.py +0 -0
  252. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_billing.py +0 -0
  253. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_db.py +0 -0
  254. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_sft_gc_off.py +0 -0
  255. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_spec_and_validation.py +0 -0
  256. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_thinking_config.py +0 -0
  257. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_update_check.py +0 -0
  258. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_version.py +0 -0
  259. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  260. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_recombine.py +0 -0
  261. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_weight_sync.py +0 -0
  262. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_wandb_naming.py +0 -0
  263. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_weight_cache.py +0 -0
  264. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_dryrun.py +0 -0
  265. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_hardexit.py +0 -0
  266. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_image.py +0 -0
  267. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_init_heartbeat.py +0 -0
  268. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_stack.py +0 -0
  269. {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_thinking.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.34
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
@@ -348,14 +348,17 @@ def _build_parser() -> argparse.ArgumentParser:
348
348
  checkpoints.set_defaults(func=cmd_checkpoints)
349
349
 
350
350
  deploy = sub.add_parser("deploy", help="deploy a run's adapter to a serving endpoint")
351
- 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
+ )
352
356
  deploy.add_argument("--dry-run", action="store_true")
353
357
  deploy.add_argument(
354
- "--step",
355
- type=int,
356
- default=None,
357
- help="deploy a specific intermediate checkpoint (see `flash checkpoints <run_id>`) "
358
- "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)",
359
362
  )
360
363
  deploy.set_defaults(func=cmd_deploy)
361
364
 
@@ -368,7 +371,8 @@ def _build_parser() -> argparse.ArgumentParser:
368
371
  "--adapter-id",
369
372
  dest="adapter_id",
370
373
  required=True,
371
- 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",
372
376
  )
373
377
  export.add_argument(
374
378
  "--repository",
@@ -380,13 +384,6 @@ def _build_parser() -> argparse.ArgumentParser:
380
384
  help="HuggingFace token with write access to --repository "
381
385
  "(default: HF_TOKEN from your shell or a local .env / .env.local)",
382
386
  )
383
- export.add_argument(
384
- "--step",
385
- type=int,
386
- default=None,
387
- help="export a specific intermediate checkpoint (see `flash checkpoints <adapter-id>`) "
388
- "instead of the run's final adapter; works even for a run cancelled mid-RL",
389
- )
390
387
  export.add_argument(
391
388
  "--public",
392
389
  action="store_true",
@@ -400,6 +397,12 @@ def _build_parser() -> argparse.ArgumentParser:
400
397
  chat = sub.add_parser("chat", help="chat with a deployed adapter")
401
398
  chat.add_argument("run_id")
402
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
+ )
403
406
  chat.add_argument("--max-tokens", type=int, default=512)
404
407
  chat.add_argument("--temperature", type=float, default=0.0)
405
408
  chat.set_defaults(func=cmd_chat)
@@ -407,9 +407,12 @@ def cmd_train(args) -> int:
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,23 +420,25 @@ 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
 
@@ -442,7 +447,7 @@ def _follow_status(client: ApiClient, run_id: str, interval: float = 2.0) -> int
442
447
  last_rendered: str | None = None
443
448
  while True:
444
449
  status = client.get_run(run_id)
445
- rendered = render.run_status(status) if render.styled() else json.dumps(status, indent=2)
450
+ rendered = _render_status(status)
446
451
  if rendered != last_rendered:
447
452
  print(rendered)
448
453
  last_rendered = rendered
@@ -452,21 +457,6 @@ def _follow_status(client: ApiClient, run_id: str, interval: float = 2.0) -> int
452
457
  time.sleep(interval)
453
458
 
454
459
 
455
- def _print_log_text(text: str) -> bool:
456
- if not text:
457
- return False
458
- print(text, end="")
459
- if not text.endswith("\n"):
460
- print()
461
- return True
462
-
463
-
464
- def _print_log_snapshot(client: ApiClient, run_id: str) -> bool:
465
- """Print a finite snapshot from the log endpoint without following new bytes."""
466
- page = client.get_logs(run_id, offset=0)
467
- return _print_log_text(str(page.get("logs") or ""))
468
-
469
-
470
460
  def _print_worker_output(client: ApiClient, run_id: str, *, printed_any: bool = False) -> bool:
471
461
  for name, text in (client.get_worker_output(run_id) or {}).items():
472
462
  if not text:
@@ -484,11 +474,13 @@ def _print_worker_output(client: ApiClient, run_id: str, *, printed_any: bool =
484
474
  def cmd_log(args) -> int:
485
475
  client = client_from_config()
486
476
  if getattr(args, "follow", False):
487
- state = _poll_logs(client, args.run_id, interval=2.0)
488
- _print_worker_output(client, args.run_id, printed_any=True)
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)
489
479
  return 0 if state in _OK_STATES else 1
490
- printed_any = _print_log_snapshot(client, args.run_id)
491
- _print_worker_output(client, args.run_id, printed_any=printed_any)
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))
492
484
  return 0
493
485
 
494
486
 
@@ -496,11 +488,7 @@ def cmd_status(args) -> int:
496
488
  client = client_from_config()
497
489
  if getattr(args, "follow", False):
498
490
  return _follow_status(client, args.run_id)
499
- status = client.get_run(args.run_id)
500
- if render.styled():
501
- print(render.run_status(status))
502
- else:
503
- print(json.dumps(status, indent=2))
491
+ print(_render_status(client.get_run(args.run_id)))
504
492
  return 0
505
493
 
506
494
 
@@ -553,20 +541,37 @@ def cmd_checkpoints(args) -> int:
553
541
  if render.styled():
554
542
  print(render.checkpoints_table(args.run_id, checkpoints))
555
543
  return 0
544
+ from flash.schema import format_checkpoint_ref
545
+
556
546
  for c in checkpoints:
557
- 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'])}")
558
550
  print(
559
- f"\ndeploy one with `flash deploy {args.run_id} --step <STEP>`.",
551
+ f"\ndeploy one with `flash deploy {args.run_id}/step-<STEP>`.",
560
552
  file=sys.stderr,
561
553
  )
562
554
  return 0
563
555
 
564
556
 
565
557
  def cmd_deploy(args) -> int:
566
- 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(
567
572
  args.run_id,
568
573
  dry_run=args.dry_run,
569
- step=getattr(args, "step", None),
574
+ verify=not getattr(args, "no_verify", False),
570
575
  )
571
576
  if render.styled():
572
577
  print(render.deployed(dep))
@@ -574,11 +579,35 @@ def cmd_deploy(args) -> int:
574
579
  print(json.dumps(dep, indent=2))
575
580
  # a dry run creates no deployment, so the billing / undeploy hint would be misleading.
576
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 "")
577
584
  note = (
578
- 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}` "
579
586
  "to deregister the adapter."
580
587
  )
581
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
+ )
582
611
  return 0
583
612
 
584
613
 
@@ -592,9 +621,8 @@ def cmd_export(args) -> int:
592
621
  "(export it in your shell or put it in a local .env / .env.local)"
593
622
  )
594
623
  client = client_from_config()
595
- where = f" (step {args.step})" if args.step is not None else ""
596
624
  progress = (
597
- f"exporting adapter {args.adapter_id}{where} to {args.repository} — "
625
+ f"exporting adapter {args.adapter_id} to {args.repository} — "
598
626
  "downloading then re-uploading; this can take a minute..."
599
627
  )
600
628
  print(render.note(progress) if render.styled() else progress, file=sys.stderr)
@@ -602,7 +630,6 @@ def cmd_export(args) -> int:
602
630
  args.adapter_id,
603
631
  repository=args.repository,
604
632
  hf_token=hf_token,
605
- step=args.step,
606
633
  private=not args.public,
607
634
  )
608
635
  if render.styled():
@@ -639,16 +666,23 @@ def cmd_deployments(args) -> int:
639
666
  if render.styled():
640
667
  print(render.deployments_table(rows))
641
668
  return 0
642
- print(f"{'RUN_ID':<32} {'GPU':<9} ENDPOINT")
669
+ print(f"{'RUN_ID':<32} {'STATE':<10} {'GPU':<9} {'ENDPOINT':<32} DETAIL")
643
670
  for r in rows:
644
671
  d = r.get("deployment") or {}
645
- 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
+ )
646
677
  return 0
647
678
 
648
679
 
649
680
  def cmd_chat(args) -> int:
650
681
  client = client_from_config()
651
682
  messages = [{"role": "user", "content": args.message}]
683
+ system = getattr(args, "system", None)
684
+ if system:
685
+ messages.insert(0, {"role": "system", "content": system})
652
686
  if render.styled():
653
687
  print(render.chat_label())
654
688
  stream = getattr(client, "chat_stream", None)
@@ -188,25 +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
- def normalize_segment(value: str) -> str | None:
194
- segment = re.sub(r"[^a-z0-9._-]+", "-", value.lower()).strip("-")
195
- if segment in {"", ".", ".."} or not re.search(r"[a-z0-9]", segment):
196
- return None
197
- return segment
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
198
197
 
199
198
  text = str(raw or "").strip()
200
199
  if "/" not in text:
201
- return normalize_segment(text)
200
+ return normalize_env_name_segment(text)
202
201
  parts = [part.strip() for part in text.split("/")]
203
202
  if len(parts) != 2 or not all(parts):
204
203
  return None
205
- namespace = normalize_segment(parts[0])
206
- name = normalize_segment(parts[1])
207
- if not namespace or not name:
204
+ name = normalize_env_name_segment(parts[1])
205
+ if name is None:
208
206
  return None
209
- return f"{namespace}/{name}"
207
+ return f"{parts[0]}/{name}"
210
208
 
211
209
 
212
210
  def _with_syspath_bootstrap(env_source: str) -> str:
@@ -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
 
@@ -138,7 +138,8 @@ flash cancel <run-id> # stop a run
138
138
 
139
139
  ```bash
140
140
  flash checkpoints <run-id> # deployable per-step RL checkpoints
141
- flash deploy <run-id> # serve the trained adapter (--step N for an intermediate checkpoint)
141
+ flash deploy <run-id> # serve the trained adapter
142
+ flash deploy <run-id>/step-N # serve an intermediate checkpoint
142
143
  flash chat <run-id> -m "hello" # chat with the deployed adapter
143
144
  flash deployments # active serving endpoints
144
145
  flash undeploy <run-id> # tear the endpoint down
@@ -208,7 +209,7 @@ spending another GPU run:
208
209
  | Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_tokens` for GRPO rollouts or `max_length` for SFT only after seeing truncation. Oversizing them by default just burns memory/cost. |
209
210
  | Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash log <run-id>`, reduce footprint (`max_length`, `max_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
210
211
  | Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash log <run-id> --follow` to reattach, `flash log <run-id>` for the console/error output, or `flash cancel <run-id>` if you intentionally want to stop it. |
211
- | Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id> --step N`, and compare with held-out probes before exporting or relying on the final adapter. |
212
+ | Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id>/step-N`, and compare with held-out probes before exporting or relying on the final adapter. |
212
213
  | Export fails before upload | CLI says no HuggingFace token | Pass `flash export --api-key hf_...`, or set `HF_TOKEN` in your shell, `.env`, or `.env.local`. Exports are private unless you pass `--public`. |
213
214
  | SFT loss improves but quality does not | Train loss falls while held-out behavior stalls or degrades | Keep a held-out split outside training. Deploy and score that split; if quality drops, reduce epochs or improve data instead of adding more passes. |
214
215
  | Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, cap steps/epochs for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
@@ -350,9 +351,9 @@ Pick SFT when you already have good answers and want the model to imitate them.
350
351
  algorithm = "grpo"
351
352
 
352
353
  [train]
353
- # paste the full adapter_ref `flash status <sft-run-id>` prints, verbatim
354
- # (shape: <owner>/<repo>:sft/<run-id> the owner/repo prefix is required)
355
- init_from_adapter = "your-org/your-repo:sft/<sft-run-id>"
354
+ # the SFT run id (as printed by `flash status`); add /step-N to warm-start from a
355
+ # specific checkpoint listed by `flash checkpoints <run-id>`
356
+ init_from_adapter = "<sft-run-id>"
356
357
  lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effective serving cap
357
358
  lora_alpha = 32
358
359
  ```
@@ -479,12 +480,12 @@ flash runs # list your runs and their state/cost
479
480
  flash cancel <run-id> # stop a live run
480
481
  flash checkpoints <run-id> # list deployable RL checkpoints
481
482
  flash deploy <run-id> # serve the trained adapter
482
- flash deploy <run-id> --step N # serve a specific RL checkpoint
483
+ flash deploy <run-id>/step-N # serve a specific RL checkpoint
483
484
  flash chat <run-id> -m "probe" # stream a reply from the deployed adapter
484
485
  flash deployments # list active serving deployments
485
486
  flash undeploy <run-id> # tear down an active deployment
486
487
  flash export --adapter-id <run-id> --repository <you>/<repo> # export final adapter
487
- flash export --adapter-id <run-id> --repository <you>/<repo> --step N # export a checkpoint
488
+ flash export --adapter-id <run-id>/step-N --repository <you>/<repo> # export a checkpoint
488
489
  ```
489
490
 
490
491
  See the full reference at https://freesolo.co/docs.
@@ -98,18 +98,6 @@ class _ProgressReader:
98
98
  return chunk
99
99
 
100
100
 
101
- def _validate_checkpoint_step(step: int | float) -> int:
102
- """Validate a checkpoint step before the request: reject bool (int(True)==1) and fractional floats
103
- (int(2.7)==2 would target the WRONG checkpoint), then return the int — fail fast before the server."""
104
- if isinstance(step, bool):
105
- raise ClientError(f"invalid checkpoint step: {step!r} (must be an integer)")
106
- if isinstance(step, float) and not step.is_integer():
107
- raise ClientError(
108
- f"invalid checkpoint step: {step!r} (must be a whole number, not fractional)"
109
- )
110
- return int(step)
111
-
112
-
113
101
  def _validate_chat_messages(messages: list[dict]) -> None:
114
102
  if not isinstance(messages, list):
115
103
  raise ClientError("chat messages must be a list")
@@ -233,31 +221,45 @@ class ApiClient:
233
221
  return self._request("GET", f"/v1/runs/{run_id}/logs?offset={int(offset)}")
234
222
 
235
223
  def get_worker_output(self, run_id: str) -> dict[str, str]:
236
- return self._request("GET", f"/v1/runs/{run_id}/worker").get("worker", {})
224
+ try:
225
+ return self._request("GET", f"/v1/runs/{run_id}/worker").get("worker", {})
226
+ except ApiError as exc:
227
+ if exc.status == 404 and "not found" in str(exc).strip().lower():
228
+ return {}
229
+ raise
237
230
 
238
231
  def cancel_run(self, run_id: str) -> dict:
239
- return self._request("POST", f"/v1/runs/{run_id}/cancel")
232
+ # Cancel blocks on synchronous provider teardown (worker stop + instance destroy +
233
+ # endpoint GC), which can far exceed the default 60s — timing out here left the CLI
234
+ # printing a traceback while the server went on to mark the run cancelled anyway.
235
+ return self._request("POST", f"/v1/runs/{run_id}/cancel", timeout=10 * 60)
240
236
 
241
237
  def checkpoints(self, run_id: str) -> list[dict]:
242
- """Deployable per-step RL checkpoints for a run (each `flash deploy --step N`-able)."""
238
+ """Deployable per-step RL checkpoints for a run (serve one with `flash deploy RUN/step-N`)."""
243
239
  return self._request("GET", f"/v1/runs/{run_id}/checkpoints")["checkpoints"]
244
240
 
245
241
  def deploy(
246
242
  self,
247
243
  run_id: str,
248
244
  dry_run: bool = False,
249
- step: int | None = None,
245
+ verify: bool = True,
250
246
  ) -> dict:
251
- # Deploy blocks on registration and serving warmup, which can take many minutes.
252
- deploy_timeout = 30 * 60 if not dry_run else None
253
- body: dict = {"dry_run": dry_run}
247
+ from flash.schema import parse_checkpoint_ref
248
+
249
+ parsed = parse_checkpoint_ref(run_id)
250
+ if parsed is None:
251
+ raise ClientError(
252
+ "invalid adapter id: expected RUN_ID for the final adapter or RUN_ID/step-N "
253
+ "for a saved checkpoint"
254
+ )
255
+ base_run_id, step = parsed
256
+ body: dict = {"dry_run": dry_run, "verify": verify}
254
257
  if step is not None:
255
- body["step"] = _validate_checkpoint_step(step)
258
+ body["step"] = step
256
259
  return self._request(
257
260
  "POST",
258
- f"/v1/runs/{run_id}/deploy",
261
+ f"/v1/runs/{base_run_id}/deploy",
259
262
  body=body,
260
- timeout=deploy_timeout,
261
263
  )
262
264
 
263
265
  def export(
@@ -266,15 +268,23 @@ class ApiClient:
266
268
  *,
267
269
  repository: str,
268
270
  hf_token: str,
269
- step: int | None = None,
270
271
  private: bool = True,
271
272
  ) -> dict:
272
273
  """Copy a run's adapter into a user-owned HuggingFace repo."""
274
+ from flash.schema import parse_checkpoint_ref
275
+
276
+ parsed = parse_checkpoint_ref(run_id)
277
+ if parsed is None:
278
+ raise ClientError(
279
+ "invalid adapter id: expected RUN_ID for the final adapter or RUN_ID/step-N "
280
+ "for a saved checkpoint"
281
+ )
282
+ base_run_id, step = parsed
273
283
  body: dict = {"repository": repository, "hf_token": hf_token, "private": private}
274
284
  if step is not None:
275
- body["step"] = _validate_checkpoint_step(step)
285
+ body["step"] = step
276
286
  return self._request(
277
- "POST", f"/v1/runs/{run_id}/export", body=body, timeout=30 * 60
287
+ "POST", f"/v1/runs/{base_run_id}/export", body=body, timeout=30 * 60
278
288
  )
279
289
 
280
290
  def undeploy(self, run_id: str) -> dict:
@@ -289,13 +299,14 @@ class ApiClient:
289
299
  messages: list[dict],
290
300
  temperature: float = 0.0,
291
301
  max_tokens: int = 512,
302
+ timeout: float | None = None,
292
303
  ) -> dict:
293
304
  _validate_chat_messages(messages)
294
305
  return self._request(
295
306
  "POST",
296
307
  f"/v1/runs/{run_id}/chat",
297
308
  body={"messages": messages, "temperature": temperature, "max_tokens": max_tokens},
298
- timeout=30 * 60,
309
+ timeout=timeout if timeout is not None else 30 * 60,
299
310
  )
300
311
 
301
312
  def chat_stream(