freesolo-flash-dev 1.0.76__tar.gz → 1.0.78__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.76 → freesolo_flash_dev-1.0.78}/Dockerfile.worker +1 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/catalog.py +1 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/TRAINING.md +5 -5
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/__init__.py +1 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/envpush.py +145 -76
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cost/analytical.py +113 -67
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cost/spec.py +3 -3
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/vram.py +3 -3
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/backend_common.py +44 -17
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/allocator.py +44 -11
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/base.py +8 -4
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/__init__.py +29 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/gpus.py +28 -3
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/pricing.py +2 -2
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/runner/__init__.py +15 -9
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/runner/lifecycle.py +121 -40
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/schema/__init__.py +7 -2
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_allocator.py +2 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_backend_common.py +95 -7
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_gpus_flag.py +17 -7
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_estimate.py +123 -119
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_disk_gb.py +6 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_push.py +127 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_jobs.py +83 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_lambda_runner.py +2 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_multi_gpu_gate.py +241 -56
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_opd.py +13 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_provider_routing.py +13 -6
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_resume_on_retry.py +1 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_runmgmt.py +12 -6
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.env.example +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/dependabot.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/CODE_OF_CONDUCT.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/CONTRIBUTING.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/NOTICE +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/README.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/SECURITY.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/SELF_HOSTING.md +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/env_eval.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/env_test.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/traces.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cli/training_doc.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/reward_profile.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/grpo_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/grpo_plugin.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/multiturn_glue.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd_plugin.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd_structured.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/opd_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/rl_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/sft_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/evaluations.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/opd_validation.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/providers/weight_cache_preload.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/projects.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/flash/thinking.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/scripts/scrub_history.sh +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_async_uploads.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_credential_shadowing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_traces.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cli_wandb_warning.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_evaluations.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_ref_pinning.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_env_test.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_gpu_type_single_field.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_grpo_resident_sizing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_managed_config_fields.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_opd_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_projects.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_reward_profile.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_rl_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serve_reasoning_content.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_server_standalone.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_sft_chunked_nll.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_sft_train.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_step_metrics.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_vram_architecture.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_wandb_link.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_warmstart_error_attribution.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.76 → freesolo_flash_dev-1.0.78}/tests/test_worker_thinking.py +0 -0
|
@@ -268,7 +268,7 @@ print('worker stack OK | torch', torch.__version__, '| cudnn', cudnn, '| triton'
|
|
|
268
268
|
# flash's, so a job that sets WANDB_API_KEY loses its curves and run link unless wandb is importable
|
|
269
269
|
# in here; resolve_verl_loggers() degrades to console when it is not, which is the right behaviour
|
|
270
270
|
# for a foreign FLASH_VERL_PYTHON but silently drops W&B on the default image (codex[bot]).
|
|
271
|
-
ARG VERL_SPEC=verl[vllm]@git+https://github.com/freesolo-co/verl@
|
|
271
|
+
ARG VERL_SPEC=verl[vllm]@git+https://github.com/freesolo-co/verl@1bea7d6825bbb9d2164e86e379b3680e7c53bb8a
|
|
272
272
|
ENV FLASH_VERL_PYTHON=/opt/verl-venv/bin/python
|
|
273
273
|
RUN printf '%s\n' "numpy==2.2.6" "xgrammar==0.1.25" "vllm==0.19.1" > /tmp/verl-overrides.txt \
|
|
274
274
|
&& uv venv --seed /opt/verl-venv \
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.78
|
|
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
|
|
@@ -1286,13 +1286,13 @@ gpu.count=4`, and the same 1..8 bound rejects a bad value.
|
|
|
1286
1286
|
most cards it may use, and it stops at the first count that fits: a class that fits the run on one
|
|
1287
1287
|
card is allocated as one card, and a class that needs sharding gets the _smallest_ fitting
|
|
1288
1288
|
combination, not the count you asked for. `--gpus 4` on a 9B **SFT** run pinned to a 24 GB class
|
|
1289
|
-
allocates 2.
|
|
1290
|
-
|
|
1289
|
+
allocates 2. Eight cards is the public and allocatable maximum; ceilings between powers of two round
|
|
1290
|
+
down to the next rentable count.
|
|
1291
1291
|
|
|
1292
1292
|
That example is SFT-specific on purpose. The fit test is per algorithm, and the same 9B pinned to
|
|
1293
|
-
the same 24 GB class
|
|
1294
|
-
|
|
1295
|
-
|
|
1293
|
+
the same 24 GB class may need a wider combination under GRPO or OPD because rollout memory raises
|
|
1294
|
+
the whole-run floor. Raising `--gpus` still cannot rescue a pin the algorithm does not fit on even
|
|
1295
|
+
as an 8-card combination.
|
|
1296
1296
|
|
|
1297
1297
|
**There is no exact-count mechanism.** Pinning a small `[gpu] type` raises the floor above one card
|
|
1298
1298
|
but still does not pin n — it only moves which combination is smallest. To see what a submit
|
|
@@ -577,7 +577,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
577
577
|
help=(
|
|
578
578
|
"most cards to run the job on; sets [gpu] count (1-8). a ceiling, not an exact "
|
|
579
579
|
"count: allocation still picks one card when one fits the run alone, and only "
|
|
580
|
-
"rentable counts (1, 2, 4) are ever provisioned"
|
|
580
|
+
"rentable counts (1, 2, 4, 8) are ever provisioned"
|
|
581
581
|
),
|
|
582
582
|
)
|
|
583
583
|
train.add_argument("--dry-run", action="store_true")
|
|
@@ -605,8 +605,15 @@ def _dynamic_import_name(node, callees: frozenset[str]) -> str | None:
|
|
|
605
605
|
return first.value.split(".", 1)[0] or None
|
|
606
606
|
|
|
607
607
|
|
|
608
|
-
def _imported_module_names(tree) -> set[str]:
|
|
609
|
-
"""Top-level names a parsed module imports
|
|
608
|
+
def _imported_module_names(tree, *, relative_names_are_siblings: bool = True) -> set[str]:
|
|
609
|
+
"""Top-level sibling names a parsed module imports, by statement or literal call.
|
|
610
|
+
|
|
611
|
+
Every name returned is resolved against the env root by the caller, so a relative import
|
|
612
|
+
only belongs here when the parsed file sits AT that root. Pass
|
|
613
|
+
`relative_names_are_siblings=False` for a file nested deeper: `from . import config` inside
|
|
614
|
+
`pkg/__init__.py` names `pkg/config.py`, which the package walk already ships, and reading it
|
|
615
|
+
as a root-level name published an unrelated top-level `config.py` instead (codex[bot]).
|
|
616
|
+
"""
|
|
610
617
|
import ast
|
|
611
618
|
|
|
612
619
|
names: set[str] = set()
|
|
@@ -614,8 +621,22 @@ def _imported_module_names(tree) -> set[str]:
|
|
|
614
621
|
for node in ast.walk(tree):
|
|
615
622
|
if isinstance(node, ast.Import):
|
|
616
623
|
names.update(alias.name.split(".", 1)[0] for alias in node.names)
|
|
617
|
-
elif isinstance(node, ast.ImportFrom)
|
|
618
|
-
|
|
624
|
+
elif isinstance(node, ast.ImportFrom):
|
|
625
|
+
if node.level == 0:
|
|
626
|
+
if node.module:
|
|
627
|
+
names.add(node.module.split(".", 1)[0])
|
|
628
|
+
elif node.level == 1 and relative_names_are_siblings:
|
|
629
|
+
# `from . import config` and `from .utils import load` name siblings of this file
|
|
630
|
+
# just as the absolute spellings do. an entrypoint written to work both as a
|
|
631
|
+
# package member and as a loose module puts the relative form first and falls back
|
|
632
|
+
# to the absolute one, so reading only absolute imports left the helper unpackaged
|
|
633
|
+
# whenever the relative spelling came first -- and the fallback then had no file to
|
|
634
|
+
# import. a bare `from . import x` carries the name in `names`, not in `module`.
|
|
635
|
+
if node.module:
|
|
636
|
+
names.add(node.module.split(".", 1)[0])
|
|
637
|
+
else:
|
|
638
|
+
names.update(alias.name.split(".", 1)[0] for alias in node.names)
|
|
639
|
+
# level >= 2 climbs above the package this push carries, so it can name no sibling.
|
|
619
640
|
elif isinstance(node, ast.Call):
|
|
620
641
|
dynamic = _dynamic_import_name(node, callees)
|
|
621
642
|
if dynamic:
|
|
@@ -623,24 +644,109 @@ def _imported_module_names(tree) -> set[str]:
|
|
|
623
644
|
return names
|
|
624
645
|
|
|
625
646
|
|
|
626
|
-
def _helper_imports(helper: Path) -> set[str]:
|
|
647
|
+
def _helper_imports(helper: Path, *, env_root: Path) -> set[str]:
|
|
627
648
|
"""Top-level names a packaged helper imports, for following its own dependencies.
|
|
628
649
|
|
|
629
650
|
A helper that fails to parse is still shipped verbatim: refusing the push over a file the
|
|
630
651
|
sidecar may never execute would be a harsher failure than the ModuleNotFoundError this
|
|
631
|
-
lookahead exists to avoid, and the sidecar's own syntax is validated separately.
|
|
652
|
+
lookahead exists to avoid, and the sidecar's own syntax is validated separately.
|
|
653
|
+
|
|
654
|
+
Only a helper sitting directly at `env_root` has root-level siblings, so a relative import
|
|
655
|
+
from deeper in a packaged subdirectory is dropped rather than resolved against the wrong
|
|
656
|
+
directory -- the package walk that reached it already ships its true siblings."""
|
|
632
657
|
import ast
|
|
633
658
|
|
|
634
659
|
if helper.suffix != ".py":
|
|
635
660
|
return set()
|
|
636
661
|
try:
|
|
637
662
|
return _imported_module_names(
|
|
638
|
-
ast.parse(helper.read_text(encoding="utf-8"), filename=str(helper))
|
|
663
|
+
ast.parse(helper.read_text(encoding="utf-8"), filename=str(helper)),
|
|
664
|
+
relative_names_are_siblings=helper.parent == env_root,
|
|
639
665
|
)
|
|
640
666
|
except (OSError, SyntaxError, UnicodeDecodeError):
|
|
641
667
|
return set()
|
|
642
668
|
|
|
643
669
|
|
|
670
|
+
def _iter_import_closure(
|
|
671
|
+
seed_modules: set[str], *, env_root: Path, entrypoint: Path, yielded: set[Path]
|
|
672
|
+
) -> Iterator[tuple[Path, Path]]:
|
|
673
|
+
"""Yield the sibling helpers reachable from `seed_modules`, following their imports too.
|
|
674
|
+
|
|
675
|
+
A helper that imports another sibling needs that sibling too: shipping `scorer.py` without
|
|
676
|
+
the `thresholds` it imports published an environment that raised ModuleNotFoundError on its
|
|
677
|
+
first case, having passed every local check because the source directory was importable
|
|
678
|
+
(cursor[bot]). The queue grows only through siblings actually reached, so it stays a closure
|
|
679
|
+
over files this push already carries rather than a general dependency resolver -- an
|
|
680
|
+
unimported neighbour stays local.
|
|
681
|
+
|
|
682
|
+
`yielded` is shared with the caller's other walks so a helper that IS `dataset/` (or lives
|
|
683
|
+
under it) is not emitted twice; `_check_env_push_limits` would otherwise charge those bytes
|
|
684
|
+
and members twice and reject a tree that is actually under the limit (codex[bot]).
|
|
685
|
+
"""
|
|
686
|
+
import os
|
|
687
|
+
|
|
688
|
+
pending = sorted(seed_modules)
|
|
689
|
+
visited: set[str] = set()
|
|
690
|
+
while pending:
|
|
691
|
+
module_name = pending.pop()
|
|
692
|
+
if module_name in visited:
|
|
693
|
+
continue
|
|
694
|
+
visited.add(module_name)
|
|
695
|
+
# `from graders.rules import score` resolves to a sibling PACKAGE, not graders.py.
|
|
696
|
+
# matching only the module spelling published an environment whose sidecar raises
|
|
697
|
+
# ModuleNotFoundError on its first case, having passed every local check.
|
|
698
|
+
#
|
|
699
|
+
# resolution follows python's own order, which is NOT simply "directories first".
|
|
700
|
+
# a directory holding __init__.py is a regular package and outranks a same-named
|
|
701
|
+
# module, so shipping the .py and skipping the package published the file the
|
|
702
|
+
# sidecar never imports while dropping the one it does (cursor[bot]). but a PEP 420
|
|
703
|
+
# namespace directory (no __init__.py) is only a fallback portion: `graders.py`
|
|
704
|
+
# wins over a bare `graders/`. requiring the marker unconditionally sent a
|
|
705
|
+
# namespace helper to the .py fallback, which did not exist either, and published
|
|
706
|
+
# an archive missing the helper entirely (codex[bot]). verified by probe: with both
|
|
707
|
+
# present, `import graders` binds graders.py.
|
|
708
|
+
package = env_root / module_name
|
|
709
|
+
helper = env_root / f"{module_name}.py"
|
|
710
|
+
ships_package = (
|
|
711
|
+
package.is_dir()
|
|
712
|
+
and not package.is_symlink()
|
|
713
|
+
and ((package / "__init__.py").is_file() or not helper.is_file())
|
|
714
|
+
)
|
|
715
|
+
if ships_package and not _ignore_env_push_path(
|
|
716
|
+
package, env_root=env_root, entrypoint=entrypoint
|
|
717
|
+
):
|
|
718
|
+
for root, dirs, files in os.walk(
|
|
719
|
+
package, topdown=True, followlinks=False, onerror=_raise_walk_error
|
|
720
|
+
):
|
|
721
|
+
root_path = Path(root)
|
|
722
|
+
dirs[:] = sorted(
|
|
723
|
+
name
|
|
724
|
+
for name in dirs
|
|
725
|
+
if not _ignore_env_push_path(
|
|
726
|
+
root_path / name, env_root=env_root, entrypoint=entrypoint
|
|
727
|
+
)
|
|
728
|
+
)
|
|
729
|
+
for name in sorted(files):
|
|
730
|
+
child = root_path / name
|
|
731
|
+
if child in yielded or _ignore_env_push_path(
|
|
732
|
+
child, env_root=env_root, entrypoint=entrypoint
|
|
733
|
+
):
|
|
734
|
+
continue
|
|
735
|
+
yielded.add(child)
|
|
736
|
+
pending.extend(_helper_imports(child, env_root=env_root))
|
|
737
|
+
yield child, child.relative_to(env_root)
|
|
738
|
+
continue
|
|
739
|
+
if (
|
|
740
|
+
helper.is_file()
|
|
741
|
+
and helper != entrypoint
|
|
742
|
+
and helper not in yielded
|
|
743
|
+
and not _ignore_env_push_path(helper, env_root=env_root, entrypoint=entrypoint)
|
|
744
|
+
):
|
|
745
|
+
yielded.add(helper)
|
|
746
|
+
pending.extend(_helper_imports(helper, env_root=env_root))
|
|
747
|
+
yield helper, helper.relative_to(env_root)
|
|
748
|
+
|
|
749
|
+
|
|
644
750
|
def _iter_env_sidecar_files(
|
|
645
751
|
env_root: Path, *, entrypoint: Path, include_full_tree: bool
|
|
646
752
|
) -> Iterator[tuple[Path, Path]]:
|
|
@@ -666,6 +772,24 @@ def _iter_env_sidecar_files(
|
|
|
666
772
|
):
|
|
667
773
|
yielded.add(doc)
|
|
668
774
|
yield doc, doc.relative_to(env_root)
|
|
775
|
+
# the entrypoint's OWN imports ship, exactly as its evaluation sidecar's do. seeding the
|
|
776
|
+
# closure only from evaluations.py published an environment whose entrypoint imported
|
|
777
|
+
# siblings without them: `env push` exits 0 and prints an id, so nothing surfaces until a
|
|
778
|
+
# GPU has been rented and the worker imports the module. parsed with the same reader as
|
|
779
|
+
# every other module here, so a lazily imported helper (inside load_environment) counts.
|
|
780
|
+
try:
|
|
781
|
+
entry_tree = ast.parse(entrypoint.read_text(encoding="utf-8"), filename=str(entrypoint))
|
|
782
|
+
except (OSError, SyntaxError, UnicodeDecodeError):
|
|
783
|
+
# the entrypoint is validated for syntax on its own path, and a push that cannot read
|
|
784
|
+
# it fails there with a better message. an unreadable file here just means no closure.
|
|
785
|
+
entry_tree = None
|
|
786
|
+
if entry_tree is not None:
|
|
787
|
+
yield from _iter_import_closure(
|
|
788
|
+
_imported_module_names(entry_tree),
|
|
789
|
+
env_root=env_root,
|
|
790
|
+
entrypoint=entrypoint,
|
|
791
|
+
yielded=yielded,
|
|
792
|
+
)
|
|
669
793
|
# the eval sidecar ships with its entrypoint. `env eval` loads evaluations.py next to an
|
|
670
794
|
# exact .py target, so omitting it here would publish an environment whose suite passed
|
|
671
795
|
# locally and is simply absent once pushed.
|
|
@@ -675,8 +799,14 @@ def _iter_env_sidecar_files(
|
|
|
675
799
|
and sidecar != entrypoint
|
|
676
800
|
and not _ignore_env_push_path(sidecar, env_root=env_root, entrypoint=entrypoint)
|
|
677
801
|
):
|
|
678
|
-
yielded
|
|
679
|
-
|
|
802
|
+
# the entrypoint closure above shares `yielded` and runs first, so an entrypoint that
|
|
803
|
+
# imports `evaluations` already shipped it. yielding it a second time made
|
|
804
|
+
# `_check_env_push_limits` charge those bytes and that member twice, rejecting a tree
|
|
805
|
+
# that is actually under the limit (codex[bot]). only the yield is skipped: the syntax
|
|
806
|
+
# check and import walk below must still run either way.
|
|
807
|
+
if sidecar not in yielded:
|
|
808
|
+
yielded.add(sidecar)
|
|
809
|
+
yield sidecar, sidecar.relative_to(env_root)
|
|
680
810
|
try:
|
|
681
811
|
tree = ast.parse(sidecar.read_text(encoding="utf-8"), filename=str(sidecar))
|
|
682
812
|
except SyntaxError as exc:
|
|
@@ -688,73 +818,12 @@ def _iter_env_sidecar_files(
|
|
|
688
818
|
# (flash/envs/evaluations.py). scanning only top-level nodes skips exactly that
|
|
689
819
|
# pattern, so `env test` passes locally and the pushed environment is missing the
|
|
690
820
|
# helper the sidecar imports the moment it grades a case.
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
# this push already carries rather than a general dependency resolver.
|
|
698
|
-
pending = sorted(imported_modules)
|
|
699
|
-
visited: set[str] = set()
|
|
700
|
-
while pending:
|
|
701
|
-
module_name = pending.pop()
|
|
702
|
-
if module_name in visited:
|
|
703
|
-
continue
|
|
704
|
-
visited.add(module_name)
|
|
705
|
-
# `from graders.rules import score` resolves to a sibling PACKAGE, not graders.py.
|
|
706
|
-
# matching only the module spelling published an environment whose sidecar raises
|
|
707
|
-
# ModuleNotFoundError on its first case, having passed every local check.
|
|
708
|
-
#
|
|
709
|
-
# resolution follows python's own order, which is NOT simply "directories first".
|
|
710
|
-
# a directory holding __init__.py is a regular package and outranks a same-named
|
|
711
|
-
# module, so shipping the .py and skipping the package published the file the
|
|
712
|
-
# sidecar never imports while dropping the one it does (cursor[bot]). but a PEP 420
|
|
713
|
-
# namespace directory (no __init__.py) is only a fallback portion: `graders.py`
|
|
714
|
-
# wins over a bare `graders/`. requiring the marker unconditionally sent a
|
|
715
|
-
# namespace helper to the .py fallback, which did not exist either, and published
|
|
716
|
-
# an archive missing the helper entirely (codex[bot]). verified by probe: with both
|
|
717
|
-
# present, `import graders` binds graders.py.
|
|
718
|
-
package = env_root / module_name
|
|
719
|
-
helper = env_root / f"{module_name}.py"
|
|
720
|
-
ships_package = (
|
|
721
|
-
package.is_dir()
|
|
722
|
-
and not package.is_symlink()
|
|
723
|
-
and ((package / "__init__.py").is_file() or not helper.is_file())
|
|
724
|
-
)
|
|
725
|
-
if ships_package and not _ignore_env_push_path(
|
|
726
|
-
package, env_root=env_root, entrypoint=entrypoint
|
|
727
|
-
):
|
|
728
|
-
for root, dirs, files in os.walk(
|
|
729
|
-
package, topdown=True, followlinks=False, onerror=_raise_walk_error
|
|
730
|
-
):
|
|
731
|
-
root_path = Path(root)
|
|
732
|
-
dirs[:] = sorted(
|
|
733
|
-
name
|
|
734
|
-
for name in dirs
|
|
735
|
-
if not _ignore_env_push_path(
|
|
736
|
-
root_path / name, env_root=env_root, entrypoint=entrypoint
|
|
737
|
-
)
|
|
738
|
-
)
|
|
739
|
-
for name in sorted(files):
|
|
740
|
-
child = root_path / name
|
|
741
|
-
if child in yielded or _ignore_env_push_path(
|
|
742
|
-
child, env_root=env_root, entrypoint=entrypoint
|
|
743
|
-
):
|
|
744
|
-
continue
|
|
745
|
-
yielded.add(child)
|
|
746
|
-
pending.extend(_helper_imports(child))
|
|
747
|
-
yield child, child.relative_to(env_root)
|
|
748
|
-
continue
|
|
749
|
-
if (
|
|
750
|
-
helper.is_file()
|
|
751
|
-
and helper != entrypoint
|
|
752
|
-
and helper not in yielded
|
|
753
|
-
and not _ignore_env_push_path(helper, env_root=env_root, entrypoint=entrypoint)
|
|
754
|
-
):
|
|
755
|
-
yielded.add(helper)
|
|
756
|
-
pending.extend(_helper_imports(helper))
|
|
757
|
-
yield helper, helper.relative_to(env_root)
|
|
821
|
+
yield from _iter_import_closure(
|
|
822
|
+
_imported_module_names(tree),
|
|
823
|
+
env_root=env_root,
|
|
824
|
+
entrypoint=entrypoint,
|
|
825
|
+
yielded=yielded,
|
|
826
|
+
)
|
|
758
827
|
roots = [
|
|
759
828
|
env_root / name
|
|
760
829
|
for name in ("dataset", "datasets")
|
|
@@ -7,19 +7,16 @@ policy/reference update.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from flash.providers.allocator import required_vram_gb, vram_headroom
|
|
11
|
-
from flash.providers.base import largest_rentable_count
|
|
10
|
+
from flash.providers.allocator import geometry_safe_gpu_cap, required_vram_gb, vram_headroom
|
|
12
11
|
|
|
13
12
|
from .facts import (
|
|
14
13
|
GPU_COMPUTE_TFLOPS,
|
|
15
14
|
active_params_b,
|
|
16
15
|
download_weight_gb,
|
|
17
16
|
effective_train_tflops,
|
|
18
|
-
gpu_hourly_usd,
|
|
19
17
|
gpu_vram_gb,
|
|
20
18
|
has_nvlink,
|
|
21
19
|
model_quant,
|
|
22
|
-
pick_gpu,
|
|
23
20
|
reward_seconds_per_completion,
|
|
24
21
|
teacher_seconds_per_completion,
|
|
25
22
|
teacher_token_cost_usd,
|
|
@@ -309,16 +306,18 @@ def sft_seconds_for_tokens(config: RunConfig, gpu: str, train_tokens: float) ->
|
|
|
309
306
|
return flops / (peak * mfu)
|
|
310
307
|
|
|
311
308
|
|
|
312
|
-
def
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
309
|
+
def _offline_gpu_shape(
|
|
310
|
+
config: RunConfig, *, max_wall_seconds: float = 0.0
|
|
311
|
+
) -> tuple[str, int, int, str, float]:
|
|
312
|
+
"""Offline structural quote: (gpu, need, count, provider, per-card rate).
|
|
313
|
+
|
|
314
|
+
Quote preparation must not query live capacity: a sold-out market or transient lookup failure is
|
|
315
|
+
exactly what the lifecycle retry machinery exists to survive, and consuming it here prevents the
|
|
316
|
+
run/status from being created at all. Rank the managed 1/2/4/8-card shapes on the same cost model
|
|
317
|
+
as allocation, then replace this provisional quote with the selected live candidate immediately
|
|
318
|
+
before provisioning.
|
|
319
|
+
"""
|
|
320
|
+
total_params_b(config.model_id) # catalog-only; no HF/network sizing in `--cost`
|
|
322
321
|
need = required_vram_gb(
|
|
323
322
|
config.model_id,
|
|
324
323
|
config.method,
|
|
@@ -326,12 +325,70 @@ def select_gpu(config: RunConfig, *, max_wall_seconds: float = 0.0) -> tuple[str
|
|
|
326
325
|
thinking=config.thinking,
|
|
327
326
|
model_revision=config.model_revision,
|
|
328
327
|
)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
328
|
+
from flash.providers.base import (
|
|
329
|
+
GPU_INFO,
|
|
330
|
+
canonical_gpu,
|
|
331
|
+
combined_vram_gb,
|
|
332
|
+
providers_for,
|
|
333
|
+
rentable_gpu_counts,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
provider = config.provider if config.provider != "auto" else "auto"
|
|
337
|
+
if config.gpu_type:
|
|
338
|
+
names = (canonical_gpu(config.gpu_type),)
|
|
339
|
+
else:
|
|
340
|
+
names = tuple(
|
|
341
|
+
info.name for info in GPU_INFO.values() if info.enum_member and info.validated
|
|
342
|
+
)
|
|
343
|
+
safe_gpu_count = geometry_safe_gpu_cap(
|
|
344
|
+
config.model_id, config.gpu_count, model_revision=config.model_revision
|
|
345
|
+
)
|
|
346
|
+
ranked = []
|
|
347
|
+
for gpu in names:
|
|
348
|
+
info = GPU_INFO[gpu]
|
|
349
|
+
if provider != "auto" and provider not in providers_for(gpu):
|
|
350
|
+
continue
|
|
351
|
+
for count in rentable_gpu_counts(safe_gpu_count):
|
|
352
|
+
if combined_vram_gb(info.vram_gb, count) < need:
|
|
353
|
+
continue
|
|
354
|
+
# Provisional quoting is structural and must not touch a live market. Vast pricing is
|
|
355
|
+
# offer-backed (therefore capacity-backed), and Lambda's catalog can blip too. Use the
|
|
356
|
+
# provider's offline static rate here; the lifecycle replaces it from the selected candidate
|
|
357
|
+
# before provisioning, so the persisted/charged quote still carries the exact live rate.
|
|
358
|
+
if provider == "lambda":
|
|
359
|
+
from flash.providers.lambdalabs.pricing import static_hourly_rate
|
|
360
|
+
|
|
361
|
+
hourly = static_hourly_rate(gpu)
|
|
362
|
+
else:
|
|
363
|
+
hourly = info.hourly_usd
|
|
364
|
+
gpu_bound, fixed = step_seconds_split(config, gpu)
|
|
365
|
+
step_seconds = gpu_bound / multi_card_speedup(count, gpu) + fixed
|
|
366
|
+
ranked.append(
|
|
367
|
+
(
|
|
368
|
+
hourly * count * step_seconds,
|
|
369
|
+
count,
|
|
370
|
+
combined_vram_gb(info.vram_gb, count),
|
|
371
|
+
info.vram_gb,
|
|
372
|
+
gpu,
|
|
373
|
+
hourly,
|
|
374
|
+
)
|
|
375
|
+
)
|
|
376
|
+
if not ranked:
|
|
377
|
+
if config.gpu_type:
|
|
378
|
+
info = GPU_INFO[canonical_gpu(config.gpu_type)]
|
|
379
|
+
raise ValueError(
|
|
380
|
+
f"exact GPU {info.name!r} cannot fit this run: it requires at least {need} GB"
|
|
381
|
+
)
|
|
382
|
+
shape = f" across up to {safe_gpu_count} cards" if safe_gpu_count > 1 else ""
|
|
383
|
+
raise ValueError(f"no GPU class fits >= {need} GB{shape}")
|
|
384
|
+
_cost, count, _combined, _per_card, gpu, hourly = min(ranked)
|
|
385
|
+
return gpu, need, count, provider, hourly
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def select_gpu(config: RunConfig, *, max_wall_seconds: float = 0.0) -> tuple[str, int]:
|
|
389
|
+
"""(chosen GPU class, required VRAM GB) from the offline structural quote."""
|
|
390
|
+
gpu, need, _count, _provider, _hourly = _offline_gpu_shape(
|
|
391
|
+
config, max_wall_seconds=max_wall_seconds
|
|
335
392
|
)
|
|
336
393
|
return gpu, need
|
|
337
394
|
|
|
@@ -385,8 +442,15 @@ def _notes(
|
|
|
385
442
|
return tuple(notes)
|
|
386
443
|
|
|
387
444
|
|
|
388
|
-
def estimate_cost(
|
|
389
|
-
|
|
445
|
+
def estimate_cost(
|
|
446
|
+
config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S, allocation=None
|
|
447
|
+
) -> CostEstimate:
|
|
448
|
+
"""Deterministic pre-flight cost calculation.
|
|
449
|
+
|
|
450
|
+
``allocation`` is the exact live candidate selected by the retrying lifecycle. Preparation omits
|
|
451
|
+
it and stays offline; immediately before provisioning the persisted quote is replaced from this
|
|
452
|
+
candidate so successful billing uses the real provider, class, count, and per-card rate.
|
|
453
|
+
"""
|
|
390
454
|
# Billing cap: mirror the runner's max(60, max_wall_seconds) floor so a sub-60s cap isn't underpriced.
|
|
391
455
|
cap_s = (
|
|
392
456
|
max(60.0, float(config.max_wall_seconds))
|
|
@@ -404,63 +468,45 @@ def estimate_cost(config: RunConfig, *, wall_cap_s: float = DEFAULT_WALL_CAP_S)
|
|
|
404
468
|
market_wall_s = float(config.max_wall_seconds)
|
|
405
469
|
else:
|
|
406
470
|
market_wall_s = 0.0
|
|
407
|
-
if
|
|
408
|
-
# gpu_type is provisioned at launch through allocate (disk-aware, live rate). quote the same
|
|
409
|
-
# path with either an automatic or pinned provider so the estimate matches the actual launch rate,
|
|
410
|
-
# and a disk floor lifts the quote off the wrong class.
|
|
411
|
-
from flash.providers.allocator import allocate
|
|
412
|
-
|
|
413
|
-
allocation = allocate(
|
|
414
|
-
config.model_id,
|
|
415
|
-
config.method,
|
|
416
|
-
train=config.train_knobs(),
|
|
417
|
-
thinking=config.thinking,
|
|
418
|
-
max_wall_seconds=market_wall_s,
|
|
419
|
-
disk_gb=config.disk_gb,
|
|
420
|
-
provider=("" if config.provider == "auto" else config.provider),
|
|
421
|
-
gpu_type=config.gpu_type,
|
|
422
|
-
model_revision=config.model_revision,
|
|
423
|
-
# quote the same card ceiling launch allocates under, so a multi-card run is estimated on
|
|
424
|
-
# the combination it will actually rent rather than on a single-class search that ignores it.
|
|
425
|
-
max_gpu_count=config.gpu_count,
|
|
426
|
-
)
|
|
471
|
+
if allocation is not None:
|
|
427
472
|
gpu = allocation.gpu
|
|
428
473
|
quote_provider = allocation.provider
|
|
429
|
-
hourly = allocation.hourly_usd
|
|
430
|
-
need =
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
474
|
+
hourly = float(allocation.hourly_usd)
|
|
475
|
+
need = int(
|
|
476
|
+
getattr(allocation, "min_vram_gb", 0)
|
|
477
|
+
or required_vram_gb(
|
|
478
|
+
config.model_id,
|
|
479
|
+
config.method,
|
|
480
|
+
train=config.train_knobs(),
|
|
481
|
+
thinking=config.thinking,
|
|
482
|
+
model_revision=config.model_revision,
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
billed_gpu_count = int(getattr(allocation, "gpu_count", 1) or 1)
|
|
434
486
|
else:
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
#
|
|
438
|
-
#
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
# submit and charged verbatim by _status_estimated_charge, so an over-count here overbills.
|
|
442
|
-
billed_gpu_count = largest_rentable_count(config.gpu_count)
|
|
443
|
-
# quote the same vram-floored vast market pick_gpu selected under (min_vram_gb=need): without the
|
|
444
|
-
# floor the rate lookup searches from the smallest managed class, letting cheap small-card offers
|
|
445
|
-
# crowd a high-vram selection off the limited page -> it silently falls back to the static rate.
|
|
446
|
-
hourly = gpu_hourly_usd(
|
|
447
|
-
gpu,
|
|
448
|
-
provider=quote_provider,
|
|
449
|
-
max_wall_seconds=market_wall_s,
|
|
450
|
-
min_vram_gb=need,
|
|
451
|
-
gpu_type="", # this branch is only reached when gpu_type is empty
|
|
487
|
+
# Preparation and `flash train --cost` must stay independent of live capacity. A provider
|
|
488
|
+
# lookup blip here would consume the first allocation failure before a run/status exists, so
|
|
489
|
+
# the lifecycle could never retry it. This provisional structural quote is replaced from the
|
|
490
|
+
# exact selected candidate immediately before provisioning.
|
|
491
|
+
gpu, need, billed_gpu_count, quote_provider, hourly = _offline_gpu_shape(
|
|
492
|
+
config, max_wall_seconds=market_wall_s
|
|
452
493
|
)
|
|
453
494
|
|
|
454
495
|
setup = setup_seconds(config)
|
|
455
|
-
|
|
496
|
+
gpu_bound_s, fixed_s = step_seconds_split(config, gpu)
|
|
497
|
+
speedup = multi_card_speedup(billed_gpu_count, gpu)
|
|
498
|
+
sps = gpu_bound_s / speedup + fixed_s
|
|
456
499
|
required_save_s = required_save_overhead_seconds(config)
|
|
457
500
|
# A one-time kernel/graph compile is paid once on the first step (MoE-only; 0 for dense). It is
|
|
458
|
-
# training GPU time, so it belongs in the (billed) train term, not setup.
|
|
501
|
+
# training GPU time, so it belongs in the (billed) train term, not setup. Required saves are also
|
|
502
|
+
# synchronous fixed overhead; neither is divided by the number of cards.
|
|
459
503
|
compile_s = compile_seconds(config, gpu)
|
|
460
504
|
raw_train = compile_s + config.steps * sps + required_save_s
|
|
461
505
|
if not config.is_grpo and config.train_tokens is not None:
|
|
462
506
|
raw_train = (
|
|
463
|
-
compile_s
|
|
507
|
+
compile_s
|
|
508
|
+
+ sft_seconds_for_tokens(config, gpu, config.train_tokens) / speedup
|
|
509
|
+
+ required_save_s
|
|
464
510
|
)
|
|
465
511
|
sps = raw_train / config.steps
|
|
466
512
|
|
|
@@ -172,6 +172,6 @@ def runconfig_from_spec(spec) -> RunConfig:
|
|
|
172
172
|
)
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
def estimate_for_spec(spec) -> CostEstimate:
|
|
176
|
-
"""
|
|
177
|
-
return estimate_cost(runconfig_from_spec(spec))
|
|
175
|
+
def estimate_for_spec(spec, *, allocation=None) -> CostEstimate:
|
|
176
|
+
"""Cost estimate for a parsed training spec, optionally pinned to the selected live candidate."""
|
|
177
|
+
return estimate_cost(runconfig_from_spec(spec), allocation=allocation)
|
|
@@ -1207,9 +1207,9 @@ def check_fit(
|
|
|
1207
1207
|
|
|
1208
1208
|
gpu_gb = GPU_VRAM_GB.get(gpu, 32)
|
|
1209
1209
|
# clamp to what the ceiling actually BUYS before sizing: only powers of two up to the
|
|
1210
|
-
# combination cap are ever rented, so a ceiling of 3 provisions 2 cards
|
|
1211
|
-
#
|
|
1212
|
-
#
|
|
1210
|
+
# combination cap are ever rented, so a ceiling of 3 provisions 2 cards. sizing on the raw
|
|
1211
|
+
# ceiling would accept a shape submit can never allocate -- the parse/submit divergence this
|
|
1212
|
+
# parameter exists to close, inverted.
|
|
1213
1213
|
cards = largest_rentable_count(gpu_count)
|
|
1214
1214
|
usable_gb = combined_vram_gb(gpu_gb, cards)
|
|
1215
1215
|
if params_b is None:
|