freesolo-flash-dev 0.2.46__tar.gz → 0.2.47__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 (294) hide show
  1. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/PKG-INFO +2 -2
  2. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/commands.py +2 -3
  3. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/training_doc.py +5 -5
  4. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cost/analytical.py +6 -10
  5. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cost/facts.py +4 -7
  6. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cost/spec.py +0 -3
  7. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cost/types.py +2 -3
  8. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/recipe.py +2 -3
  9. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/opd.py +10 -11
  10. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/schema/__init__.py +2 -17
  11. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/spec.py +0 -3
  12. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/pyproject.toml +3 -3
  13. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_analytical.py +16 -25
  14. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_opd.py +7 -10
  15. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/uv.lock +1 -1
  16. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.dockerignore +0 -0
  17. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.env.example +0 -0
  18. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/auto-rebake.yml +0 -0
  19. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/bake-kernel-cache.yml +0 -0
  20. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/ci.yml +0 -0
  21. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/main-source-guard.yml +0 -0
  22. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/notify-tests-repo.yml +0 -0
  23. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/publish-dev.yml +0 -0
  24. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/publish-image.yml +0 -0
  25. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/publish.yml +0 -0
  26. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/version-parity.yml +0 -0
  27. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.github/workflows/worker-image.yml +0 -0
  28. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/.gitignore +0 -0
  29. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/Dockerfile +0 -0
  30. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/Dockerfile.worker +0 -0
  31. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/LICENSE +0 -0
  32. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/README.md +0 -0
  33. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/build/kernel_cache/.gitignore +0 -0
  34. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/build/kernel_cache/.keep +0 -0
  35. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/Dockerfile.kernelcache +0 -0
  36. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/Dockerfile.kernelcache.relayer +0 -0
  37. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/bake_kernel_cache.py +0 -0
  38. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/bake_pod_entry.py +0 -0
  39. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/kernel_fingerprint.py +0 -0
  40. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/docker/make_rp_handler.py +0 -0
  41. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/__init__.py +0 -0
  42. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/_channel.py +0 -0
  43. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/_fileio.py +0 -0
  44. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/_logging.py +0 -0
  45. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/_update_check.py +0 -0
  46. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/adapter_artifacts.py +0 -0
  47. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/catalog.py +0 -0
  48. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/__init__.py +0 -0
  49. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/__main__.py +0 -0
  50. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/_tty.py +0 -0
  51. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/envpush.py +0 -0
  52. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cli/render.py +0 -0
  53. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/client/__init__.py +0 -0
  54. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/client/config.py +0 -0
  55. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/client/http.py +0 -0
  56. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/client/runtime_secrets.py +0 -0
  57. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/client/specs.py +0 -0
  58. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/cost/__init__.py +0 -0
  59. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/__init__.py +0 -0
  60. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/accounting.py +0 -0
  61. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/chalk_kernels.py +0 -0
  62. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/multiturn_rollout.py +0 -0
  63. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/vram.py +0 -0
  64. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/__init__.py +0 -0
  65. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/__main__.py +0 -0
  66. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/_pkg.py +0 -0
  67. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/adapter.py +0 -0
  68. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/decoding.py +0 -0
  69. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/finalize.py +0 -0
  70. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/gpu_setup.py +0 -0
  71. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/grpo.py +0 -0
  72. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/heartbeat.py +0 -0
  73. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/hf.py +0 -0
  74. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/kernel_warmup.py +0 -0
  75. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/lora.py +0 -0
  76. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/opd_gkd.py +0 -0
  77. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/packing.py +0 -0
  78. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/__init__.py +0 -0
  79. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/attn.py +0 -0
  80. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/diagnostics.py +0 -0
  81. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/lifecycle.py +0 -0
  82. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/liger.py +0 -0
  83. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/loraplus.py +0 -0
  84. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/perf/memory.py +0 -0
  85. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/rl.py +0 -0
  86. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/sft.py +0 -0
  87. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/teacher.py +0 -0
  88. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/tokenizer_align.py +0 -0
  89. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/engine/worker/wandb_log.py +0 -0
  90. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/__init__.py +0 -0
  91. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/adapter.py +0 -0
  92. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/archive_policy.py +0 -0
  93. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/base.py +0 -0
  94. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/loader.py +0 -0
  95. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/pull.py +0 -0
  96. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/envs/registry.py +0 -0
  97. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/lora_rank.py +0 -0
  98. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/__init__.py +0 -0
  99. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_auth.py +0 -0
  100. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_hf_artifacts.py +0 -0
  101. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_hf_retry.py +0 -0
  102. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_http.py +0 -0
  103. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_instance.py +0 -0
  104. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_instance_bootstrap.py +0 -0
  105. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_instance_poll.py +0 -0
  106. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_instance_provider.py +0 -0
  107. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_poll.py +0 -0
  108. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/_worker.py +0 -0
  109. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/allocator.py +0 -0
  110. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/base.py +0 -0
  111. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/__init__.py +0 -0
  112. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/api.py +0 -0
  113. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/auth.py +0 -0
  114. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/gpus.py +0 -0
  115. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  116. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  117. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/preflight.py +0 -0
  118. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/lambdalabs/pricing.py +0 -0
  119. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/preflight.py +0 -0
  120. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/realized.py +0 -0
  121. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/__init__.py +0 -0
  122. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/api.py +0 -0
  123. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/auth.py +0 -0
  124. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/cost.py +0 -0
  125. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/gpus.py +0 -0
  126. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/jobs.py +0 -0
  127. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/keys.py +0 -0
  128. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/preflight.py +0 -0
  129. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/preload.py +0 -0
  130. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/pricing.py +0 -0
  131. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/slots.py +0 -0
  132. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/train/__init__.py +0 -0
  133. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/train/deps.py +0 -0
  134. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/runpod/train/endpoints.py +0 -0
  135. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/__init__.py +0 -0
  136. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/api.py +0 -0
  137. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/auth.py +0 -0
  138. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/jobs/__init__.py +0 -0
  139. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/jobs/builders.py +0 -0
  140. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/preflight.py +0 -0
  141. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/providers/vast/pricing.py +0 -0
  142. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/py.typed +0 -0
  143. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/runner/__init__.py +0 -0
  144. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/runner/checkpoints.py +0 -0
  145. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/runner/deploy.py +0 -0
  146. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/runner/lifecycle.py +0 -0
  147. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/schema/fields.py +0 -0
  148. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/serve/__init__.py +0 -0
  149. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/serve/deploy.py +0 -0
  150. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/serve/export.py +0 -0
  151. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/serve/pricing.py +0 -0
  152. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/__init__.py +0 -0
  153. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/__main__.py +0 -0
  154. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/_deps.py +0 -0
  155. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/_internal_client.py +0 -0
  156. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/_locks.py +0 -0
  157. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/_runtime.py +0 -0
  158. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/app.py +0 -0
  159. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/auth.py +0 -0
  160. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/billing.py +0 -0
  161. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/billing_retry.py +0 -0
  162. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/checkpoints.py +0 -0
  163. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/db.py +0 -0
  164. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/environment_registry.py +0 -0
  165. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/envs.py +0 -0
  166. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/reconcile.py +0 -0
  167. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/routes/__init__.py +0 -0
  168. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/routes/envs.py +0 -0
  169. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/routes/meta.py +0 -0
  170. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/routes/runs.py +0 -0
  171. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/routes/serving.py +0 -0
  172. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/flash/server/run_registry.py +0 -0
  173. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/infisical-entrypoint.sh +0 -0
  174. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/scripts/build_dev_dist.py +0 -0
  175. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/__init__.py +0 -0
  176. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/_helpers/__init__.py +0 -0
  177. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/_helpers/runner.py +0 -0
  178. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/_helpers/specs.py +0 -0
  179. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/_helpers/vast.py +0 -0
  180. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/conftest.py +0 -0
  181. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/fixtures/math_eval.jsonl +0 -0
  182. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/fixtures/math_train.jsonl +0 -0
  183. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/live/__init__.py +0 -0
  184. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/live/conftest.py +0 -0
  185. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/live/test_lambda_live.py +0 -0
  186. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/live/test_runpod_live.py +0 -0
  187. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/live/test_vast_live.py +0 -0
  188. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_agent_flash_cli_contract.py +0 -0
  189. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_algorithms.py +0 -0
  190. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_allocator.py +0 -0
  191. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_b200_rollout_opt.py +0 -0
  192. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_backend_jobspec_contract.py +0 -0
  193. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_billing_retry.py +0 -0
  194. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cancel_remote.py +0 -0
  195. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_catalog_consistency.py +0 -0
  196. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_chalk_kernels.py +0 -0
  197. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_charge_pricing.py +0 -0
  198. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_checkpoints.py +0 -0
  199. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_commands.py +0 -0
  200. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_errors.py +0 -0
  201. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_estimate.py +0 -0
  202. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_help.py +0 -0
  203. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_managed.py +0 -0
  204. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cli_render_theme.py +0 -0
  205. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_client.py +0 -0
  206. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_client_server_integration.py +0 -0
  207. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_config_overrides.py +0 -0
  208. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_equation.py +0 -0
  209. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_estimate.py +0 -0
  210. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_hardware.py +0 -0
  211. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_models.py +0 -0
  212. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_cost_rewards.py +0 -0
  213. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_dev_channel.py +0 -0
  214. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_disk_gb.py +0 -0
  215. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_endpoint_name.py +0 -0
  216. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_cache_evict.py +0 -0
  217. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_delete.py +0 -0
  218. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_download.py +0 -0
  219. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_download_api.py +0 -0
  220. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_no_install.py +0 -0
  221. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_publish.py +0 -0
  222. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_pull.py +0 -0
  223. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_pull_managed_control_plane.py +0 -0
  224. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_push.py +0 -0
  225. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_env_rate_limit_resolve.py +0 -0
  226. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_envs_coverage.py +0 -0
  227. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_export.py +0 -0
  228. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_flash_mvp.py +0 -0
  229. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_flash_worker.py +0 -0
  230. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_flashinfer_cache_dirs.py +0 -0
  231. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_github_urlopen_retry.py +0 -0
  232. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_gpus.py +0 -0
  233. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_grpo_mask_aware.py +0 -0
  234. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_grpo_params.py +0 -0
  235. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_grpo_sleep_gate.py +0 -0
  236. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_health_liveness.py +0 -0
  237. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_idle_endpoint_reaper.py +0 -0
  238. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_internal_client.py +0 -0
  239. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_jobs.py +0 -0
  240. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_kernel_cache.py +0 -0
  241. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_kernel_fingerprint.py +0 -0
  242. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_kv_util.py +0 -0
  243. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_lambda_runner.py +0 -0
  244. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_logging.py +0 -0
  245. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_login_perms.py +0 -0
  246. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_lora_rank_preflight.py +0 -0
  247. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_managed_hf_repo.py +0 -0
  248. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_metrics_schema_agent_contract.py +0 -0
  249. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_mig_guard.py +0 -0
  250. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_multiturn_rollout.py +0 -0
  251. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_oom_escalate_gpu.py +0 -0
  252. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_open_model_policy.py +0 -0
  253. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_orchestrator_flash.py +0 -0
  254. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_packing.py +0 -0
  255. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_poll_helpers.py +0 -0
  256. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_preflight.py +0 -0
  257. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_provider_routing.py +0 -0
  258. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_provider_teardown_robustness.py +0 -0
  259. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_providers_symmetry.py +0 -0
  260. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_realized_cost.py +0 -0
  261. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_resolve_params_b.py +0 -0
  262. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_resume_on_retry.py +0 -0
  263. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_runmgmt.py +0 -0
  264. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_runpod_api_delete.py +0 -0
  265. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_runpod_key_fingerprint.py +0 -0
  266. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_runpod_key_waterfall.py +0 -0
  267. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_runpod_slots.py +0 -0
  268. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_serve.py +0 -0
  269. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_server_api.py +0 -0
  270. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_server_billing.py +0 -0
  271. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_server_db.py +0 -0
  272. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_serving_contract.py +0 -0
  273. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_sft_example_selection.py +0 -0
  274. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_sft_gc_off.py +0 -0
  275. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_spec_and_validation.py +0 -0
  276. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_thinking_config.py +0 -0
  277. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_train_context_preflight.py +0 -0
  278. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_update_check.py +0 -0
  279. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_vast_api.py +0 -0
  280. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_vast_offers.py +0 -0
  281. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_vast_runner.py +0 -0
  282. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_verifiers.py +0 -0
  283. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_version.py +0 -0
  284. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  285. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_vl_warmstart_recombine.py +0 -0
  286. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_wandb_naming.py +0 -0
  287. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_warmstart_cross_repo.py +0 -0
  288. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_weight_cache.py +0 -0
  289. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_dryrun.py +0 -0
  290. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_hardexit.py +0 -0
  291. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_image.py +0 -0
  292. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_init_heartbeat.py +0 -0
  293. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_stack.py +0 -0
  294. {freesolo_flash_dev-0.2.46 → freesolo_flash_dev-0.2.47}/tests/test_worker_thinking.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.46
4
- Summary: Flash — managed LoRA post-training (SFT/GRPO) for Freesolo environments, driven by the `flash` CLI
3
+ Version: 0.2.47
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
7
7
  Author: Freesolo
@@ -504,16 +504,15 @@ def cmd_env_setup(args) -> int:
504
504
  opd.write_text(
505
505
  f"{opd_multiturn_note}"
506
506
  'model = "Qwen/Qwen3.5-4B"\n'
507
- 'algorithm = "opd" # on-policy distillation from a Fireworks GLM teacher\n\n'
507
+ 'algorithm = "opd" # on-policy distillation from the managed GLM 5.2 teacher\n\n'
508
508
  "# Environment: upload this project folder with\n"
509
509
  "# `flash env push --name my-env .`, then paste the returned id below.\n"
510
- "# The GLM teacher key is platform-managed — nothing to set up or export.\n"
510
+ "# The GLM 5.2 teacher and key are platform-managed — nothing to set up or export.\n"
511
511
  "[environment]\n"
512
512
  'id = ""\n\n'
513
513
  "[train]\n"
514
514
  "steps = 100 # opd is step-driven (like GRPO)\n"
515
515
  "lora_rank = 32\n"
516
- '# teacher_model = "accounts/fireworks/models/glm-5p2" # Fireworks GLM teacher (default)\n'
517
516
  "# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
518
517
  "# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
519
518
  )
@@ -377,15 +377,16 @@ SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
377
377
  ## On-policy distillation (`algorithm = "opd"`)
378
378
 
379
379
  Pick distillation when a much stronger **teacher** model can grade your student's work
380
- token-by-token. The student samples on-policy (like GRPO), a Fireworks-hosted GLM teacher scores
380
+ token-by-token. The student samples on-policy (like GRPO), the managed GLM 5.2 teacher scores
381
381
  each of *its own* completions, and a dense per-token loss teaches the student to match the teacher —
382
382
  far more sample-efficient than reward-based RL and with no reward to design. It is **step-driven**
383
383
  (`steps`) and produces a LoRA served exactly like SFT.
384
384
 
385
- - **No teacher key to set up.** The Fireworks key for the GLM teacher is platform-managed: the
385
+ - **No teacher key or model override to set up.** The GLM 5.2 teacher and its Fireworks key are platform-managed: the
386
386
  service supplies its own key to every opd run, so there is nothing to export or declare — an opd
387
- run submits like any other. (Bring-your-own teacher keys are not supported; a `FIREWORKS_API_KEY`
388
- in your shell is simply ignored.) The key is never stored in the spec or needed at serving time.
387
+ run submits like any other. Bring-your-own teacher models and keys are not supported; a
388
+ `FIREWORKS_API_KEY` in your shell is simply ignored. The key is never stored in the spec or
389
+ needed at serving time.
389
390
  - **The student (Qwen / MiniCPM / Kimi) and the teacher (GLM) have different tokenizers.** Flash
390
391
  bridges the vocabulary mismatch with **groupwise reverse-KL** (the collinear-ai *spider* / Tinker
391
392
  method): it aligns the two tokenizations by shared decoded-text spans and applies per-span reverse
@@ -413,7 +414,6 @@ id = "your-org/my-env"
413
414
  [train]
414
415
  steps = 100
415
416
  lora_rank = 32
416
- # teacher_model = "accounts/fireworks/models/glm-5p2" # Fireworks GLM teacher (default)
417
417
  # kl_penalty_coef = 1.0 # reverse-KL scale
418
418
  ```
419
419
 
@@ -44,11 +44,6 @@ MFU_DECODE = 0.12 # batched vLLM rollout (decode is memory-bandwidth-bound)
44
44
  # wall is ceil(completions / slots) waves x latency, not completions x latency.
45
45
  REWARD_CONCURRENCY = 16.0
46
46
 
47
- # Teacher scoring is CONCURRENT too: run_opd's primary path fans a step's completions across up to
48
- # TEACHER_CONCURRENCY Fireworks calls (mirror of opd.py _TEACHER_SCORE_MAX_WORKERS), so the teacher
49
- # wall is ceil(completions / slots) waves x latency, not completions x latency.
50
- TEACHER_CONCURRENCY = 8.0
51
-
52
47
  # Cold-start overhead (seconds): container boot + deps + model load (+ vLLM init for GRPO).
53
48
  #
54
49
  # Calibrated against a real fresh-worker run (0.8B SFT, RTX 3090 @ $0.239/hr) whose elapsed wall
@@ -112,11 +107,12 @@ def seconds_per_step(config: RunConfig, gpu: str) -> float:
112
107
  gen_s = (GRPO_GEN_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (peak * MFU_DECODE)
113
108
  update_s = (OPD_UPDATE_FLOPS_PER_TOKEN_PER_PARAM * params * seq_tokens) / (peak * MFU_TRAIN)
114
109
  teacher_lat = teacher_seconds_per_completion()
115
- # run_opd's primary path scores a step's completions CONCURRENTLY over Fireworks, bounded by
116
- # TEACHER_CONCURRENCY workers (opd.py _TEACHER_SCORE_MAX_WORKERS), so the teacher wall is
117
- # ceil(completions / slots) waves x latency NOT the full serial sum (that describes only the
118
- # CPU-test fallback that can't batch-generate). ceil keeps a partial last wave at one latency.
119
- teacher_s = math.ceil(completions / TEACHER_CONCURRENCY) * teacher_lat
110
+ # run_opd's primary path scores a step's completions CONCURRENTLY over Fireworks with a fan-out
111
+ # cap of the step's OWN completion count (prompts_per_step * group_size, opd.py Phase 2), so
112
+ # every completion in a step is scored in ONE parallel wave the teacher wall is a single
113
+ # latency, NOT the full serial sum (that describes only the CPU-test fallback that can't
114
+ # batch-generate). The teacher endpoint's rate limit is the real ceiling on this fan-out.
115
+ teacher_s = teacher_lat if completions else 0.0
120
116
  return gen_s + teacher_s + update_s
121
117
 
122
118
  if not n.is_grpo:
@@ -166,10 +166,9 @@ def reward_seconds_per_completion(override: float | None = None) -> float:
166
166
  # NO FIREWORKS_API_KEY. Source: https://fireworks.ai/models/fireworks/glm-5p2 lists
167
167
  # $1.40 / $0.14 / $4.40 (input / cached input / output) per 1M. opd echo-scores completions
168
168
  # (max_tokens=0), so only INPUT tokens are billed (the teacher never generates) — but the table keeps
169
- # both so a mispriced entry is obvious. glm-5p1 shares the GLM-5 serverless rate.
169
+ # both so a mispriced entry is obvious.
170
170
  TEACHER_USD_PER_1M: dict[str, tuple[float, float]] = {
171
171
  "accounts/fireworks/models/glm-5p2": (1.40, 4.40),
172
- "accounts/fireworks/models/glm-5p1": (1.40, 4.40),
173
172
  }
174
173
  # Fireworks echo-scoring round-trip per completion (wall time, concurrency-bound like reward grading).
175
174
  AVG_TEACHER_SECONDS_PER_COMPLETION = 2.0
@@ -178,11 +177,9 @@ AVG_TEACHER_SECONDS_PER_COMPLETION = 2.0
178
177
  def teacher_price_per_1m(teacher_model: str) -> tuple[float, float]:
179
178
  """(input, output) $/1M tokens for a teacher model.
180
179
 
181
- The empty-string sentinel (an opd run that OMITS [train] teacher_model) resolves to the recipe's
182
- default teacher (``RECIPE.opd.teacher_model``) the SINGLE source of "who is the default
183
- teacher" — so the quote uses that model's real rate with no forked default-price constant. An
184
- unknown non-default teacher falls back to the default teacher's rate (spec-parse already rejects
185
- an unpriced teacher_model, so this is only a defensive backstop)."""
180
+ OPD uses the recipe's fixed GLM 5.2 teacher. ``teacher_model`` is accepted only for internal
181
+ diagnostics/tests; user configs cannot override it. Unknown values fall back defensively to
182
+ the fixed teacher rate."""
186
183
  from flash.engine.recipe import RECIPE
187
184
 
188
185
  default = TEACHER_USD_PER_1M[RECIPE.opd.teacher_model]
@@ -110,9 +110,6 @@ def runconfig_from_spec(spec) -> RunConfig:
110
110
  provider="auto",
111
111
  max_wall_seconds=g.max_wall_seconds,
112
112
  environment=spec.environment.id or None,
113
- # "" is the unset sentinel (RunConfig.teacher_model); facts.teacher_price_per_1m resolves it
114
- # to the recipe default. Non-opd carries no teacher.
115
- teacher_model=t.teacher_model if spec.algorithm == "opd" else "",
116
113
  )
117
114
 
118
115
 
@@ -27,9 +27,8 @@ class RunConfig:
27
27
  thinking: bool = False
28
28
  # GRPO only: seconds to score one completion. None -> the single average grader latency.
29
29
  reward_seconds_per_completion: float | None = None
30
- # OPD only: the Fireworks teacher id, so the teacher-token quote uses the right per-model rate.
31
- # "" (the unset sentinel, matching TrainSpec.teacher_model) resolves to the recipe's default
32
- # teacher (RECIPE.opd.teacher_model) at the pricing boundary (facts.teacher_price_per_1m).
30
+ # OPD only: internal Fireworks teacher id for teacher-token diagnostics. User configs cannot
31
+ # override this; an empty value resolves to the recipe's fixed GLM 5.2 teacher.
33
32
  teacher_model: str = ""
34
33
 
35
34
  max_wall_seconds: int | None = None # wall cap (spec gpu.max_wall_seconds); None = 24h
@@ -50,9 +50,8 @@ class OPDConfig:
50
50
  """On-policy distillation: student samples, a remote teacher scores its tokens, and a groupwise
51
51
  reverse-KL loss (the collinear-ai spider / Tinker cross-tokenizer method) trains the student."""
52
52
 
53
- # Fireworks-hosted teacher reached over the OpenAI-compatible API; overridable via
54
- # [train].teacher_model. GLM-5.2 is a strong reasoning model whose per-token logprobs
55
- # supervise the (much smaller) student.
53
+ # Fixed Fireworks-hosted GLM 5.2 teacher reached over the OpenAI-compatible API. This is
54
+ # platform-managed and intentionally not configurable from [train].
56
55
  teacher_model: str = "accounts/fireworks/models/glm-5p2"
57
56
  teacher_base_url: str = "https://api.fireworks.ai/inference/v1"
58
57
  # OPD is step-driven like GRPO (on-policy sampling), not epoch-driven like SFT.
@@ -54,11 +54,6 @@ from flash.engine.worker.perf import (
54
54
  from flash.engine.worker.teacher import TeacherError
55
55
  from flash.engine.worker.tokenizer_align import groupwise_alignment, groupwise_coverage
56
56
 
57
- # Upper bound on teacher-scoring HTTP calls fired concurrently within one optimizer step. Generation
58
- # (GPU) and the gkd loss forward/backward stay serial on the main thread; only the (dominant) teacher
59
- # Fireworks round-trips for a step's samples are overlapped, so this caps the fan-out into the API.
60
- _TEACHER_SCORE_MAX_WORKERS = 8 # bound concurrent GLM/Fireworks scoring calls per step
61
-
62
57
 
63
58
  @dataclass(frozen=True)
64
59
  class OpdKnobs:
@@ -108,7 +103,7 @@ def _resolve_opd_knobs() -> OpdKnobs:
108
103
  "publishes an untrained adapter. Omit the field to use the default, or set a positive value."
109
104
  )
110
105
  return OpdKnobs(
111
- teacher_model=opt("teacher_model", "") or d.teacher_model,
106
+ teacher_model=d.teacher_model,
112
107
  teacher_base_url=d.teacher_base_url,
113
108
  steps=int(opt("steps", 0) or d.num_steps),
114
109
  learning_rate=float(opt("learning_rate", 0) or d.learning_rate),
@@ -668,16 +663,20 @@ def run_opd():
668
663
  # Feed the stall clock during the (serial, up-to-~900s-each) generations;
669
664
  # nseq is still 0 here since no loss has landed yet this step.
670
665
  _opd_progress(opt_steps, nseq)
671
- # Phase 2 (concurrent, NETWORK only): fire the teacher scoring calls for every scorable
672
- # rollout at once, bounded by _TEACHER_SCORE_MAX_WORKERS. _score_one touches only the
673
- # stateless teacher HTTP client + Python strings (no torch/model/shared mutable state),
674
- # so the round-trips overlap safely; truncated/empty/U+FFFD rollouts are never scored.
666
+ # Phase 2 (concurrent, NETWORK only): fire the teacher scoring calls for EVERY scorable
667
+ # rollout at once the fan-out cap is the step's own completion count
668
+ # (prompts_per_step * group_size), so every sample in a step is scored in parallel (one
669
+ # round-trip of wall time) and the concurrency self-scales when group_size is raised
670
+ # (no queueing 8-at-a-time). The teacher endpoint's own rate limit is the real ceiling;
671
+ # 429s retry inside _score_one. _score_one touches only the stateless teacher HTTP client
672
+ # + Python strings (no torch/model/shared mutable state), so the round-trips overlap
673
+ # safely; truncated/empty/U+FFFD rollouts are never scored.
675
674
  scorable = [i for i, p in enumerate(pending) if not (p.gen.skip or p.gen.truncated)]
676
675
  _opd_progress(opt_steps, nseq) # refresh entering the (bounded) network phase
677
676
  if scorable:
678
677
  permanent_err = None
679
678
  with ThreadPoolExecutor(
680
- max_workers=min(len(scorable), _TEACHER_SCORE_MAX_WORKERS)
679
+ max_workers=min(len(scorable), knobs.prompts_per_step * knobs.group_size)
681
680
  ) as pool:
682
681
  fut_to_idx = {
683
682
  pool.submit(
@@ -21,7 +21,6 @@ from flash.schema.fields import (
21
21
  _train_float,
22
22
  _train_int,
23
23
  _train_stops,
24
- _train_str,
25
24
  _wandb_spec,
26
25
  _worker_env,
27
26
  )
@@ -210,8 +209,6 @@ _TRAIN_KEYS = frozenset(
210
209
  "stop_sequences",
211
210
  "max_steps",
212
211
  "max_examples",
213
- # on-policy distillation (algorithm="opd")
214
- "teacher_model",
215
212
  }
216
213
  )
217
214
 
@@ -222,7 +219,7 @@ def spec_from_dict(raw: dict[str, Any], run_id: str | None = None) -> JobSpec:
222
219
  hint = ""
223
220
  if {"grpo", "sft", "opd"} & set(unknown):
224
221
  hint = (
225
- " — GRPO/SFT/opd knobs (group_size, batch_size, max_tokens, teacher_model, …) "
222
+ " — GRPO/SFT/opd knobs (group_size, batch_size, max_tokens, …) "
226
223
  "belong under [train], not a [grpo]/[sft]/[opd] table"
227
224
  )
228
225
  raise ConfigError(
@@ -358,7 +355,6 @@ def spec_from_dict(raw: dict[str, Any], run_id: str | None = None) -> JobSpec:
358
355
  # minimum=0: explicit 0 means "no cap" per TrainSpec contract
359
356
  max_steps=_train_int(train_raw, "max_steps", minimum=0),
360
357
  max_examples=_train_int(train_raw, "max_examples", minimum=0),
361
- teacher_model=_train_str(train_raw, "teacher_model"),
362
358
  ),
363
359
  gpu=GpuSpec(type=gpu_type),
364
360
  run_id=run_id or "local", # server-assigned at create_run; never user-set
@@ -389,24 +385,13 @@ def _validate_grpo(spec: JobSpec) -> None:
389
385
 
390
386
 
391
387
  def _validate_opd(spec: JobSpec) -> None:
392
- """OPD contract: step-driven (positive steps), a priced teacher, and a usable prompt budget.
388
+ """OPD contract: step-driven (positive steps) with a usable prompt budget.
393
389
 
394
390
  The teacher key (FIREWORKS_API_KEY) is a platform-owned credential the control plane injects into
395
391
  the worker env (build_worker_env), like HF_TOKEN — never a user-declared secret."""
396
392
  if spec.train.steps is not None and spec.train.steps <= 0:
397
393
  # OPD is step-driven (on-policy sampling), like GRPO — not epoch-driven.
398
394
  raise ConfigError("train.steps must be positive for opd")
399
- if spec.train.teacher_model:
400
- # Reject an unpriced teacher override at parse time: falling back to the default rate would
401
- # make both the submit-time quote and the final charge wrong for a differently-priced model.
402
- from flash.cost.facts import TEACHER_USD_PER_1M
403
-
404
- if spec.train.teacher_model not in TEACHER_USD_PER_1M:
405
- raise ConfigError(
406
- f"[train] teacher_model {spec.train.teacher_model!r} has no pricing entry, so its "
407
- f"cost quote and charge would silently use the default rate. Use a priced teacher "
408
- f"({', '.join(sorted(TEACHER_USD_PER_1M))}) or add an entry in flash/cost/facts.py."
409
- )
410
395
  if spec.train.max_length:
411
396
  # Mirror run_opd's prompt-budget guard at PARSE time: a max_length that leaves no room for
412
397
  # any prompt after the completion budget is rejected here, BEFORE a paid worker is
@@ -124,8 +124,6 @@ class TrainSpec:
124
124
  advantage_clip: float | None = None
125
125
  thinking_length_penalty_coef: float | None = None
126
126
  stop_sequences: tuple[str, ...] = ()
127
- # On-policy distillation (algorithm="opd"). None/"" => worker resolves the recipe default.
128
- teacher_model: str = ""
129
127
 
130
128
 
131
129
  @dataclass(frozen=True)
@@ -212,7 +210,6 @@ class JobSpec:
212
210
  advantage_clip=_opt_float(train.get("advantage_clip")),
213
211
  thinking_length_penalty_coef=_opt_float(train.get("thinking_length_penalty_coef")),
214
212
  stop_sequences=_str_tuple(train.get("stop_sequences")),
215
- teacher_model=str(train.get("teacher_model") or ""),
216
213
  ),
217
214
  gpu=GpuSpec(
218
215
  type=gpu.get("type", DEFAULT_GPU),
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "0.2.46"
4
- description = "Flash — managed LoRA post-training (SFT/GRPO) for Freesolo environments, driven by the `flash` CLI"
3
+ version = "0.2.47"
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).
7
7
  requires-python = ">=3.11,<3.13"
@@ -106,7 +106,7 @@ artifacts = ["flash/py.typed"]
106
106
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
107
107
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
108
108
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
109
- version = "0.2.46"
109
+ version = "0.2.47"
110
110
 
111
111
  [tool.pytest.ini_options]
112
112
  pythonpath = ["."]
@@ -272,33 +272,24 @@ def test_nonpositive_run_knobs_rejected(knob, bad):
272
272
  RunConfig(MID, "grpo", 100, **{knob: bad})
273
273
 
274
274
 
275
- def test_opd_teacher_scoring_is_billed_in_concurrency_waves_not_serially():
276
- # Teacher scoring runs CONCURRENTLY in run_opd (up to TEACHER_CONCURRENCY Fireworks calls per
277
- # step), so a step's teacher wall is ceil(completions / slots) waves x latency, NOT the full
278
- # serial sum. Isolate the teacher term by holding seq_tokens (hence gen_s/update_s) constant while
279
- # doubling completions across a wave boundary: TEACHER_CONCURRENCY completions = 1 wave, 2x = 2
280
- # waves, so the per-step delta is exactly ONE teacher latency -- not the ~8x a serial
281
- # (completions x latency) model would add.
282
- from flash.cost.analytical import TEACHER_CONCURRENCY, seconds_per_step
275
+ def test_opd_teacher_scoring_is_one_parallel_wave():
276
+ # Teacher scoring fans a step's completions across prompts_per_step*group_size concurrent Fireworks
277
+ # calls (opd.py Phase 2), so EVERY completion in a step is scored in one parallel wave -- the
278
+ # teacher wall is a single latency regardless of completion count, NOT the serial (completions x
279
+ # latency) sum. Hold seq_tokens (hence gen_s/update_s) constant while doubling the completion count;
280
+ # since scoring is now fully parallel the teacher term is unchanged, so the per-step delta is 0.
281
+ from flash.cost.analytical import seconds_per_step
283
282
  from flash.cost.facts import teacher_seconds_per_completion
284
283
 
285
284
  gpu = "RTX 5090"
286
285
  teacher_lat = teacher_seconds_per_completion()
287
- assert teacher_lat > 0 # else the assertion below is vacuous
288
- slots = int(TEACHER_CONCURRENCY)
289
- # completions x seq_len is identical (slots*2048 == 2*slots*1024), so only the wave count differs.
290
- one_wave = RunConfig(MID, "opd", 10, batch_size=slots, group_size=1, seq_len=2048)
291
- two_waves = RunConfig(MID, "opd", 10, batch_size=slots * 2, group_size=1, seq_len=1024)
292
- delta = seconds_per_step(two_waves, gpu) - seconds_per_step(one_wave, gpu)
293
- assert delta == pytest.approx(teacher_lat), (
294
- "crossing one concurrency wave must add exactly one teacher latency, not one per completion"
286
+ assert teacher_lat > 0 # else the isolation below is vacuous
287
+ # completions x seq_len is identical (8*2048 == 16*1024), so gen_s/update_s match and only the
288
+ # completion count (hence any wave scaling) differs.
289
+ few = RunConfig(MID, "opd", 10, batch_size=8, group_size=1, seq_len=2048)
290
+ many = RunConfig(MID, "opd", 10, batch_size=16, group_size=1, seq_len=1024)
291
+ delta = seconds_per_step(many, gpu) - seconds_per_step(few, gpu)
292
+ assert delta == pytest.approx(0.0, abs=1e-6), (
293
+ "full-parallel teacher scoring bills one wave; doubling completions at equal total tokens "
294
+ "must not add teacher latency"
295
295
  )
296
-
297
-
298
- def test_opd_teacher_concurrency_matches_worker_pool():
299
- # The cost model's TEACHER_CONCURRENCY must track the worker's actual pool size, or quotes drift
300
- # from real runs. Guard the two constants against silent divergence.
301
- from flash.cost.analytical import TEACHER_CONCURRENCY
302
- from flash.engine.worker.opd import _TEACHER_SCORE_MAX_WORKERS
303
-
304
- assert TEACHER_CONCURRENCY == _TEACHER_SCORE_MAX_WORKERS
@@ -416,24 +416,23 @@ def test_opd_vram_sizing_uses_completion_budget_not_sft_default():
416
416
  assert opd_rollout_seq_len(4096, 8192, False) == 4096 # explicit max_length pins the sequence
417
417
 
418
418
 
419
- def test_opd_rejects_unpriced_teacher_model_but_accepts_priced():
419
+ def test_opd_rejects_teacher_model_override():
420
420
  from flash.schema import ConfigError, spec_from_dict
421
421
 
422
- def _spec(teacher):
423
- return spec_from_dict(
422
+ with pytest.raises(ConfigError, match="teacher_model"):
423
+ spec_from_dict(
424
424
  {
425
425
  "model": "Qwen/Qwen3.5-4B",
426
426
  "algorithm": "opd",
427
427
  "environment": {"id": "github:owner/repo@main:env/environment.py"},
428
- "train": {"steps": 5, "hf_repo": "owner/runs", "teacher_model": teacher},
428
+ "train": {
429
+ "steps": 5,
430
+ "teacher_model": "accounts/fireworks/models/glm-5p2",
431
+ },
429
432
  },
430
433
  run_id="x",
431
434
  )
432
435
 
433
- _spec("accounts/fireworks/models/glm-5p2") # priced -> ok
434
- with pytest.raises(ConfigError):
435
- _spec("accounts/fireworks/models/mystery-9000") # unpriced override -> reject at parse
436
-
437
436
 
438
437
  def test_opd_rejects_prompt_budget_at_parse_time_before_provisioning():
439
438
  """max_length <= max_tokens leaves no prompt budget; opd must reject it at spec-parse time
@@ -2607,7 +2606,6 @@ def test_opd_spec_json_round_trip():
2607
2606
  "environment": {"id": "github:owner/repo@main:env/environment.py"},
2608
2607
  "train": {
2609
2608
  "steps": 25,
2610
- "teacher_model": "accounts/fireworks/models/glm-5p1",
2611
2609
  "hf_repo": "owner/runs",
2612
2610
  },
2613
2611
  },
@@ -2616,7 +2614,6 @@ def test_opd_spec_json_round_trip():
2616
2614
  restored = JobSpec.from_json(spec.to_json())
2617
2615
  assert restored == spec
2618
2616
  assert restored.phase == "opd"
2619
- assert restored.train.teacher_model == "accounts/fireworks/models/glm-5p1"
2620
2617
  # The teacher key is platform-managed (control-plane-injected into the worker env, like
2621
2618
  # HF_TOKEN) — NOT a user secret, so it is never added to environment.secrets.
2622
2619
  assert "FIREWORKS_API_KEY" not in restored.environment.secrets
@@ -1024,7 +1024,7 @@ wheels = [
1024
1024
 
1025
1025
  [[package]]
1026
1026
  name = "freesolo-flash"
1027
- version = "0.2.41"
1027
+ version = "0.2.47"
1028
1028
  source = { editable = "." }
1029
1029
 
1030
1030
  [package.optional-dependencies]