freesolo-flash-dev 1.0.6__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.
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cost/analytical.py +3 -3
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cost/facts.py +25 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/vram.py +11 -27
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd.py +118 -11
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_allocator.py +38 -7
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_estimate.py +30 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_hardware.py +19 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_opd.py +252 -60
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.env.example +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/README.md +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/catalog.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cli/training_doc.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo_multimodal.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/grpo_perturn_trainer.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/opd_vllm.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/runner/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/schema/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_async_uploads.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_grpo_perturn.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_jobs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_image_rollout.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_per_turn_reward.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_multiturn_rollout_request_policy.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_opd_vllm.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_sft_activation_shaping.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_sft_tokenize_cache.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_single_turn_multimodal_grpo.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.6 → freesolo_flash_dev-1.0.7}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.6 → 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.
|
|
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
|
|
@@ -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 =
|
|
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 =
|
|
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
|
|
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
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
#
|
|
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
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
"
|
|
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, "
|
|
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
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
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
|
-
|
|
2303
|
-
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "freesolo-flash-dev"
|
|
3
|
-
version = "1.0.
|
|
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.
|
|
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
|
-
"
|
|
664
|
+
"A100 PCIe",
|
|
633
665
|
),
|
|
634
666
|
(
|
|
635
|
-
#
|
|
636
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
752
|
-
#
|
|
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)
|
|
@@ -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
|