freesolo-flash-dev 1.0.100__tar.gz → 1.1.2__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.100 → freesolo_flash_dev-1.1.2}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft_train.py +51 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/__init__.py +7 -1
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/deploy.py +3 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/lifecycle.py +19 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/pyproject.toml +3 -3
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_resume_safety.py +4 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_routing.py +51 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_train.py +73 -3
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.env.example +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/dependabot.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/CODE_OF_CONDUCT.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/CONTRIBUTING.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/NOTICE +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/README.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/SECURITY.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/SELF_HOSTING.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/README.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/deploy/infisical/entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/_paths.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/catalog.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/TRAINING.md +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/commands.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_eval.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/env_test.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/traces.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cli/training_doc.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/reward_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/rollout_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/sft_workload.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/backend_common.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo_plugin.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/multiturn_glue.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_plugin.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_structured.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/opd_train.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rl_train.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/sft_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/teacher_encoding.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/evaluations.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_limits.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/opd_validation.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/providers/weight_cache_preload.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/schema/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/projects.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/routes/teacher.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/server/teacher_broker.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/thinking.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/workload_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/scripts/scrub_history.sh +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_async_uploads.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_backend_common.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_commands.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_credential_shadowing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_gpus_flag.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_traces.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cli_wandb_warning.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_config_env_vars.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_evaluations.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_ref_pinning.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_env_test.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_gpu_type_single_field.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_resident_sizing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_grpo_reward_batching.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_infisical_overlay.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_jobs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_managed_config_fields.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multi_gpu_gate.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_multimodal_training.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_limits.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_opd_train.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_parasail_teacher.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_projects.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_reward_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rl_train.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_profile_quote.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_profile_sampling.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_rollout_workload_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serve_reasoning_content.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_server_standalone.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_chunked_nll.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_profile_worker.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_sft_workload.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_step_floor.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_step_metrics.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_teacher_broker.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_vram_architecture.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_link.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_warmstart_error_attribution.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_worker_thinking.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_workload_profile.py +0 -0
- {freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/tests/test_workload_profile_lifecycle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.2
|
|
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
|
|
@@ -549,6 +549,9 @@ class _VerlCheckpointWatcher:
|
|
|
549
549
|
) -> None:
|
|
550
550
|
self.local_dir = local_dir
|
|
551
551
|
self.export_root = export_root
|
|
552
|
+
# sibling of export_root so it shares its filesystem: _staged_source hardlinks into it, and
|
|
553
|
+
# os.link cannot cross a mount. holds one step at a time and is cleaned up after each publish.
|
|
554
|
+
self.staging_root = os.path.join(export_root, "_staging")
|
|
552
555
|
self.python_bin = python_bin
|
|
553
556
|
self.model_id = model_id
|
|
554
557
|
self.model_revision = model_revision
|
|
@@ -605,10 +608,58 @@ class _VerlCheckpointWatcher:
|
|
|
605
608
|
def _should_publish(self, step: int) -> bool:
|
|
606
609
|
return not self.required_steps or step in self.required_steps
|
|
607
610
|
|
|
611
|
+
def _staged_source(self, step: int, checkpoint_dir: str) -> str:
|
|
612
|
+
"""Pin a completed checkpoint against verl's retention before the slow work reads it.
|
|
613
|
+
|
|
614
|
+
This watcher publishes on its own thread, so the merge and the multi-GB upload below both
|
|
615
|
+
overlap training. verl owns `local_dir` and prunes it from the CHILD process: under
|
|
616
|
+
`trainer.max_ckpt_to_keep=1`, `register_checkpoint` calls `shutil.rmtree` on global_step_N as
|
|
617
|
+
soon as N+1 finishes saving. Nothing the parent sets can pin a directory across that, so a
|
|
618
|
+
required save still exporting or uploading when the next save lands reads a tree being
|
|
619
|
+
deleted underneath it, and an otherwise successful paid run fails its required save. Close
|
|
620
|
+
`save_at_steps` (say `[2, 3]`) make that likely rather than theoretical.
|
|
621
|
+
|
|
622
|
+
HARDLINKS, not a copy and not a rename:
|
|
623
|
+
- a copy of tens of GB is itself slow enough to lose the same race it is meant to close.
|
|
624
|
+
- a rename would win the race but break in-pod resume: verl resolves
|
|
625
|
+
`latest_checkpointed_iteration.txt` to `global_step_N` (`find_latest_ckpt_path`), so moving
|
|
626
|
+
the directory leaves the tracker pointing at nothing if the pod is preempted afterwards.
|
|
627
|
+
|
|
628
|
+
Linking is O(1) per file and leaves the original in place, so verl's retention and resume see
|
|
629
|
+
exactly what they saw before while the link keeps the DATA alive for as long as this watcher
|
|
630
|
+
needs it. A pruned original just drops its own reference; the inode survives.
|
|
631
|
+
|
|
632
|
+
Best-effort by design: any failure returns the original path, which is the previous
|
|
633
|
+
(race-exposed) behaviour. Degrading beats failing a run that would otherwise complete.
|
|
634
|
+
"""
|
|
635
|
+
staged = os.path.join(self.staging_root, f"global_step_{step}")
|
|
636
|
+
try:
|
|
637
|
+
shutil.rmtree(staged, ignore_errors=True)
|
|
638
|
+
shutil.copytree(checkpoint_dir, staged, copy_function=os.link)
|
|
639
|
+
except OSError as error:
|
|
640
|
+
print(
|
|
641
|
+
f"[ckpt] step {step} could not be staged out of verl's retention tree "
|
|
642
|
+
f"({error}); publishing from verl's copy",
|
|
643
|
+
flush=True,
|
|
644
|
+
)
|
|
645
|
+
shutil.rmtree(staged, ignore_errors=True)
|
|
646
|
+
return checkpoint_dir
|
|
647
|
+
return staged
|
|
648
|
+
|
|
608
649
|
def _publish(self, step: int, checkpoint_dir: str) -> None:
|
|
609
650
|
if not self._should_publish(step):
|
|
610
651
|
self.processed_steps.add(step)
|
|
611
652
|
return
|
|
653
|
+
staged_dir = self._staged_source(step, checkpoint_dir)
|
|
654
|
+
try:
|
|
655
|
+
self._publish_from(step, staged_dir)
|
|
656
|
+
finally:
|
|
657
|
+
if staged_dir != checkpoint_dir:
|
|
658
|
+
# the links have served their purpose once the upload returns; the originals and the
|
|
659
|
+
# exported adapter are untouched by this.
|
|
660
|
+
shutil.rmtree(staged_dir, ignore_errors=True)
|
|
661
|
+
|
|
662
|
+
def _publish_from(self, step: int, checkpoint_dir: str) -> None:
|
|
612
663
|
actor_dir = resolve_checkpoint_actor_dir(checkpoint_dir)
|
|
613
664
|
adapter_dir = os.path.join(self.export_root, f"step-{step}")
|
|
614
665
|
_export_checkpoint_adapter(
|
|
@@ -2080,18 +2080,24 @@ def _persist_metrics(spec: JobSpec, metrics: dict) -> float:
|
|
|
2080
2080
|
# case _gpu_rate prices off whichever configured provider offers the class.
|
|
2081
2081
|
provider = str(metrics.get("allocated_provider") or "")
|
|
2082
2082
|
rate = _gpu_rate(gpu_type, provider)
|
|
2083
|
+
# `hourly_rate` is per CARD, so a sharded run costs the wall times the rate times the number of
|
|
2084
|
+
# cards it actually occupied. `allocated_gpu_count` is worker/lifecycle-stamped for the same
|
|
2085
|
+
# reason `allocated_gpu` is: the spec's gpu.count is only a ceiling and allocation may pick
|
|
2086
|
+
# fewer. Absent on records predating the stamp, where one card is the correct reading.
|
|
2087
|
+
gpu_count = max(1, int(metrics.get("allocated_gpu_count") or 1))
|
|
2083
2088
|
cost = metrics.get("cost_usd")
|
|
2084
2089
|
if cost:
|
|
2085
2090
|
cost = float(cost or 0.0)
|
|
2086
2091
|
else:
|
|
2087
2092
|
wall = float(metrics.get("wall_seconds") or 0.0)
|
|
2088
|
-
cost = wall / 3600.0 * rate
|
|
2093
|
+
cost = wall / 3600.0 * rate * gpu_count
|
|
2089
2094
|
metrics = {**metrics, "cost_usd": cost}
|
|
2090
2095
|
metrics.setdefault("notes", {})
|
|
2091
2096
|
if isinstance(metrics["notes"], dict):
|
|
2092
2097
|
metrics["notes"]["provider"] = provider or "unknown"
|
|
2093
2098
|
metrics["notes"]["gpu_rate_usd_hr"] = rate
|
|
2094
2099
|
metrics["notes"]["gpu"] = gpu_type
|
|
2100
|
+
metrics["notes"]["gpu_count"] = gpu_count
|
|
2095
2101
|
with open(os.path.join(dest, "metrics.json"), "w") as f:
|
|
2096
2102
|
json.dump(metrics, f, indent=2)
|
|
2097
2103
|
with contextlib.suppress(Exception):
|
|
@@ -1110,6 +1110,7 @@ def attach_run(run_id: str, log_stream=None) -> RunStatus:
|
|
|
1110
1110
|
raise ValueError("persisted attempt identity is missing or invalid")
|
|
1111
1111
|
next_attempt = recovered_attempt + 1
|
|
1112
1112
|
allocated_gpu = remote.pop("allocated_gpu", None)
|
|
1113
|
+
allocated_gpu_count = remote.pop("allocated_gpu_count", None)
|
|
1113
1114
|
handle = JobHandle.from_dict(remote)
|
|
1114
1115
|
try:
|
|
1115
1116
|
poll_spec = _spec_with_remaining_wall(worker_spec, require_provider_minimum=False)
|
|
@@ -1256,6 +1257,8 @@ def attach_run(run_id: str, log_stream=None) -> RunStatus:
|
|
|
1256
1257
|
return status_for_return()
|
|
1257
1258
|
if allocated_gpu and isinstance(res.metrics, dict):
|
|
1258
1259
|
res.metrics.setdefault("allocated_gpu", allocated_gpu)
|
|
1260
|
+
if allocated_gpu_count and isinstance(res.metrics, dict):
|
|
1261
|
+
res.metrics.setdefault("allocated_gpu_count", int(allocated_gpu_count))
|
|
1259
1262
|
if not _adopt_completed_attempt(
|
|
1260
1263
|
run_id,
|
|
1261
1264
|
worker_spec,
|
|
@@ -316,6 +316,9 @@ def _runpod_completed_metrics(handle, *, deadline_at: float | None = None) -> di
|
|
|
316
316
|
allocated_gpu = original.get("allocated_gpu")
|
|
317
317
|
if allocated_gpu:
|
|
318
318
|
metrics.setdefault("allocated_gpu", allocated_gpu)
|
|
319
|
+
allocated_count = original.get("allocated_gpu_count")
|
|
320
|
+
if allocated_count:
|
|
321
|
+
metrics.setdefault("allocated_gpu_count", int(allocated_count))
|
|
319
322
|
return metrics
|
|
320
323
|
except _CompletedAttemptPending:
|
|
321
324
|
raise
|
|
@@ -684,6 +687,11 @@ def _submit_seed_supervised(
|
|
|
684
687
|
**canonical_handle,
|
|
685
688
|
"seed": int(seed),
|
|
686
689
|
"allocated_gpu": current_gpu.get("name"),
|
|
690
|
+
# carried beside the gpu name for the same reason and by the same route: the
|
|
691
|
+
# canonical provider handle drops unknown keys, so a recovering process can only
|
|
692
|
+
# learn the shape from what was persisted here. without the count a run adopted
|
|
693
|
+
# after a control-plane restart prices its wall as a single card.
|
|
694
|
+
"allocated_gpu_count": current_gpu.get("count"),
|
|
687
695
|
"on_last_gpu": bool(current_on_last_gpu["value"]),
|
|
688
696
|
"code_prefix": code_prefix,
|
|
689
697
|
}
|
|
@@ -742,6 +750,11 @@ def _submit_seed_supervised(
|
|
|
742
750
|
metrics.setdefault("allocated_gpu", current_gpu["name"])
|
|
743
751
|
if current_gpu.get("provider"):
|
|
744
752
|
metrics.setdefault("allocated_provider", current_gpu["provider"])
|
|
753
|
+
# the runpod serverless route returns here rather than through the `res.ok` stamp below, so
|
|
754
|
+
# the card count has to be recorded on both or a sharded serverless run is still priced as
|
|
755
|
+
# one card. same source either way: the candidate allocation actually chose.
|
|
756
|
+
if current_gpu.get("count"):
|
|
757
|
+
metrics.setdefault("allocated_gpu_count", int(current_gpu["count"]))
|
|
745
758
|
return metrics
|
|
746
759
|
|
|
747
760
|
max_retries = int(spec.gpu.max_retries)
|
|
@@ -984,6 +997,7 @@ def _submit_seed_supervised(
|
|
|
984
997
|
raise
|
|
985
998
|
current_gpu["name"] = chosen.gpu
|
|
986
999
|
current_gpu["provider"] = chosen.provider
|
|
1000
|
+
current_gpu["count"] = int(getattr(chosen, "gpu_count", 1) or 1)
|
|
987
1001
|
current_attempt["value"] = attempt
|
|
988
1002
|
retry_delay = 0
|
|
989
1003
|
submission_lock = _deploy_lock(spec.run_id)
|
|
@@ -1113,6 +1127,11 @@ def _submit_seed_supervised(
|
|
|
1113
1127
|
# the provider that actually billed this run, so cost attribution prices the
|
|
1114
1128
|
# class on ITS substrate rather than assuming RunPod's table.
|
|
1115
1129
|
res.metrics.setdefault("allocated_provider", chosen.provider)
|
|
1130
|
+
# and how many cards of it. `hourly_rate(gpu_type)` is per CARD, so a fallback that
|
|
1131
|
+
# prices the wall once records a 2x/4x run at half or a quarter of its real spend.
|
|
1132
|
+
# the spec's own gpu.count cannot stand in: it is a ceiling, and allocation
|
|
1133
|
+
# routinely picks fewer (see TRAINING.md, "a ceiling, not an exact count").
|
|
1134
|
+
res.metrics.setdefault("allocated_gpu_count", int(getattr(chosen, "gpu_count", 1)))
|
|
1116
1135
|
return res.metrics
|
|
1117
1136
|
# cancel wins over any retry-shaped failure.
|
|
1118
1137
|
try:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "freesolo-flash-dev"
|
|
3
|
-
version = "1.
|
|
4
|
-
description =
|
|
3
|
+
version = "1.1.2"
|
|
4
|
+
description ="Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
# RunPod Flash supports Python 3.11-3.12 (not 3.13 yet).
|
|
7
7
|
requires-python = ">=3.11,<3.13"
|
|
@@ -121,7 +121,7 @@ artifacts = ["flash/py.typed"]
|
|
|
121
121
|
# auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
|
|
122
122
|
# PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
|
|
123
123
|
# dev-channel release = bump this (and [project].version) and merge to `dev`.
|
|
124
|
-
version = "1.
|
|
124
|
+
version = "1.1.2"
|
|
125
125
|
|
|
126
126
|
[tool.pytest.ini_options]
|
|
127
127
|
pythonpath = ["."]
|
|
@@ -656,6 +656,8 @@ def test_opd_automatic_retry_after_teardown_requires_all_markers_absent(monkeypa
|
|
|
656
656
|
# stamped alongside the gpu so cost attribution prices the class on the substrate
|
|
657
657
|
# that actually billed it.
|
|
658
658
|
"allocated_provider": "runpod",
|
|
659
|
+
# and the card count, since the hourly rate is per card.
|
|
660
|
+
"allocated_gpu_count": 1,
|
|
659
661
|
}
|
|
660
662
|
assert provider.attempts == [0, 1]
|
|
661
663
|
retry_submit = provider.events.index(("submit", 1))
|
|
@@ -744,6 +746,8 @@ def test_opd_retry_passes_gate_revision_and_overwrites_spoofed_value(monkeypatch
|
|
|
744
746
|
# stamped alongside the gpu so cost attribution prices the class on the substrate
|
|
745
747
|
# that actually billed it.
|
|
746
748
|
"allocated_provider": "runpod",
|
|
749
|
+
# and the card count, since the hourly rate is per card.
|
|
750
|
+
"allocated_gpu_count": 1,
|
|
747
751
|
}
|
|
748
752
|
broker_transport = {
|
|
749
753
|
"FLASH_CONTROL_PANEL_URL": "https://broker.example",
|
|
@@ -796,6 +796,57 @@ def test_runpod_cost_projection_flows_into_run_status(orch, monkeypatch):
|
|
|
796
796
|
assert cost == pytest.approx(0.345) # 0.5 hr x $0.69/hr (RTX 4090)
|
|
797
797
|
|
|
798
798
|
|
|
799
|
+
def test_a_multi_card_run_is_costed_for_every_card_it_occupied(orch):
|
|
800
|
+
"""`hourly_rate` is per CARD, so the measured cost must multiply by the allocated count.
|
|
801
|
+
|
|
802
|
+
without it a 4-card run records a quarter of its real spend, and the cost analytics that compare
|
|
803
|
+
estimates against actuals are corrupted by exactly that factor. the count has to come from the
|
|
804
|
+
metrics stamp rather than `spec.gpu.count`, which is only a ceiling -- allocation routinely picks
|
|
805
|
+
fewer cards than authored.
|
|
806
|
+
"""
|
|
807
|
+
spec = _spec()
|
|
808
|
+
_seed_status(orch, spec)
|
|
809
|
+
base = {"train_tokens": 4096, "wall_seconds": 1800, "allocated_gpu": "RTX 4090"}
|
|
810
|
+
|
|
811
|
+
four_card = orch._persist_metrics(spec, {**base, "allocated_gpu_count": 4})
|
|
812
|
+
|
|
813
|
+
assert four_card == pytest.approx(0.345 * 4), "a 4-card run was priced as one card"
|
|
814
|
+
# a record predating the stamp still reads as one card rather than zero or a crash
|
|
815
|
+
assert orch._persist_metrics(spec, dict(base)) == pytest.approx(0.345)
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def test_the_allocated_card_count_reaches_the_metrics_the_cost_is_read_from(orch, monkeypatch):
|
|
819
|
+
"""The multiply above is inert unless allocation actually stamps the count it chose.
|
|
820
|
+
|
|
821
|
+
this drives the real submit path rather than handing `_persist_metrics` a literal, because the
|
|
822
|
+
two halves fail independently: pricing can multiply correctly by a count that is never recorded.
|
|
823
|
+
"""
|
|
824
|
+
from flash.providers import allocator
|
|
825
|
+
from flash.providers.base import Candidate, PollResult
|
|
826
|
+
from flash.providers.runpod import jobs as rp_jobs
|
|
827
|
+
|
|
828
|
+
monkeypatch.setattr(
|
|
829
|
+
allocator,
|
|
830
|
+
"allocate",
|
|
831
|
+
lambda *a, **k: _alloc(candidates=(Candidate("runpod", "RTX 4090", 0.69, 24, 4),)),
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
def fake_runpod_submit(run_spec, seed, log=None, on_handle=None, attempt=0, **_):
|
|
835
|
+
if on_handle:
|
|
836
|
+
on_handle(_runpod_handle(attempt=attempt))
|
|
837
|
+
return PollResult(True, metrics={"train_tokens": 4096, "wall_seconds": 1800})
|
|
838
|
+
|
|
839
|
+
monkeypatch.setattr(rp_jobs, "submit_run", fake_runpod_submit)
|
|
840
|
+
spec = _spec(provider="runpod", type="RTX 4090", count=4)
|
|
841
|
+
_seed_status(orch, spec)
|
|
842
|
+
|
|
843
|
+
metrics = orch._submit_seed_supervised(spec, spec.seed, io.StringIO())
|
|
844
|
+
|
|
845
|
+
assert metrics["allocated_gpu_count"] == 4
|
|
846
|
+
# and the stamp is what pricing then reads, so the two halves compose
|
|
847
|
+
assert orch._persist_metrics(spec, metrics) == pytest.approx(0.345 * 4)
|
|
848
|
+
|
|
849
|
+
|
|
799
850
|
def test_infra_retry_walks_to_next_runpod_class_and_deletes_endpoint(orch, monkeypatch):
|
|
800
851
|
from flash.providers import allocator
|
|
801
852
|
from flash.providers.base import Candidate, PollResult
|
|
@@ -5,7 +5,9 @@ from __future__ import annotations
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import importlib.util
|
|
7
7
|
import os
|
|
8
|
+
import pathlib
|
|
8
9
|
import re
|
|
10
|
+
import shutil
|
|
9
11
|
import sys
|
|
10
12
|
import types
|
|
11
13
|
from dataclasses import replace
|
|
@@ -959,10 +961,13 @@ def test_checkpoint_watcher_exports_and_uploads_required_step(monkeypatch, tmp_p
|
|
|
959
961
|
|
|
960
962
|
watcher._publish(5, str(checkpoint_dir))
|
|
961
963
|
|
|
962
|
-
|
|
964
|
+
# both read the STAGED copy rather than verl's directory (see _staged_source); what matters is
|
|
965
|
+
# that they agree on one source and that it carries the `actor/` level this layout puts there.
|
|
966
|
+
assert os.path.basename(exported[0][0]) == "actor"
|
|
967
|
+
assert exported[0][0] == os.path.join(uploaded[0][1], "actor")
|
|
963
968
|
assert published[0][1] == 5
|
|
964
969
|
assert published[0][2]["required"] is True
|
|
965
|
-
assert uploaded == [
|
|
970
|
+
assert [step for step, _ in uploaded] == [5]
|
|
966
971
|
assert watcher.processed_steps == {5}
|
|
967
972
|
|
|
968
973
|
|
|
@@ -1002,7 +1007,72 @@ def test_checkpoint_watcher_exports_the_sft_layout(monkeypatch, tmp_path):
|
|
|
1002
1007
|
|
|
1003
1008
|
watcher._publish(5, str(checkpoint_dir))
|
|
1004
1009
|
|
|
1005
|
-
|
|
1010
|
+
# the export reads a STAGED copy, not verl's own directory (see _staged_source), so assert the
|
|
1011
|
+
# layout this test is about -- global_step_N itself, no `actor/` level -- rather than the path.
|
|
1012
|
+
assert [os.path.basename(path) for path in exported] == ["global_step_5"]
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def test_a_required_save_survives_verl_pruning_it_mid_publish(monkeypatch, tmp_path):
|
|
1016
|
+
"""verl deleting global_step_N while it is being published must not fail the run.
|
|
1017
|
+
|
|
1018
|
+
the watcher publishes on its own thread, so the merge and the multi-GB upload overlap training.
|
|
1019
|
+
under `trainer.max_ckpt_to_keep=1` verl's `register_checkpoint` rmtree's global_step_N from the
|
|
1020
|
+
CHILD process the moment N+1 finishes saving, and the parent has no way to pin it. with close
|
|
1021
|
+
`save_at_steps` (here [2, 3]) that lands squarely inside the publish window.
|
|
1022
|
+
|
|
1023
|
+
this reproduces the interleave rather than asserting on the staging mechanism: the fake upload
|
|
1024
|
+
deletes verl's directory the way verl would, then reads the source it was handed. before staging,
|
|
1025
|
+
that read fails and takes the whole paid run down with a required-save error.
|
|
1026
|
+
"""
|
|
1027
|
+
import flash.engine.worker as worker
|
|
1028
|
+
from flash.engine.worker import sft_train
|
|
1029
|
+
|
|
1030
|
+
local_dir = tmp_path / "checkpoints"
|
|
1031
|
+
checkpoint_dir = local_dir / "global_step_2"
|
|
1032
|
+
(checkpoint_dir / "actor" / "huggingface").mkdir(parents=True)
|
|
1033
|
+
(checkpoint_dir / "actor" / "model.safetensors").write_bytes(b"weights")
|
|
1034
|
+
|
|
1035
|
+
monkeypatch.setattr(
|
|
1036
|
+
sft_train,
|
|
1037
|
+
"_export_checkpoint_adapter",
|
|
1038
|
+
lambda actor, adapter, **kwargs: os.makedirs(adapter, exist_ok=True),
|
|
1039
|
+
)
|
|
1040
|
+
monkeypatch.setattr(
|
|
1041
|
+
worker, "publish_deployable_checkpoint", lambda adapter, step, **kwargs: None
|
|
1042
|
+
)
|
|
1043
|
+
read_back = {}
|
|
1044
|
+
|
|
1045
|
+
def fake_upload(step, checkpoint, **kwargs):
|
|
1046
|
+
kwargs["before_upload"]()
|
|
1047
|
+
# verl prunes global_step_2 because global_step_3 just landed. this is the whole race.
|
|
1048
|
+
shutil.rmtree(checkpoint_dir, ignore_errors=True)
|
|
1049
|
+
# the upload streams the folder AFTER that, so it must still have something to read.
|
|
1050
|
+
read_back["payload"] = (
|
|
1051
|
+
pathlib.Path(checkpoint, "actor", "model.safetensors").read_bytes()
|
|
1052
|
+
if os.path.isdir(checkpoint)
|
|
1053
|
+
else None
|
|
1054
|
+
)
|
|
1055
|
+
return True
|
|
1056
|
+
|
|
1057
|
+
monkeypatch.setattr(worker, "upload_resume_checkpoint", fake_upload)
|
|
1058
|
+
watcher = sft_train._VerlCheckpointWatcher(
|
|
1059
|
+
local_dir=str(local_dir),
|
|
1060
|
+
export_root=str(tmp_path / "exports"),
|
|
1061
|
+
python_bin="/verl/python",
|
|
1062
|
+
model_id="org/model",
|
|
1063
|
+
model_revision="commit",
|
|
1064
|
+
required_steps=(2, 3),
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
watcher._publish(2, str(checkpoint_dir))
|
|
1068
|
+
|
|
1069
|
+
assert read_back["payload"] == b"weights", (
|
|
1070
|
+
"the upload lost its source when verl pruned the checkpoint, so a required save would fail "
|
|
1071
|
+
"an otherwise successful paid run"
|
|
1072
|
+
)
|
|
1073
|
+
assert watcher.processed_steps == {2}
|
|
1074
|
+
# the staging links are transient: they must not outlive the publish and accumulate on the pod.
|
|
1075
|
+
assert not os.path.exists(os.path.join(str(tmp_path / "exports"), "_staging", "global_step_2"))
|
|
1006
1076
|
|
|
1007
1077
|
|
|
1008
1078
|
def test_resume_credits_only_required_saves_that_are_durable(monkeypatch):
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/bug_report.yml
RENAMED
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/ISSUE_TEMPLATE/feature_request.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/bake-kernel-cache.yml
RENAMED
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/main-source-guard.yml
RENAMED
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/notify-tests-repo.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/.github/workflows/version-parity.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/docker/Dockerfile.kernelcache.relayer
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/multiturn_reward_scoring.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/backend_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/grpo_multiturn.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{freesolo_flash_dev-1.0.100 → freesolo_flash_dev-1.1.2}/flash/engine/worker/kernel_warmup.py
RENAMED
|
File without changes
|
|
File without changes
|