freesolo-flash-dev 1.0.5__tar.gz → 1.0.7__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 (359) hide show
  1. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/PKG-INFO +1 -1
  2. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/render.py +1 -1
  3. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cost/analytical.py +3 -3
  4. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cost/facts.py +25 -0
  5. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/vram.py +11 -27
  6. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd.py +118 -11
  7. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/base.py +4 -21
  8. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/schema/__init__.py +3 -3
  9. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/reconcile.py +2 -1
  10. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/pyproject.toml +2 -2
  11. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_allocator.py +38 -7
  12. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_render_theme.py +43 -1
  13. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_estimate.py +30 -0
  14. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_hardware.py +19 -0
  15. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_gpus.py +8 -6
  16. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_opd.py +252 -60
  17. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_spec_and_validation.py +4 -4
  18. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/uv.lock +1 -1
  19. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.claude/skills/verify/SKILL.md +0 -0
  20. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.dockerignore +0 -0
  21. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.env.example +0 -0
  22. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/auto-rebake.yml +0 -0
  23. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/bake-kernel-cache.yml +0 -0
  24. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/ci.yml +0 -0
  25. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/main-source-guard.yml +0 -0
  26. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/notify-tests-repo.yml +0 -0
  27. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/publish-dev.yml +0 -0
  28. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/publish-image.yml +0 -0
  29. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/publish.yml +0 -0
  30. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/version-parity.yml +0 -0
  31. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.github/workflows/worker-image.yml +0 -0
  32. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/.gitignore +0 -0
  33. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/Dockerfile +0 -0
  34. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/Dockerfile.worker +0 -0
  35. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/LICENSE +0 -0
  36. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/README.md +0 -0
  37. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/build/kernel_cache/.gitignore +0 -0
  38. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/build/kernel_cache/.keep +0 -0
  39. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/Dockerfile.kernelcache +0 -0
  40. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/Dockerfile.kernelcache.relayer +0 -0
  41. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/bake_kernel_cache.py +0 -0
  42. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/bake_pod_entry.py +0 -0
  43. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/kernel_fingerprint.py +0 -0
  44. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/docker/make_rp_handler.py +0 -0
  45. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/__init__.py +0 -0
  46. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/_channel.py +0 -0
  47. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/_fileio.py +0 -0
  48. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/_logging.py +0 -0
  49. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/_update_check.py +0 -0
  50. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/adapter_artifacts.py +0 -0
  51. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/catalog.py +0 -0
  52. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/__init__.py +0 -0
  53. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/__main__.py +0 -0
  54. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/_tty.py +0 -0
  55. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/commands.py +0 -0
  56. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/env_setup.py +0 -0
  57. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/envpush.py +0 -0
  58. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cli/training_doc.py +0 -0
  59. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/client/__init__.py +0 -0
  60. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/client/config.py +0 -0
  61. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/client/http.py +0 -0
  62. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/client/runtime_secrets.py +0 -0
  63. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/client/specs.py +0 -0
  64. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cost/__init__.py +0 -0
  65. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cost/spec.py +0 -0
  66. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/cost/types.py +0 -0
  67. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/diagnostics.py +0 -0
  68. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/__init__.py +0 -0
  69. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/accounting.py +0 -0
  70. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/chalk_kernels.py +0 -0
  71. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/multiturn_reward_scoring.py +0 -0
  72. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/multiturn_rollout.py +0 -0
  73. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/recipe.py +0 -0
  74. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/steps.py +0 -0
  75. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/structured_outputs.py +0 -0
  76. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/__init__.py +0 -0
  77. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/__main__.py +0 -0
  78. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/_pkg.py +0 -0
  79. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/adapter.py +0 -0
  80. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/decoding.py +0 -0
  81. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/finalize.py +0 -0
  82. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/gpu_setup.py +0 -0
  83. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo.py +0 -0
  84. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo_multimodal.py +0 -0
  85. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
  86. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/heartbeat.py +0 -0
  87. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/hf.py +0 -0
  88. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/kernel_warmup.py +0 -0
  89. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/lora.py +0 -0
  90. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd_gkd.py +0 -0
  91. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd_vllm.py +0 -0
  92. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/packing.py +0 -0
  93. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/__init__.py +0 -0
  94. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/attn.py +0 -0
  95. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/diagnostics.py +0 -0
  96. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/lifecycle.py +0 -0
  97. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/liger.py +0 -0
  98. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/loraplus.py +0 -0
  99. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/memory.py +0 -0
  100. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/rl.py +0 -0
  101. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/rng.py +0 -0
  102. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/sft.py +0 -0
  103. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/teacher.py +0 -0
  104. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/tokenizer_align.py +0 -0
  105. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker/wandb_log.py +0 -0
  106. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/engine/worker_entrypoint.py +0 -0
  107. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/__init__.py +0 -0
  108. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/adapter.py +0 -0
  109. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/archive.py +0 -0
  110. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/archive_policy.py +0 -0
  111. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/base.py +0 -0
  112. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/loader.py +0 -0
  113. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/pull.py +0 -0
  114. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/envs/registry.py +0 -0
  115. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/lora_rank.py +0 -0
  116. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/multimodal.py +0 -0
  117. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/opd_retry_contract.py +0 -0
  118. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/__init__.py +0 -0
  119. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_auth.py +0 -0
  120. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_deadline.py +0 -0
  121. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_hf_artifacts.py +0 -0
  122. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_hf_retry.py +0 -0
  123. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_http.py +0 -0
  124. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_instance.py +0 -0
  125. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_bootstrap.py +0 -0
  126. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_poll.py +0 -0
  127. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_provider.py +0 -0
  128. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_poll.py +0 -0
  129. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/_worker.py +0 -0
  130. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/allocator.py +0 -0
  131. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/__init__.py +0 -0
  132. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/api.py +0 -0
  133. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/auth.py +0 -0
  134. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/gpus.py +0 -0
  135. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  136. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  137. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/preflight.py +0 -0
  138. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/pricing.py +0 -0
  139. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/preflight.py +0 -0
  140. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/realized.py +0 -0
  141. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/__init__.py +0 -0
  142. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/api.py +0 -0
  143. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/auth.py +0 -0
  144. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/cost.py +0 -0
  145. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/gpus.py +0 -0
  146. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/jobs.py +0 -0
  147. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/keys.py +0 -0
  148. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/preflight.py +0 -0
  149. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/preload.py +0 -0
  150. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/pricing.py +0 -0
  151. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/slots.py +0 -0
  152. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/train/__init__.py +0 -0
  153. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/train/endpoints.py +0 -0
  154. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/__init__.py +0 -0
  155. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/api.py +0 -0
  156. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/auth.py +0 -0
  157. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/jobs/__init__.py +0 -0
  158. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/jobs/builders.py +0 -0
  159. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/preflight.py +0 -0
  160. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/providers/vast/pricing.py +0 -0
  161. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/py.typed +0 -0
  162. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/runner/__init__.py +0 -0
  163. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/runner/checkpoints.py +0 -0
  164. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/runner/deploy.py +0 -0
  165. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/runner/lifecycle.py +0 -0
  166. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/runner/verified_revisions.py +0 -0
  167. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/schema/fields.py +0 -0
  168. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/serve/__init__.py +0 -0
  169. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/serve/deploy.py +0 -0
  170. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/serve/export.py +0 -0
  171. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/serve/pricing.py +0 -0
  172. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/serve/urls.py +0 -0
  173. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/__init__.py +0 -0
  174. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/__main__.py +0 -0
  175. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/_deps.py +0 -0
  176. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/_internal_client.py +0 -0
  177. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/_locks.py +0 -0
  178. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/_runtime.py +0 -0
  179. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/app.py +0 -0
  180. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/auth.py +0 -0
  181. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/billing.py +0 -0
  182. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/billing_retry.py +0 -0
  183. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/checkpoints.py +0 -0
  184. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/db.py +0 -0
  185. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/environment_registry.py +0 -0
  186. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/envs.py +0 -0
  187. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/repo_cleanup.py +0 -0
  188. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/routes/__init__.py +0 -0
  189. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/routes/envs.py +0 -0
  190. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/routes/meta.py +0 -0
  191. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/routes/runs.py +0 -0
  192. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/routes/serving.py +0 -0
  193. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/server/run_registry.py +0 -0
  194. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/flash/spec.py +0 -0
  195. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/infisical-entrypoint.sh +0 -0
  196. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/scripts/build_dev_dist.py +0 -0
  197. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/__init__.py +0 -0
  198. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/_helpers/__init__.py +0 -0
  199. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/_helpers/runner.py +0 -0
  200. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/_helpers/specs.py +0 -0
  201. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/_helpers/vast.py +0 -0
  202. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/conftest.py +0 -0
  203. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/fixtures/math_eval.jsonl +0 -0
  204. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/fixtures/math_train.jsonl +0 -0
  205. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/live/__init__.py +0 -0
  206. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/live/conftest.py +0 -0
  207. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/live/test_lambda_live.py +0 -0
  208. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/live/test_runpod_live.py +0 -0
  209. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/live/test_vast_live.py +0 -0
  210. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_accounting_coverage.py +0 -0
  211. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_agent_flash_cli_contract.py +0 -0
  212. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_algorithms.py +0 -0
  213. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_async_uploads.py +0 -0
  214. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_b200_rollout_opt.py +0 -0
  215. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_backend_jobspec_contract.py +0 -0
  216. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_base_model_provenance.py +0 -0
  217. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_billing_retry.py +0 -0
  218. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cancel_remote.py +0 -0
  219. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_catalog_consistency.py +0 -0
  220. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_chalk_kernels.py +0 -0
  221. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_charge_pricing.py +0 -0
  222. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_checkpoints.py +0 -0
  223. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_commands.py +0 -0
  224. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_commands_coverage.py +0 -0
  225. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_errors.py +0 -0
  226. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_estimate.py +0 -0
  227. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_help.py +0 -0
  228. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_main_coverage.py +0 -0
  229. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_managed.py +0 -0
  230. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cli_render_coverage.py +0 -0
  231. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_client.py +0 -0
  232. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_client_server_integration.py +0 -0
  233. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_client_stream_reads.py +0 -0
  234. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_config_overrides.py +0 -0
  235. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_controlled_experiment_repairs.py +0 -0
  236. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_analytical.py +0 -0
  237. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_equation.py +0 -0
  238. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_models.py +0 -0
  239. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_cost_rewards.py +0 -0
  240. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_deploy_latency.py +0 -0
  241. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_dev_channel.py +0 -0
  242. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_disk_gb.py +0 -0
  243. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_endpoint_name.py +0 -0
  244. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_cache_evict.py +0 -0
  245. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_delete.py +0 -0
  246. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_download.py +0 -0
  247. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_download_api.py +0 -0
  248. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_no_install.py +0 -0
  249. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_publish.py +0 -0
  250. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_pull.py +0 -0
  251. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_coverage.py +0 -0
  252. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_loader_coverage.py +0 -0
  253. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_managed_control_plane.py +0 -0
  254. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_push.py +0 -0
  255. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_env_rate_limit_resolve.py +0 -0
  256. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_envs_coverage.py +0 -0
  257. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_export.py +0 -0
  258. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_flash_mvp.py +0 -0
  259. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_flash_worker.py +0 -0
  260. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_flashinfer_cache_dirs.py +0 -0
  261. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_github_urlopen_retry.py +0 -0
  262. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_grpo_mask_aware.py +0 -0
  263. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_grpo_params.py +0 -0
  264. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_grpo_perturn.py +0 -0
  265. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_grpo_sleep_gate.py +0 -0
  266. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_health_liveness.py +0 -0
  267. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_hf_retry.py +0 -0
  268. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_idle_endpoint_reaper.py +0 -0
  269. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_instance_bootstrap_coverage.py +0 -0
  270. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_internal_client.py +0 -0
  271. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_jobs.py +0 -0
  272. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_kernel_cache.py +0 -0
  273. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_kernel_fingerprint.py +0 -0
  274. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_kv_util.py +0 -0
  275. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_lambda_api_coverage.py +0 -0
  276. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_lambda_runner.py +0 -0
  277. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_logging.py +0 -0
  278. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_login_perms.py +0 -0
  279. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_lora_rank_coverage.py +0 -0
  280. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_lora_rank_preflight.py +0 -0
  281. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_managed_hf_repo.py +0 -0
  282. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_metrics_schema_agent_contract.py +0 -0
  283. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_mig_guard.py +0 -0
  284. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multimodal_input_grads.py +0 -0
  285. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multimodal_training.py +0 -0
  286. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_image_rollout.py +0 -0
  287. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_per_turn_reward.py +0 -0
  288. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout.py +0 -0
  289. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_coverage.py +0 -0
  290. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_records.py +0 -0
  291. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_request_policy.py +0 -0
  292. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_named_reward_metrics.py +0 -0
  293. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_oom_escalate_gpu.py +0 -0
  294. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_opd_full_state_resume.py +0 -0
  295. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_opd_resume_safety.py +0 -0
  296. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_opd_vllm.py +0 -0
  297. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_open_model_policy.py +0 -0
  298. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_orchestrator_flash.py +0 -0
  299. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_packing.py +0 -0
  300. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_packing_coverage.py +0 -0
  301. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_poll_helpers.py +0 -0
  302. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_preflight.py +0 -0
  303. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_provider_preflight_coverage.py +0 -0
  304. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_provider_routing.py +0 -0
  305. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_provider_teardown_robustness.py +0 -0
  306. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_providers_symmetry.py +0 -0
  307. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_realized_cost.py +0 -0
  308. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_repo_cleanup.py +0 -0
  309. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_resolve_params_b.py +0 -0
  310. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_resume_on_retry.py +0 -0
  311. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_runmgmt.py +0 -0
  312. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_runpod_api_delete.py +0 -0
  313. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_runpod_key_fingerprint.py +0 -0
  314. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_runpod_key_waterfall.py +0 -0
  315. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_runpod_slots.py +0 -0
  316. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_serve.py +0 -0
  317. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_api.py +0 -0
  318. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_app_coverage.py +0 -0
  319. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_auth_singleflight.py +0 -0
  320. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_billing.py +0 -0
  321. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_checkpoints_coverage.py +0 -0
  322. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_db.py +0 -0
  323. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_envs_coverage.py +0 -0
  324. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_server_locks_coverage.py +0 -0
  325. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_serving_context_preflight.py +0 -0
  326. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_serving_contract.py +0 -0
  327. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_serving_pricing_coverage.py +0 -0
  328. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_serving_schema_coverage.py +0 -0
  329. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_sft_activation_shaping.py +0 -0
  330. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_sft_example_selection.py +0 -0
  331. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_sft_gc_off.py +0 -0
  332. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_sft_max_context.py +0 -0
  333. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_sft_tokenize_cache.py +0 -0
  334. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_single_turn_multimodal_grpo.py +0 -0
  335. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_structured_outputs.py +0 -0
  336. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_thinking_config.py +0 -0
  337. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_train_context_preflight.py +0 -0
  338. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_training_controls.py +0 -0
  339. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_update_check.py +0 -0
  340. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vast_api.py +0 -0
  341. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vast_api_coverage.py +0 -0
  342. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vast_offers.py +0 -0
  343. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vast_provider_coverage.py +0 -0
  344. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vast_runner.py +0 -0
  345. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_verified_revisions.py +0 -0
  346. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_verifiers.py +0 -0
  347. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_version.py +0 -0
  348. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  349. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_wandb_log_coverage.py +0 -0
  350. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_wandb_naming.py +0 -0
  351. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_warmstart_adapter_download.py +0 -0
  352. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_warmstart_cross_repo.py +0 -0
  353. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_weight_cache.py +0 -0
  354. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_worker_dryrun.py +0 -0
  355. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_worker_hardexit.py +0 -0
  356. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_worker_image.py +0 -0
  357. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_worker_init_heartbeat.py +0 -0
  358. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/tests/test_worker_stack.py +0 -0
  359. {freesolo_flash_dev-1.0.5 → freesolo_flash_dev-1.0.7}/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: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Flash — managed LoRA post-training (SFT/GRPO/OPD) 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
@@ -440,7 +440,7 @@ def gpus_table(rows: list[tuple[str, int, float | None]], tip: str) -> str:
440
440
 
441
441
  def gpu_label(spec: dict, remote: dict) -> str:
442
442
  """Human-facing GPU label. Provider metadata stays internal."""
443
- return remote.get("gpu") or (spec.get("gpu") or {}).get("type", "")
443
+ return remote.get("allocated_gpu") or (spec.get("gpu") or {}).get("type", "")
444
444
 
445
445
 
446
446
  def runs_table(runs: list[dict]) -> str:
@@ -14,8 +14,8 @@ from flash.providers.allocator import required_vram_gb, vram_headroom
14
14
  from .facts import (
15
15
  active_params_b,
16
16
  download_weight_gb,
17
+ effective_train_tflops,
17
18
  gpu_hourly_usd,
18
- gpu_tflops,
19
19
  gpu_vram_gb,
20
20
  model_quant,
21
21
  pick_gpu,
@@ -150,7 +150,7 @@ def compile_seconds(config: RunConfig, gpu: str) -> float:
150
150
  def seconds_per_step(config: RunConfig, gpu: str) -> float:
151
151
  """Steady-state wall time for one optimizer step on ``gpu``."""
152
152
  n = config.normalized()
153
- peak = gpu_tflops(gpu) * 1e12 # FLOP/s
153
+ peak = effective_train_tflops(gpu) * 1e12 # FLOP/s (realized training throughput; see facts)
154
154
  # An MoE's per-step wall scales with TOTAL params (routing + all-expert coordination + grouped
155
155
  # GEMM under-utilization), not the tiny active-param FLOPs; dense models keep active (== total).
156
156
  moe = _is_moe(n.model_id)
@@ -198,7 +198,7 @@ def sft_seconds_for_tokens(config: RunConfig, gpu: str, train_tokens: float) ->
198
198
  moe = _is_moe(n.model_id)
199
199
  params = (total_params_b(n.model_id) if moe else active_params_b(n.model_id)) * 1e9
200
200
  mfu = MFU_SFT_TRAIN_MOE if moe else MFU_SFT_TRAIN
201
- peak = gpu_tflops(gpu) * 1e12
201
+ peak = effective_train_tflops(gpu) * 1e12
202
202
  flops = SFT_FLOPS_PER_TOKEN_PER_PARAM * params * train_tokens
203
203
  return flops / (peak * mfu)
204
204
 
@@ -30,6 +30,31 @@ def gpu_tflops(name: str) -> float:
30
30
  return GPU_COMPUTE_TFLOPS.get(name, _DEFAULT_TFLOPS)
31
31
 
32
32
 
33
+ # realized TRAINING throughput sits well below peak when a class's training kernels don't reach it.
34
+ # b200 (sm100) has no arch-tuned training kernels yet and falls back to the same portable paths as
35
+ # h200 (sm90), so its 2.25 pflops dense-bf16 peak does not materialize for training -- realized
36
+ # throughput is h200-class (and frequently lower for rl/grpo). cap b200 at h200 so the analytical
37
+ # cost model does not rank it as faster/cheaper than h200 on peak flops alone (it is not, and is
38
+ # often slower). this is a conservative floor: it never prices b200 above h200-equivalent training
39
+ # time, so it cannot over-charge, and it removes the "b200 looks cheapest" inversion at the source.
40
+ # refine per-workload once real b200 training samples exist. the vram/serving paths keep the true
41
+ # peak via gpu_tflops; only the training-time model uses this.
42
+ _TRAIN_TFLOPS_CAP: dict[str, float] = {
43
+ "B200": 990.0, # h200-class realized training throughput, not the 2250 dense-bf16 peak
44
+ }
45
+
46
+
47
+ def effective_train_tflops(name: str) -> float:
48
+ """Realized bf16 TRAINING throughput for the analytical cost model.
49
+
50
+ Equal to ``gpu_tflops`` (peak) except where a class's training kernels fall short of peak: b200
51
+ training falls back to portable kernels, so it is capped at h200-class throughput rather than
52
+ its raw 2.25 pflops peak."""
53
+ peak = gpu_tflops(name)
54
+ cap = _TRAIN_TFLOPS_CAP.get(name)
55
+ return min(peak, cap) if cap is not None else peak
56
+
57
+
33
58
  def gpu_hourly_usd(
34
59
  name: str,
35
60
  provider: str | None = None,
@@ -27,6 +27,8 @@ _BYTES_PER_PARAM = {
27
27
  _BASE_OVERHEAD_GB = 4.0
28
28
  _ACT_COEF = 0.12
29
29
  _SFT_PER_DEVICE_BS_DEFAULT = 4
30
+ OPD_CE_CHUNK_SIZE = 64
31
+ _OPD_CE_PEAK_BYTES_PER_LOGIT = 16
30
32
 
31
33
 
32
34
  def _sft_per_device_bs() -> int:
@@ -112,7 +114,7 @@ def opd_rollout_concurrency(prompts_per_step: int = 1, group_size: int = 1) -> i
112
114
 
113
115
 
114
116
  def opd_loss_microbatch(params_b: float, prompts_per_step: int = 1, group_size: int = 1) -> int:
115
- """Loss microbatch size used by OPD's dense-logit GKD backward.
117
+ """Loss microbatch size used by OPD's GKD backward.
116
118
 
117
119
  Keep this in lockstep with worker.opd._opd_loss_microbatch_size without importing the worker from
118
120
  the sizing path. Small/medium catalog models run up to four loss samples per forward; 35B-class
@@ -370,35 +372,17 @@ def estimate_vram_gb(
370
372
  rollout = weights + max(
371
373
  _KV_CAP, _resident_kv_gb(eff_b, seq_len, rollout_concurrency, fp8_kv=fp8_kv)
372
374
  )
373
- # opd's gkd loss forward materializes DENSE logits there is NO fused cross-entropy: the
374
- # student forward yields full-sequence bf16 logits [seq, vocab], then the completion rows are
375
- # gathered in fp32 [completion, vocab] for the logsumexp. The SFT fused path budgets ZERO
376
- # vocab logits for >=3B models, so a long-completion opd job (e.g. max_tokens=8192) would be
377
- # sized for an under-capacity card and OOM.
378
- #
379
- # Mirror opd_rollout_seq_len / run_opd's completion resolution: explicit max_tokens, else the
380
- # OPD recipe default (thinking uses the longer max_completion_len_thinking). A GRPO-style
381
- # min(seq_len, 1024) fallback would UNDER-budget a thinking opd job (1536-token completions).
375
+ # text opd projects only completion-prediction hidden states and computes exact full-vocabulary
376
+ # ce in checkpointed chunks. image samples still use the dense full-sequence logits path and are
377
+ # processed one at a time. allocation does not know the dataset modality, so reserve the larger
378
+ # of one text ce chunk and one dense image loss forward/backward peak.
382
379
  completion = opd_completion_len(max_tokens, thinking)
383
- # run_opd backprops a small loss microbatch. Budget that actual dense-logit microbatch, not
384
- # the full rollout/teacher batch and not a single sequence.
385
380
  loss_mb = opd_loss_microbatch(params_b, batch_size, group_size)
386
381
  activations = loss_mb * _ACT_COEF * (seq_len / 1024.0) * width
387
- # Dense-logit peak spans BOTH the forward and the loss BACKWARD (OPD has no fused CE):
388
- # - forward: bf16 full-sequence logits [seq, vocab] (seq * 2)
389
- # fp32 completion rows [completion, vocab] for logsumexp, saved for backward
390
- # (completion * 4)
391
- # - backward: fp32 gradient of those completion rows [completion, vocab]
392
- # (completion * 4)
393
- # bf16 gradient scattered back into the full logits [seq, vocab] to reach lm_head
394
- # (seq * 2)
395
- # All four coexist at the backward peak. The old formula counted only the two FORWARD buffers,
396
- # so a long-completion / large-vocab (248k) opd job under-budgeted the loss backward by
397
- # ~(completion*4 + seq*2)*vocab bytes and could route to a GPU that OOMs in OPD loss backward
398
- # (codex[bot]). Mirror the SFT dense-logit sizing by budgeting the backward buffers too.
399
- logits_fwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
400
- logits_bwd = loss_mb * (seq_len * 2 + completion * 4) * vocab
401
- logits = (logits_fwd + logits_bwd) / 1e9
382
+ ce_rows = min(OPD_CE_CHUNK_SIZE, loss_mb * completion)
383
+ chunked_logits = ce_rows * vocab * _OPD_CE_PEAK_BYTES_PER_LOGIT / 1e9
384
+ dense_image_logits = (seq_len * 4 + completion * 8) * vocab / 1e9
385
+ logits = max(chunked_logits, dense_image_logits)
402
386
  return base + rollout + activations + logits
403
387
  # Actual TRL SFT keeps fused CE disabled (see worker/sft.py), so dense logits materialize even
404
388
  # for long-context / >=3B models. Callers can pass sft_fused_ce=True only for theoretical
@@ -46,7 +46,7 @@ from flash.engine.structured_outputs import (
46
46
  parse_structured_outputs,
47
47
  reasoning_parser_for,
48
48
  )
49
- from flash.engine.vram import opd_completion_len
49
+ from flash.engine.vram import OPD_CE_CHUNK_SIZE, opd_completion_len
50
50
  from flash.engine.worker._pkg import W as _w
51
51
  from flash.engine.worker.heartbeat import liveness_heartbeat
52
52
  from flash.engine.worker.hf import RequiredSaveError, _deployable_adapter_on_hf
@@ -1645,7 +1645,9 @@ def run_opd():
1645
1645
  "opd_teacher_workers": max_teacher_workers,
1646
1646
  "opd_teacher_batch_size": teacher_batch_size,
1647
1647
  "opd_loss_microbatch_size": loss_microbatch_size,
1648
- "opd_full_logits_batches": getattr(model, "_flash_opd_full_logits_batches", 0),
1648
+ "opd_completion_only_batches": getattr(
1649
+ model, "_flash_opd_completion_only_batches", 0
1650
+ ),
1649
1651
  # Multi-turn: each assistant turn is distilled independently against its transcript-so-far
1650
1652
  # prefix, so a "sample" is a TURN, not a whole episode. Report the mode + turn ceiling and
1651
1653
  # the mean turns/episode so a run that collapsed to one-turn episodes (env never replied /
@@ -2193,6 +2195,88 @@ def _forward_logits(
2193
2195
  raise
2194
2196
 
2195
2197
 
2198
+ class _LmHeadBypass(Exception):
2199
+ pass
2200
+
2201
+
2202
+ def _forward_completion_hidden_states(
2203
+ model, input_ids, attention_mask, position_ids, *, logits_to_keep: int
2204
+ ):
2205
+ """Run the causal body through the normal model boundary but stop before the LM-head matmul."""
2206
+ output_embeddings = model.get_output_embeddings()
2207
+ input_embeddings = model.get_input_embeddings()
2208
+ if output_embeddings is None or output_embeddings is input_embeddings:
2209
+ raise RuntimeError("opd chunked ce requires a distinct output embedding module")
2210
+
2211
+ captured = {}
2212
+ marker = object()
2213
+
2214
+ def _capture_hidden(_module, args):
2215
+ if not args:
2216
+ raise RuntimeError("opd lm head received no hidden states")
2217
+ captured["hidden_states"] = args[0]
2218
+ raise _LmHeadBypass(marker)
2219
+
2220
+ handle = output_embeddings.register_forward_pre_hook(_capture_hidden)
2221
+ try:
2222
+ _forward_logits(
2223
+ model,
2224
+ input_ids,
2225
+ attention_mask,
2226
+ position_ids=position_ids,
2227
+ logits_to_keep=logits_to_keep,
2228
+ )
2229
+ except _LmHeadBypass as exc:
2230
+ if not exc.args or exc.args[0] is not marker:
2231
+ raise
2232
+ else:
2233
+ raise RuntimeError("opd could not intercept the lm head for chunked ce")
2234
+ finally:
2235
+ handle.remove()
2236
+
2237
+ hidden_states = captured.get("hidden_states")
2238
+ if hidden_states is None or hidden_states.shape[1] != logits_to_keep:
2239
+ raise RuntimeError("opd model did not honor completion-only logits_to_keep")
2240
+ return hidden_states, output_embeddings
2241
+
2242
+
2243
+ def _chunked_token_logps(lm_head, hidden_states, token_ids, *, chunk_size=OPD_CE_CHUNK_SIZE):
2244
+ """Compute exact realized-token logprobs with checkpointed vocabulary chunks."""
2245
+ import torch
2246
+ import torch.nn.functional as F
2247
+ from torch.utils.checkpoint import checkpoint
2248
+
2249
+ if hidden_states.ndim != 2 or token_ids.ndim != 1:
2250
+ raise ValueError("opd chunked ce expects [tokens, hidden] states and [tokens] ids")
2251
+ if hidden_states.shape[0] != token_ids.shape[0]:
2252
+ raise ValueError("opd chunked ce hidden-state and token counts differ")
2253
+ if hidden_states.shape[0] == 0:
2254
+ return hidden_states.new_empty((0,), dtype=torch.float32)
2255
+
2256
+ size = max(1, int(chunk_size))
2257
+
2258
+ def _logps(hidden_chunk, ids_chunk):
2259
+ logits = lm_head(hidden_chunk).float()
2260
+ return -F.cross_entropy(logits, ids_chunk, reduction="none")
2261
+
2262
+ chunks = []
2263
+ for start in range(0, hidden_states.shape[0], size):
2264
+ hidden_chunk = hidden_states[start : start + size]
2265
+ ids_chunk = token_ids[start : start + size]
2266
+ if torch.is_grad_enabled():
2267
+ chunks.append(
2268
+ checkpoint(
2269
+ _logps,
2270
+ hidden_chunk,
2271
+ ids_chunk,
2272
+ use_reentrant=False,
2273
+ )
2274
+ )
2275
+ else:
2276
+ chunks.append(_logps(hidden_chunk, ids_chunk))
2277
+ return torch.cat(chunks)
2278
+
2279
+
2196
2280
  def _bump_model_counter(model, name: str, inc: int = 1) -> None:
2197
2281
  with contextlib.suppress(Exception):
2198
2282
  setattr(model, name, int(getattr(model, name, 0) or 0) + int(inc))
@@ -2286,23 +2370,46 @@ def _resolve_samples_batched(
2286
2370
  mb = max(1, int(microbatch))
2287
2371
  for start in range(0, len(text_prepared), mb):
2288
2372
  chunk = text_prepared[start : start + mb]
2289
- _bump_model_counter(model, "_flash_opd_full_logits_batches")
2373
+ _bump_model_counter(model, "_flash_opd_completion_only_batches")
2290
2374
  seqs = [list(p.prompt_ids) + list(p.student_ids) for p in chunk]
2291
2375
  max_len = max(len(seq) for seq in seqs)
2376
+ max_completion = max(len(p.student_ids) for p in chunk)
2377
+ logits_to_keep = max_completion + 1
2292
2378
  input_ids = torch.full((len(seqs), max_len), pad_id, dtype=torch.long, device=device)
2293
2379
  attention_mask = torch.zeros((len(seqs), max_len), dtype=torch.long, device=device)
2294
2380
  for row, seq in enumerate(seqs):
2295
- input_ids[row, : len(seq)] = torch.tensor(seq, dtype=torch.long, device=device)
2296
- attention_mask[row, : len(seq)] = 1
2297
- logits = _forward_logits(model, input_ids, attention_mask)
2298
- losses = []
2381
+ offset = max_len - len(seq)
2382
+ input_ids[row, offset:] = torch.tensor(seq, dtype=torch.long, device=device)
2383
+ attention_mask[row, offset:] = 1
2384
+ position_ids = attention_mask.cumsum(dim=-1) - 1
2385
+ position_ids.masked_fill_(attention_mask == 0, 0)
2386
+ hidden_tail, lm_head = _forward_completion_hidden_states(
2387
+ model,
2388
+ input_ids,
2389
+ attention_mask,
2390
+ position_ids,
2391
+ logits_to_keep=logits_to_keep,
2392
+ )
2393
+ selected_hidden = []
2394
+ selected_ids = []
2299
2395
  for row, p in enumerate(chunk):
2300
- prompt_len = len(p.prompt_ids)
2301
2396
  comp_len = len(p.student_ids)
2302
- rows = logits[row, prompt_len - 1 : prompt_len - 1 + comp_len]
2303
- loss = _gkd_loss_from_logits_rows(
2304
- rows, p.student_ids, p.groups, kl_coef=knobs.kl_coef
2397
+ selected_hidden.append(
2398
+ hidden_tail[row, logits_to_keep - comp_len - 1 : logits_to_keep - 1]
2305
2399
  )
2400
+ selected_ids.extend(int(token_id) for token_id in p.student_ids)
2401
+ flat_logps = _chunked_token_logps(
2402
+ lm_head,
2403
+ torch.cat(selected_hidden, dim=0),
2404
+ torch.tensor(selected_ids, dtype=torch.long, device=device),
2405
+ )
2406
+ losses = []
2407
+ token_offset = 0
2408
+ for p in chunk:
2409
+ comp_len = len(p.student_ids)
2410
+ sp_t = flat_logps[token_offset : token_offset + comp_len]
2411
+ token_offset += comp_len
2412
+ loss = _gkd_loss_from_logps(sp_t, p.groups, kl_coef=knobs.kl_coef)
2306
2413
  if loss is None:
2307
2414
  results[p.idx] = SampleResult(
2308
2415
  teacher_status="ok",
@@ -137,19 +137,6 @@ GPU_CLASSES: tuple[GpuClass, ...] = (
137
137
  )
138
138
 
139
139
  GPU_INFO: dict[str, GpuClass] = {g.name: g for g in GPU_CLASSES}
140
- LEGACY_GPU_CLASSES: tuple[GpuClass, ...] = (
141
- GpuClass(
142
- "RTX A6000",
143
- "NVIDIA_RTX_A6000",
144
- 48,
145
- "a6000",
146
- "sm86",
147
- 0.49,
148
- lambda_name="gpu_1x_a6000",
149
- vast_name="RTX A6000",
150
- ),
151
- )
152
- _GPU_INFO_ALL: dict[str, GpuClass] = {**GPU_INFO, **{g.name: g for g in LEGACY_GPU_CLASSES}}
153
140
  KNOWN = tuple(GPU_INFO)
154
141
  VALIDATED = tuple(g.name for g in GPU_CLASSES if g.validated)
155
142
 
@@ -204,7 +191,7 @@ def _alias_keys(name: str) -> set[str]:
204
191
 
205
192
 
206
193
  _ALIAS_TARGETS: dict[str, set[str]] = {}
207
- for _info in _GPU_INFO_ALL.values():
194
+ for _info in GPU_INFO.values():
208
195
  for _provider_name in (_info.name, _info.vast_name, *_info.vast_aliases):
209
196
  if not _provider_name:
210
197
  continue
@@ -214,7 +201,7 @@ _ALIASES: dict[str, str] = {
214
201
  key: next(iter(targets)) for key, targets in _ALIAS_TARGETS.items() if len(targets) == 1
215
202
  }
216
203
  # exact canonical names always resolve to themselves even if a provider spelling collides with them.
217
- _ALIASES.update({_info.name.lower(): _info.name for _info in _GPU_INFO_ALL.values()})
204
+ _ALIASES.update({_info.name.lower(): _info.name for _info in GPU_INFO.values()})
218
205
  # full marketing names (nvidia-smi / runpod api) and historical aliases not covered by generic rules.
219
206
  _ALIASES.update(
220
207
  {
@@ -264,11 +251,7 @@ class UnreconciledCreateError(RuntimeError):
264
251
 
265
252
 
266
253
  def canonical_gpu(name: str) -> str:
267
- """Normalize a friendly GPU name to an active or retired class; raise otherwise.
268
-
269
- Retired classes resolve for teardown/pricing compatibility but stay absent from ``GPU_INFO`` so
270
- allocation, display, and provider offer matching do not select them.
271
- """
254
+ """Normalize a friendly GPU name to a managed GPU class; raise otherwise."""
272
255
  key = (name or "").strip().lower()
273
256
  if key in _ALIASES:
274
257
  return _ALIASES[key]
@@ -276,7 +259,7 @@ def canonical_gpu(name: str) -> str:
276
259
 
277
260
 
278
261
  def get_gpu_info(name: str) -> GpuClass:
279
- return _GPU_INFO_ALL[canonical_gpu(name)]
262
+ return GPU_INFO[canonical_gpu(name)]
280
263
 
281
264
 
282
265
  def gpu_classes_for(identity_attr: str) -> list[GpuClass]:
@@ -397,9 +397,9 @@ def spec_from_dict(raw: dict[str, Any], run_id: str | None = None) -> JobSpec:
397
397
  # exact_type it suggests might not be provisionable there.
398
398
  if "type" in gpu_raw and not exact_type and not gpu_provider:
399
399
  authored_type_raw = gpu_raw["type"]
400
- # only an active validated class is a usable exact_type pin; an unrecognized string or a
401
- # retired-but-known class (e.g. RTX A6000) must not be echoed back as an exact_type, since
402
- # the exact_type validation above would then reject the very config the note suggested.
400
+ # only an active validated class is a usable exact_type pin; an unrecognized string must
401
+ # not be echoed back as an exact_type, since the exact_type validation above would then
402
+ # reject the very config the note suggested.
403
403
  pinnable: str | None = None
404
404
  if isinstance(authored_type_raw, str):
405
405
  try:
@@ -106,6 +106,7 @@ def reconcile_run(status: runner.RunStatus, *, now: float | None = None) -> bool
106
106
  later cycle (within the window) retries once the provider invoice settles."""
107
107
  now = time.time() if now is None else now
108
108
  remote = status.remote or {}
109
+ spec = status.spec or {}
109
110
  # raw persisted RunStatus.remote may omit started_ts or contain a falsey value. 0.0 means an
110
111
  # unknown launch rather than the epoch; falling back to created_at prevents inflated flat-rate
111
112
  # instance billing.
@@ -126,7 +127,7 @@ def reconcile_run(status: runner.RunStatus, *, now: float | None = None) -> bool
126
127
  "runId": status.run_id,
127
128
  "realizedCostUsd": realized.realized_usd,
128
129
  "provider": realized.provider,
129
- "gpu": remote.get("allocated_gpu") or remote.get("gpu"),
130
+ "gpu": remote.get("allocated_gpu") or (spec.get("gpu") or {}).get("type"),
130
131
  "costByResource": realized.by_resource,
131
132
  "wallSeconds": realized.wall_seconds,
132
133
  "costBasis": "realized",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "1.0.5"
3
+ version = "1.0.7"
4
4
  description = "Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI"
5
5
  readme = "README.md"
6
6
  # RunPod Flash supports Python 3.11-3.12 (not 3.13 yet).
@@ -116,7 +116,7 @@ artifacts = ["flash/py.typed"]
116
116
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
117
117
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
118
118
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
119
- version = "1.0.5"
119
+ version = "1.0.7"
120
120
 
121
121
  [tool.pytest.ini_options]
122
122
  pythonpath = ["."]
@@ -468,6 +468,38 @@ def test_estimator_logits_term_uses_max_tokens_and_caps_at_budget():
468
468
  assert e(2.0, "sft", seq_len=4096, max_tokens=256) == e(2.0, "sft", seq_len=4096, max_tokens=8192)
469
469
 
470
470
 
471
+ def test_opd_vram_estimate_reserves_one_dense_image_loss_peak():
472
+ from flash.engine.vram import estimate_vram_gb as e
473
+
474
+ seq_len = 4096
475
+ completion = 512
476
+ vocab = 248_320
477
+ with_vocab = e(
478
+ 2.0,
479
+ "opd",
480
+ seq_len=seq_len,
481
+ max_tokens=completion,
482
+ vocab=vocab,
483
+ batch_size=4,
484
+ group_size=4,
485
+ )
486
+ without_vocab = e(
487
+ 2.0,
488
+ "opd",
489
+ seq_len=seq_len,
490
+ max_tokens=completion,
491
+ vocab=0,
492
+ batch_size=4,
493
+ group_size=4,
494
+ )
495
+ expected = (seq_len * 4 + completion * 8) * vocab / 1e9
496
+ assert with_vocab - without_vocab == pytest.approx(expected)
497
+
498
+ short = e(2.0, "opd", seq_len=seq_len, max_tokens=128, vocab=vocab)
499
+ long = e(2.0, "opd", seq_len=seq_len, max_tokens=2048, vocab=vocab)
500
+ assert long > short
501
+
502
+
471
503
  def test_open_model_opd_uses_opd_sizing_not_grpo(monkeypatch):
472
504
  """Regression (codex[bot], vram.py): for an uncataloged (model_policy='allow') model the open-model
473
505
  fallback hardcoded ``_need(params_b, 'grpo', ...)``, so an OPD run was sized as a colocated-vLLM GRPO
@@ -629,11 +661,11 @@ def test_observed_qwen2_opd_vllm_case_routes_off_32gb_cards(monkeypatch):
629
661
  "lora_rank": 32,
630
662
  "lora_alpha": 64,
631
663
  },
632
- "RTX Pro 6000",
664
+ "A100 PCIe",
633
665
  ),
634
666
  (
635
- # fp8 KV (cc >= 8.9) halves the resident rollout KV, so this fits the 141 GB H200 and no
636
- # longer needs the 180 GB B200 — still far off the 32/40 GB consumer classes this guards.
667
+ # the dense image fallback grows with context and keeps this mixed-modality-safe route
668
+ # above the 96 gb class.
637
669
  "max_context_16384",
638
670
  {
639
671
  "epochs": 1,
@@ -653,7 +685,7 @@ def test_observed_qwen2_opd_vllm_case_routes_off_32gb_cards(monkeypatch):
653
685
  "lora_rank": 32,
654
686
  "lora_alpha": 64,
655
687
  },
656
- None,
688
+ "H200",
657
689
  ),
658
690
  (
659
691
  "group_size_8",
@@ -748,9 +780,8 @@ def test_observed_qwen4b_opd_vllm_startup_case_routes_off_40gb_cards(monkeypatch
748
780
  }
749
781
 
750
782
  need = required_vram_gb("Qwen/Qwen3.5-4B", "opd", train=train)
751
- # Off the 40 GB class, but no longer forced onto the 141 GB H200: the colocated OPD rollout uses an
752
- # fp8 KV cache on cc >= 8.9 hardware, and a run this size can only land on a modern (cc >= 8.9)
753
- # card, so its KV pool is fp8-sized — it fits the cheaper 96 GB RTX Pro 6000 (Blackwell).
783
+ # the dense image fallback keeps this run above the 80 gb class while still fitting the 96 gb
784
+ # rtx pro 6000.
754
785
  assert 80 < need <= 96
755
786
 
756
787
  preview_gpu = provisional_gpu("Qwen/Qwen3.5-4B", "opd", train=train)
@@ -88,7 +88,7 @@ def test_runs_and_status_hide_provider_names(monkeypatch) -> None:
88
88
  "run_id": "flash-1",
89
89
  "state": "done",
90
90
  "spec": {"model": "Qwen/Qwen3.5-4B", "algorithm": "grpo"},
91
- "remote": {"gpu": "RTX 4090", "provider": "runpod", "flash_arm": "runpod"},
91
+ "remote": {"allocated_gpu": "RTX 4090", "provider": "runpod", "flash_arm": "runpod"},
92
92
  }
93
93
  runs = render.runs_table([run])
94
94
  status = render.run_status(run)
@@ -100,6 +100,48 @@ def test_runs_and_status_hide_provider_names(monkeypatch) -> None:
100
100
  assert "flash_arm" not in status.lower()
101
101
 
102
102
 
103
+ def test_runs_and_status_prefer_allocated_gpu(monkeypatch) -> None:
104
+ monkeypatch.setenv("FLASH_STYLE", "1")
105
+ monkeypatch.setenv("NO_COLOR", "1")
106
+
107
+ allocated_run = {
108
+ "run_id": "flash-allocated",
109
+ "state": "done",
110
+ "spec": {
111
+ "model": "Qwen/Qwen3.5-4B",
112
+ "algorithm": "grpo",
113
+ "gpu": {"type": "RTX Pro 6000"},
114
+ },
115
+ "remote": {"allocated_gpu": "B200"},
116
+ }
117
+ runs = render.runs_table([allocated_run])
118
+ status = render.run_status(allocated_run)
119
+ status_panel = status.split("details", 1)[0]
120
+ assert "B200" in runs
121
+ assert "B200" in status_panel
122
+ assert "RTX Pro 6000" not in runs
123
+ assert "RTX Pro 6000" not in status_panel
124
+
125
+ spec_only_run = {
126
+ "run_id": "flash-spec-only",
127
+ "state": "done",
128
+ "spec": {"gpu": {"type": "RTX Pro 6000"}},
129
+ }
130
+ assert "RTX Pro 6000" in render.runs_table([spec_only_run])
131
+ assert "RTX Pro 6000" in render.run_status(spec_only_run)
132
+
133
+ # the removed legacy remote["gpu"] key is no longer honored: the label resolves from the
134
+ # authoritative allocated_gpu or the provisional spec type, never the dead key.
135
+ legacy_key_run = {
136
+ "run_id": "flash-legacy-key",
137
+ "state": "done",
138
+ "spec": {"gpu": {"type": "RTX Pro 6000"}},
139
+ "remote": {"gpu": "B200"},
140
+ }
141
+ assert "B200" not in render.runs_table([legacy_key_run])
142
+ assert "RTX Pro 6000" in render.runs_table([legacy_key_run])
143
+
144
+
103
145
  def test_color_respects_no_color(monkeypatch) -> None:
104
146
  monkeypatch.setenv("FLASH_STYLE", "1")
105
147
  monkeypatch.setenv("TERM", "xterm-256color")
@@ -480,3 +480,33 @@ def test_estimate_exact_pinned_provider_routes_through_disk_aware_allocate(monke
480
480
  assert seen["disk_gb"] == 200.0
481
481
  # and the quote reflects the live allocation rather than a static gpu_hourly_usd lookup.
482
482
  assert (est.provider, est.gpu, est.gpu_hourly_usd) == ("runpod", "H100", 3.29)
483
+
484
+
485
+ def test_b200_not_cheaper_or_faster_than_h200_for_grpo(monkeypatch):
486
+ # regression: the estimator must not advertise b200 as faster/cheaper than h200 on peak flops.
487
+ # b200/sm100 training is h200-class (portable kernels), so at its higher $/hr b200 must never
488
+ # come out cheaper, and never faster, than h200 for the same run.
489
+ from types import SimpleNamespace
490
+
491
+ import flash.providers.allocator as allocator_mod
492
+ from flash.cost.facts import gpu_hourly_usd
493
+
494
+ def alloc_as(gpu):
495
+ rate = gpu_hourly_usd(gpu)
496
+
497
+ def fake_allocate(model_id, method, **kwargs):
498
+ return SimpleNamespace(gpu=gpu, provider="runpod", hourly_usd=rate, min_vram_gb=80)
499
+
500
+ return fake_allocate
501
+
502
+ monkeypatch.setattr(allocator_mod, "allocate", alloc_as("H200"))
503
+ h200 = estimate_cost(RunConfig("Qwen/Qwen3.5-4B", "grpo", 100, exact_type="H200"))
504
+ monkeypatch.setattr(allocator_mod, "allocate", alloc_as("B200"))
505
+ b200 = estimate_cost(RunConfig("Qwen/Qwen3.5-4B", "grpo", 100, exact_type="B200"))
506
+
507
+ assert gpu_hourly_usd("B200") > gpu_hourly_usd("H200") # b200 is the pricier card
508
+ # same effective training throughput => b200 is no faster than h200 ...
509
+ assert b200.seconds_per_step == pytest.approx(h200.seconds_per_step)
510
+ assert b200.train_seconds == pytest.approx(h200.train_seconds)
511
+ # ... and at its higher $/hr, never cheaper.
512
+ assert b200.total_usd > h200.total_usd
@@ -100,3 +100,22 @@ def test_pick_gpu_impossible_raises():
100
100
  def test_pick_gpu_auto_matches_default():
101
101
  assert pick_gpu(24, provider="auto") == pick_gpu(24)
102
102
  assert pick_gpu(12) == "RTX 4090"
103
+
104
+
105
+ def test_effective_train_tflops_caps_b200_at_h200_class():
106
+ # b200/sm100 training falls back to portable kernels, so realized training throughput is
107
+ # h200-class, not the 2.25 pflops peak. the cost model must not treat b200 as faster than h200.
108
+ from flash.cost.facts import effective_train_tflops
109
+
110
+ assert gpu_tflops("B200") == 2250.0 # raw peak unchanged (vram/serving still use gpu_tflops)
111
+ assert effective_train_tflops("B200") == 990.0
112
+ assert effective_train_tflops("B200") == effective_train_tflops("H200")
113
+
114
+
115
+ def test_effective_train_tflops_is_peak_for_uncapped_classes():
116
+ from flash.cost.facts import effective_train_tflops
117
+
118
+ for name in ("H100", "H200", "A100 SXM", "RTX 4090", "B200"):
119
+ # only b200 is capped; every other class keeps its peak.
120
+ expected = 990.0 if name == "B200" else gpu_tflops(name)
121
+ assert effective_train_tflops(name) == expected
@@ -64,12 +64,14 @@ def test_vast_gpu_for_offer_accepts_h100_pcie_alias():
64
64
  assert vast_gpu_for_offer("Tesla T4", 16 * 1024) is None
65
65
 
66
66
 
67
- def test_retired_gpu_resolves_but_is_not_active():
68
- from flash.providers.base import GPU_INFO, canonical_gpu, get_gpu_info
69
-
70
- assert canonical_gpu("RTX A6000") == "RTX A6000"
71
- assert get_gpu_info("RTX A6000").vram_gb == 48
72
- assert "RTX A6000" not in GPU_INFO
67
+ def test_removed_gpu_class_is_unmanaged():
68
+ # RTX A6000 was a retired class kept only to resolve legacy records; it is now fully removed, so
69
+ # it is not a managed class and resolves nowhere (no special retired-but-known status remains).
70
+ from flash.providers.base import KNOWN, UnsupportedGpuError, canonical_gpu
71
+
72
+ assert "RTX A6000" not in KNOWN
73
+ with pytest.raises(UnsupportedGpuError):
74
+ canonical_gpu("RTX A6000")
73
75
 
74
76
 
75
77
  def test_expanded_gpu_table():